From 10710764986ea9b7ba31c719a93991de64c8a48f Mon Sep 17 00:00:00 2001 From: "anchi.liu" Date: Thu, 7 Dec 2023 18:09:47 +0900 Subject: [PATCH] init --- .clang-format | 150 +- .github/workflows/format.yml | 37 + Common++/header/GeneralUtils.h | 115 +- Common++/header/IpAddress.h | 2028 ++++---- Common++/header/IpUtils.h | 78 +- Common++/header/LRUList.h | 222 +- Common++/header/Logger.h | 446 +- Common++/header/MacAddress.h | 328 +- Common++/header/OUILookup.h | 103 +- Common++/header/PcapPlusPlusVersion.h | 85 +- Common++/header/PointerVector.h | 310 +- Common++/header/SystemUtils.h | 740 +-- Common++/header/TablePrinter.h | 112 +- Common++/header/TimespecTimeval.h | 18 +- Common++/src/GeneralUtils.cpp | 146 +- Common++/src/IpAddress.cpp | 1094 ++--- Common++/src/IpUtils.cpp | 576 +-- Common++/src/Logger.cpp | 81 +- Common++/src/MacAddress.cpp | 67 +- Common++/src/OUILookup.cpp | 163 +- Common++/src/PcapPlusPlusVersion.cpp | 32 +- Common++/src/SystemUtils.cpp | 498 +- Common++/src/TablePrinter.cpp | 206 +- Examples/ArpSpoofing/main.cpp | 451 +- Examples/Arping/main.cpp | 444 +- Examples/DNSResolver/main.cpp | 396 +- Examples/DnsSpoofing/main.cpp | 782 +-- Examples/DpdkBridge/AppWorkerThread.h | 145 +- Examples/DpdkBridge/Common.h | 56 +- Examples/DpdkBridge/main.cpp | 665 +-- .../AppWorkerThread.h | 309 +- Examples/DpdkExample-FilterTraffic/Common.h | 306 +- .../PacketMatchingEngine.h | 166 +- Examples/DpdkExample-FilterTraffic/main.cpp | 1069 ++-- Examples/ExampleApp/main.cpp | 64 +- Examples/HttpAnalyzer/HttpStatsCollector.h | 899 ++-- Examples/HttpAnalyzer/main.cpp | 1032 ++-- Examples/IPDefragUtil/main.cpp | 779 ++- Examples/IPFragUtil/main.cpp | 954 ++-- Examples/IcmpFileTransfer/Common.cpp | 519 +- Examples/IcmpFileTransfer/Common.h | 75 +- .../IcmpFileTransfer-catcher.cpp | 1000 ++-- .../IcmpFileTransfer-pitcher.cpp | 1056 ++-- Examples/KniPong/main.cpp | 1189 +++-- Examples/PcapPlusPlus-benchmark/benchmark.cpp | 164 +- Examples/PcapPrinter/main.cpp | 594 ++- .../dirent-for-Visual-Studio/include/dirent.h | 431 +- Examples/PcapSearch/main.cpp | 774 +-- Examples/PcapSplitter/ConnectionSplitters.h | 392 +- Examples/PcapSplitter/IPPortSplitters.h | 1035 ++-- Examples/PcapSplitter/SimpleSplitters.h | 355 +- Examples/PcapSplitter/Splitters.h | 412 +- Examples/PcapSplitter/main.cpp | 958 ++-- Examples/PfRingExample-FilterTraffic/Common.h | 272 +- .../PacketMatchingEngine.h | 166 +- Examples/PfRingExample-FilterTraffic/main.cpp | 961 ++-- Examples/SSLAnalyzer/SSLStatsCollector.h | 712 +-- Examples/SSLAnalyzer/main.cpp | 966 ++-- Examples/TLSFingerprinting/main.cpp | 1100 +++-- Examples/TcpReassembly/main.cpp | 1245 ++--- .../Tutorial-DpdkL2Fwd/WorkerThread.cpp | 66 +- .../Tutorial-DpdkL2Fwd/WorkerThread.h | 37 +- .../Tutorials/Tutorial-DpdkL2Fwd/main.cpp | 279 +- .../Tutorials/Tutorial-HelloWorld/main.cpp | 64 +- .../Tutorials/Tutorial-LiveTraffic/main.cpp | 475 +- .../Tutorial-PacketCraftAndEdit/main.cpp | 275 +- .../Tutorials/Tutorial-PacketParsing/main.cpp | 382 +- .../Tutorials/Tutorial-PcapFiles/main.cpp | 176 +- Packet++/header/ArpLayer.h | 284 +- Packet++/header/BgpLayer.h | 1334 ++--- Packet++/header/CotpLayer.h | 204 +- Packet++/header/DhcpLayer.h | 1662 +++---- Packet++/header/DhcpV6Layer.h | 894 ++-- Packet++/header/DnsLayer.h | 1171 ++--- Packet++/header/DnsLayerEnums.h | 284 +- Packet++/header/DnsResource.h | 466 +- Packet++/header/DnsResourceData.h | 817 ++-- Packet++/header/EthDot3Layer.h | 234 +- Packet++/header/EthLayer.h | 312 +- Packet++/header/FtpLayer.h | 988 ++-- Packet++/header/GreLayer.h | 862 ++-- Packet++/header/GtpLayer.h | 922 ++-- Packet++/header/HttpLayer.h | 1657 ++++--- Packet++/header/IPLayer.h | 61 +- Packet++/header/IPReassembly.h | 963 ++-- Packet++/header/IPSecLayer.h | 392 +- Packet++/header/IPv4Layer.h | 1364 +++--- Packet++/header/IPv6Extensions.h | 1135 ++--- Packet++/header/IPv6Layer.h | 527 +- Packet++/header/IcmpLayer.h | 1442 +++--- Packet++/header/IcmpV6Layer.h | 480 +- Packet++/header/IgmpLayer.h | 952 ++-- Packet++/header/LLCLayer.h | 159 +- Packet++/header/Layer.h | 374 +- Packet++/header/MplsLayer.h | 230 +- Packet++/header/NdpLayer.h | 650 +-- Packet++/header/NflogLayer.h | 445 +- Packet++/header/NtpLayer.h | 1304 ++--- Packet++/header/NullLoopbackLayer.h | 164 +- Packet++/header/PPPoELayer.h | 1394 +++--- Packet++/header/Packet.h | 831 ++-- Packet++/header/PacketTrailerLayer.h | 139 +- Packet++/header/PacketUtils.h | 143 +- Packet++/header/PayloadLayer.h | 165 +- Packet++/header/ProtocolType.h | 694 +-- Packet++/header/RadiusLayer.h | 687 +-- Packet++/header/RawPacket.h | 929 ++-- Packet++/header/S7CommLayer.h | 389 +- Packet++/header/SSHLayer.h | 851 ++-- Packet++/header/SSLCommon.h | 1128 +++-- Packet++/header/SSLHandshake.h | 2121 ++++---- Packet++/header/SSLLayer.h | 957 ++-- Packet++/header/SdpLayer.h | 295 +- Packet++/header/SingleCommandTextProtocol.h | 86 +- Packet++/header/SipLayer.h | 1396 +++--- Packet++/header/Sll2Layer.h | 360 +- Packet++/header/SllLayer.h | 190 +- Packet++/header/SomeIpLayer.h | 874 ++-- Packet++/header/SomeIpSdLayer.h | 1380 +++--- Packet++/header/StpLayer.h | 1529 +++--- Packet++/header/TLVData.h | 842 ++-- Packet++/header/TcpLayer.h | 1011 ++-- Packet++/header/TcpReassembly.h | 913 ++-- Packet++/header/TelnetLayer.h | 686 +-- Packet++/header/TextBasedProtocol.h | 513 +- Packet++/header/TpktLayer.h | 237 +- Packet++/header/UdpLayer.h | 181 +- Packet++/header/VlanLayer.h | 242 +- Packet++/header/VrrpLayer.h | 902 ++-- Packet++/header/VxlanLayer.h | 271 +- Packet++/header/WakeOnLanLayer.h | 316 +- Packet++/src/ArpLayer.cpp | 83 +- Packet++/src/BgpLayer.cpp | 1718 ++++--- Packet++/src/CotpLayer.cpp | 80 +- Packet++/src/DhcpLayer.cpp | 457 +- Packet++/src/DhcpV6Layer.cpp | 379 +- Packet++/src/DnsLayer.cpp | 1449 +++--- Packet++/src/DnsResource.cpp | 774 ++- Packet++/src/DnsResourceData.cpp | 334 +- Packet++/src/EthDot3Layer.cpp | 89 +- Packet++/src/EthLayer.cpp | 220 +- Packet++/src/FtpLayer.cpp | 726 ++- Packet++/src/GreLayer.cpp | 914 ++-- Packet++/src/GtpLayer.cpp | 1182 +++-- Packet++/src/HttpLayer.cpp | 1768 ++++--- Packet++/src/IPReassembly.cpp | 1201 +++-- Packet++/src/IPSecLayer.cpp | 166 +- Packet++/src/IPv4Layer.cpp | 992 ++-- Packet++/src/IPv6Extensions.cpp | 367 +- Packet++/src/IPv6Layer.cpp | 607 ++- Packet++/src/IcmpLayer.cpp | 1060 ++-- Packet++/src/IcmpV6Layer.cpp | 243 +- Packet++/src/IgmpLayer.cpp | 813 ++-- Packet++/src/LLCLayer.cpp | 73 +- Packet++/src/Layer.cpp | 158 +- Packet++/src/MplsLayer.cpp | 230 +- Packet++/src/NdpLayer.cpp | 274 +- Packet++/src/NflogLayer.cpp | 156 +- Packet++/src/NtpLayer.cpp | 945 ++-- Packet++/src/NullLoopbackLayer.cpp | 157 +- Packet++/src/PPPoELayer.cpp | 698 ++- Packet++/src/Packet.cpp | 1435 +++--- Packet++/src/PacketTrailerLayer.cpp | 28 +- Packet++/src/PacketUtils.cpp | 442 +- Packet++/src/PayloadLayer.cpp | 75 +- Packet++/src/RadiusLayer.cpp | 387 +- Packet++/src/RawPacket.cpp | 507 +- Packet++/src/S7CommLayer.cpp | 238 +- Packet++/src/SSHLayer.cpp | 349 +- Packet++/src/SSLCommon.cpp | 142 +- Packet++/src/SSLHandshake.cpp | 4303 ++++++++++------- Packet++/src/SSLLayer.cpp | 363 +- Packet++/src/SdpLayer.cpp | 209 +- Packet++/src/SingleCommandTextProtocol.cpp | 248 +- Packet++/src/SipLayer.cpp | 1729 +++---- Packet++/src/Sll2Layer.cpp | 341 +- Packet++/src/SllLayer.cpp | 219 +- Packet++/src/SomeIpLayer.cpp | 453 +- Packet++/src/SomeIpSdLayer.cpp | 1161 +++-- Packet++/src/StpLayer.cpp | 411 +- Packet++/src/TLVData.cpp | 151 +- Packet++/src/TcpLayer.cpp | 640 ++- Packet++/src/TcpReassembly.cpp | 1501 +++--- Packet++/src/TelnetLayer.cpp | 827 ++-- Packet++/src/TextBasedProtocol.cpp | 1325 ++--- Packet++/src/TpktLayer.cpp | 103 +- Packet++/src/UdpLayer.cpp | 239 +- Packet++/src/VlanLayer.cpp | 239 +- Packet++/src/VrrpLayer.cpp | 936 ++-- Packet++/src/VxlanLayer.cpp | 70 +- Packet++/src/WakeOnLanLayer.cpp | 179 +- Pcap++/header/Device.h | 169 +- Pcap++/header/DpdkDevice.h | 1780 ++++--- Pcap++/header/DpdkDeviceList.h | 415 +- Pcap++/header/KniDevice.h | 1216 ++--- Pcap++/header/KniDeviceList.h | 235 +- Pcap++/header/LinuxNicInformationSocket.h | 127 +- Pcap++/header/MBufRawPacket.h | 452 +- Pcap++/header/NetworkUtils.h | 160 +- Pcap++/header/PcapDevice.h | 166 +- Pcap++/header/PcapFileDevice.h | 1218 ++--- Pcap++/header/PcapFilter.h | 1626 ++++--- Pcap++/header/PcapLiveDevice.h | 1239 ++--- Pcap++/header/PcapLiveDeviceList.h | 227 +- Pcap++/header/PcapRemoteDevice.h | 289 +- Pcap++/header/PcapRemoteDeviceList.h | 303 +- Pcap++/header/PfRingDevice.h | 698 +-- Pcap++/header/PfRingDeviceList.h | 99 +- Pcap++/header/RawSocketDevice.h | 336 +- Pcap++/header/WinPcapLiveDevice.h | 102 +- Pcap++/header/XdpDevice.h | 694 +-- Pcap++/src/DpdkDevice.cpp | 2511 +++++----- Pcap++/src/DpdkDeviceList.cpp | 659 ++- Pcap++/src/KniDevice.cpp | 1793 ++++--- Pcap++/src/KniDeviceList.cpp | 252 +- Pcap++/src/LinuxNicInformationSocket.cpp | 101 +- Pcap++/src/MBufRawPacket.cpp | 495 +- Pcap++/src/NetworkUtils.cpp | 799 ++- Pcap++/src/PcapDevice.cpp | 105 +- Pcap++/src/PcapFileDevice.cpp | 1509 +++--- Pcap++/src/PcapFilter.cpp | 847 ++-- Pcap++/src/PcapLiveDevice.cpp | 1795 ++++--- Pcap++/src/PcapLiveDeviceList.cpp | 596 ++- Pcap++/src/PcapRemoteDevice.cpp | 218 +- Pcap++/src/PcapRemoteDeviceList.cpp | 374 +- Pcap++/src/PfRingDevice.cpp | 1706 ++++--- Pcap++/src/PfRingDeviceList.cpp | 157 +- Pcap++/src/RawSocketDevice.cpp | 869 ++-- Pcap++/src/WinPcapLiveDevice.cpp | 224 +- Pcap++/src/XdpDevice.cpp | 1071 ++-- run_format.bash | 6 + 231 files changed, 72514 insertions(+), 70354 deletions(-) create mode 100644 .github/workflows/format.yml mode change 100755 => 100644 Packet++/header/VrrpLayer.h mode change 100755 => 100644 Packet++/src/VrrpLayer.cpp create mode 100755 run_format.bash diff --git a/.clang-format b/.clang-format index 757c95dd69..ec93158f64 100644 --- a/.clang-format +++ b/.clang-format @@ -1,7 +1,149 @@ --- -BasedOnStyle: Microsoft -UseTab: Always +Language: Cpp +# BasedOnStyle: LLVM +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveMacros: false +AlignConsecutiveAssignments: false +AlignConsecutiveBitFields: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Right +AlignOperands: Align +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortEnumsOnASingleLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: All -NamespaceIndentation: All - +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: MultiLine +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 0 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DeriveLineEnding: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + SortPriority: 0 + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + SortPriority: 0 + - Regex: '.*' + Priority: 1 + SortPriority: 0 +IncludeIsMainRegex: '(Test)?$' +IncludeIsMainSourceRegex: '' +IndentCaseLabels: false +IndentCaseBlocks: false +IndentGotoLabels: true +IndentPPDirectives: None +IndentExternBlock: AfterExternBlock +IndentWidth: 4 +IndentWrappedFunctionNames: false +InsertTrailingCommas: None +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Left +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpaceBeforeSquareBrackets: false +Standard: Latest +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 8 +UseCRLF: false +UseTab: Never +WhitespaceSensitiveMacros: + - STRINGIZE + - PP_STRINGIZE + - BOOST_PP_STRINGIZE ... + diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml new file mode 100644 index 0000000000..0cc08f133e --- /dev/null +++ b/.github/workflows/format.yml @@ -0,0 +1,37 @@ +name: lint + +on: + push: + pull_request: + schedule: + - cron: '34 17 * * *' + +jobs: + + clang_format_check: + + runs-on: ubuntu-latest + + strategy: + matrix: + path: + - 'Common++' + - 'Pcap++' + - 'Packet++' + - 'Examples++' + + steps: + + - uses: actions/checkout@v3 + + - name: event name + run: | + echo "github.event_name: ${{ github.event_name }}" + + - name: Run clang-format style check for C/C++/Protobuf programs. + uses: jidicula/clang-format-action@v4.11.0 + with: + clang-format-version: '14' + check-path: ${{ matrix.path }} + fallback-style: 'LLVM' # optional + diff --git a/Common++/header/GeneralUtils.h b/Common++/header/GeneralUtils.h index ec5409ee95..62018f974f 100644 --- a/Common++/header/GeneralUtils.h +++ b/Common++/header/GeneralUtils.h @@ -1,8 +1,8 @@ #ifndef PCAPPP_GENERAL_UTILS #define PCAPPP_GENERAL_UTILS -#include #include +#include /// @file @@ -10,61 +10,70 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - /** - * Convert a byte array into a string of hex characters. For example: for the array { 0xaa, 0x2b, 0x10 } the string - * "aa2b10" will be returned - * @param[in] byteArr A byte array - * @param[in] byteArrSize The size of the byte array [in bytes] - * @param[in] stringSizeLimit An optional parameter that enables to limit the returned string size. If set to a positive - * integer value the returned string size will be equal or less than this value. If the string representation of the - * whole array is longer than this size then only part of the array will be read. The default value is -1 which means no - * string size limitation - * @return A string of hex characters representing the byte array - */ - std::string byteArrayToHexString(const uint8_t* byteArr, size_t byteArrSize, int stringSizeLimit = -1); +namespace pcpp { +/** + * Convert a byte array into a string of hex characters. For example: for the + * array { 0xaa, 0x2b, 0x10 } the string "aa2b10" will be returned + * @param[in] byteArr A byte array + * @param[in] byteArrSize The size of the byte array [in bytes] + * @param[in] stringSizeLimit An optional parameter that enables to limit the + * returned string size. If set to a positive integer value the returned string + * size will be equal or less than this value. If the string representation of + * the whole array is longer than this size then only part of the array will be + * read. The default value is -1 which means no string size limitation + * @return A string of hex characters representing the byte array + */ +std::string byteArrayToHexString(const uint8_t* byteArr, size_t byteArrSize, + int stringSizeLimit = -1); - /** - * Convert a string of hex characters into a byte array. For example: for the string "aa2b10" an array of values - * { 0xaa, 0x2b, 0x10 } will be returned - * @param[in] hexString A string of hex characters - * @param[out] resultByteArr A pre-allocated byte array where the result will be written to - * @param[in] resultByteArrSize The size of the pre-allocated byte array - * @return The size of the result array. If the string represents an array that is longer than the pre-allocated size - * (resultByteArrSize) then the result array will contain only the part of the string that managed to fit into the - * array, and the returned size will be resultByteArrSize. However if the string represents an array that is shorter - * than the pre-allocated size then some of the cells will remain empty and contain zeros, and the returned size will - * be the part of the array that contain data. If the input is an illegal hex string 0 will be returned. - * Illegal hex string means odd number of characters or a string that contains non-hex characters - */ - size_t hexStringToByteArray(const std::string& hexString, uint8_t* resultByteArr, size_t resultByteArrSize); +/** + * Convert a string of hex characters into a byte array. For example: for the + * string "aa2b10" an array of values { 0xaa, 0x2b, 0x10 } will be returned + * @param[in] hexString A string of hex characters + * @param[out] resultByteArr A pre-allocated byte array where the result will be + * written to + * @param[in] resultByteArrSize The size of the pre-allocated byte array + * @return The size of the result array. If the string represents an array that + * is longer than the pre-allocated size (resultByteArrSize) then the result + * array will contain only the part of the string that managed to fit into the + * array, and the returned size will be resultByteArrSize. However if the string + * represents an array that is shorter than the pre-allocated size then some of + * the cells will remain empty and contain zeros, and the returned size will be + * the part of the array that contain data. If the input is an illegal hex + * string 0 will be returned. Illegal hex string means odd number of characters + * or a string that contains non-hex characters + */ +size_t hexStringToByteArray(const std::string& hexString, + uint8_t* resultByteArr, size_t resultByteArrSize); - /** - * This is a cross platform version of memmem (https://man7.org/linux/man-pages/man3/memmem.3.html) which is not supported - * on all platforms. - * @param[in] haystack A pointer to the buffer to be searched - * @param[in] haystackLen Length of the haystack buffer - * @param[in] needle A pointer to a buffer that will be searched for - * @param[in] needleLen Length of the needle buffer - * @return A pointer to the beginning of the substring, or NULL if the substring is not found - */ - char* cross_platform_memmem(const char* haystack, size_t haystackLen, const char* needle, size_t needleLen); +/** + * This is a cross platform version of memmem + * (https://man7.org/linux/man-pages/man3/memmem.3.html) which is not supported + * on all platforms. + * @param[in] haystack A pointer to the buffer to be searched + * @param[in] haystackLen Length of the haystack buffer + * @param[in] needle A pointer to a buffer that will be searched for + * @param[in] needleLen Length of the needle buffer + * @return A pointer to the beginning of the substring, or NULL if the substring + * is not found + */ +char* cross_platform_memmem(const char* haystack, size_t haystackLen, + const char* needle, size_t needleLen); - /** - * Calculates alignment. - * @param[in] number Given number - * @return The aligned number - */ - template - static int align(int number) - { - // Only works for alignment with power of 2 - constexpr bool isPowerOfTwo = alignment && ((alignment & (alignment - 1)) == 0); - static_assert(isPowerOfTwo, "Alignment must be a power of 2"); - int mask = alignment - 1; - return (number + mask) & ~mask; - } +/** + * Calculates alignment. + * @param[in] number Given number + * @return The aligned number + */ +template +static int align(int number) { + // Only works for alignment with power of 2 + constexpr bool isPowerOfTwo = + alignment && ((alignment & (alignment - 1)) == 0); + static_assert(isPowerOfTwo, "Alignment must be a power of 2"); + int mask = alignment - 1; + return (number + mask) & ~mask; } +} // namespace pcpp #endif // PCAPPP_GENERAL_UTILS diff --git a/Common++/header/IpAddress.h b/Common++/header/IpAddress.h index d71ef6e62b..3946f7bac2 100644 --- a/Common++/header/IpAddress.h +++ b/Common++/header/IpAddress.h @@ -1,11 +1,11 @@ #ifndef PCAPPP_IP_ADDRESSES #define PCAPPP_IP_ADDRESSES +#include +#include #include #include #include -#include -#include #ifndef PCPP_DEPRECATED #if defined(__GNUC__) || defined(__clang__) @@ -13,1032 +13,1048 @@ #elif defined(_MSC_VER) #define PCPP_DEPRECATED __declspec(deprecated) #else -#pragma message("WARNING: DEPRECATED feature is not implemented for this compiler") +#pragma message( \ + "WARNING: DEPRECATED feature is not implemented for this compiler") #define PCPP_DEPRECATED #endif #endif /// @file - /** * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - - // forward declarations - class IPv4Network; - class IPv6Network; - - // The implementation of the classes is based on document N4771 "Working Draft, C++ Extensions for Networking" - // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/n4771.pdf - - /** - * @class IPv4Address - * Represents an IPv4 address (of type XXX.XXX.XXX.XXX) - */ - class IPv4Address - { - public: - /** - * A default constructor that creates an instance of the class with unspecified/zero address - */ - IPv4Address() { memset(m_Bytes, 0, sizeof(m_Bytes)); } - - /** - * A constructor that creates an instance of the class out of 4-byte integer value. - * @param[in] addrAsInt The address as 4-byte integer in network byte order - */ - IPv4Address(uint32_t addrAsInt) { memcpy(m_Bytes, &addrAsInt, sizeof(m_Bytes)); } - - /** - * A constructor that creates an instance of the class out of 4-byte array. - * @param[in] bytes The address as 4-byte array in network byte order - */ - IPv4Address(const uint8_t bytes[4]) { memcpy(m_Bytes, bytes, sizeof(m_Bytes)); } - - /** - * A constructor that creates an instance of the class out of std::string value - * If the string doesn't represent a valid IPv4 address, an instance will store an unspecified address - * @param[in] addrAsString The std::string representation of the address - */ - IPv4Address(const std::string& addrAsString); - - /** - * Converts the IPv4 address into a 4B integer - * @return a 4B integer in network byte order representing the IPv4 address - */ - inline uint32_t toInt() const; - - /** - * Returns a pointer to 4-byte array representing the IPv4 address - */ - const uint8_t* toBytes() const { return m_Bytes; } - - /** - * Returns a std::string representation of the address - * @return A string representation of the address - */ - std::string toString() const; - - /** - * Determine whether the address is a multicast address - * @return True if an address is multicast - */ - bool isMulticast() const; - - /** - * Determine whether the address is valid (it's not an unspecified/zero) - * @return True if an address is not unspecified/zero - */ - bool isValid() const { return toInt() != 0; } - - /** - * Overload of the equal-to operator - * @param[in] rhs The object to compare with - * @return True if the addresses are equal, false otherwise - */ - bool operator==(const IPv4Address& rhs) const { return toInt() == rhs.toInt(); } - - /** - * Overload of the less-than operator - * @param[in] rhs The object to compare with - * @return True if the address value is lower than the other address value, false otherwise - */ - bool operator<(const IPv4Address& rhs) const - { - uint32_t intVal = toInt(); - std::reverse((uint8_t*)(&intVal), (uint8_t*)(&intVal) + sizeof(intVal)); - - uint32_t rhsIntVal = rhs.toInt(); - std::reverse((uint8_t*)(&rhsIntVal), (uint8_t*)(&rhsIntVal) + sizeof(rhsIntVal)); - - return intVal < rhsIntVal; - } - - /** - * Overload of the not-equal-to operator - * @param[in] rhs The object to compare with - * @return True if the addresses are not equal, false otherwise - */ - bool operator!=(const IPv4Address& rhs) const { return !(*this == rhs); } - - /** - * Checks whether the address matches a network. - * @param network An IPv4Network network - * @return True if the address matches the network or false otherwise - */ - bool matchNetwork(const IPv4Network& network) const; - - /** - * Checks whether the address matches a network. - * For example: this method will return true for address 10.1.1.9 and network which is one of: - * 10.1.1.1/24, 10.1.1.1/255.255.255.0 - * Another example: this method will return false for address 11.1.1.9 and network which is one of: - * 10.1.1.1/16, 10.1.1.1/255.255.0.0 - * @param[in] network A string in one of these formats: - * - X.X.X.X/Y where X.X.X.X is a valid IP address and Y is a number between 0 and 32 - * - X.X.X.X/Y.Y.Y.Y where X.X.X.X is a valid IP address and Y.Y.Y.Y is a valid netmask - * @return True if the address matches the network or false if it doesn't or if the network is invalid - */ - bool matchNetwork(const std::string& network) const; - - /** - * @deprecated This method is deprecated, please use matchNetwork(const IPv4Network& network) - */ - PCPP_DEPRECATED bool matchSubnet(const IPv4Address& subnet, const std::string& subnetMask) const; - - /** - * @deprecated This method is deprecated, please use matchNetwork(const IPv4Network& network) - */ - PCPP_DEPRECATED bool matchSubnet(const IPv4Address& subnet, const IPv4Address& subnetMask) const; - - /** - * A static value representing a zero value of IPv4 address, meaning address of value "0.0.0.0" - * Notice this value can be omitted in the user code because the default constructor creates an instance with an unspecified/zero address. - * In order to check whether the address is zero the method isValid can be used - */ - static const IPv4Address Zero; - - /** - * A static values representing the lower and upper bound of IPv4 multicast ranges. The bounds are inclusive. - * MulticastRangeLowerBound is initialized to "224.0.0.0". - * MulticastRangeUpperBound is initialized to "239.255.255.255". - * In order to check whether the address is a multicast address the isMulticast method can be used. - */ - static const IPv4Address MulticastRangeLowerBound; - static const IPv4Address MulticastRangeUpperBound; - - private: - uint8_t m_Bytes[4]; - }; // class IPv4Address - - - // Implementation of inline methods - - uint32_t IPv4Address::toInt() const - { - uint32_t addr; - memcpy(&addr, m_Bytes, sizeof(m_Bytes)); - return addr; - } - - /** - * @class IPv6Address - * Represents an IPv6 address (of type xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx). - */ - class IPv6Address - { - public: - /** - * A default constructor that creates an instance of the class with unspecified/zero address - */ - IPv6Address() { memset(m_Bytes, 0, sizeof(m_Bytes)); } - - /** - * A constructor that creates an instance of the class out of 16-byte array. - * @param[in] bytes The address as 16-byte array in network byte order - */ - IPv6Address(const uint8_t bytes[16]) { memcpy(m_Bytes, bytes, sizeof(m_Bytes)); } - - /** - * A constructor that creates an instance of the class out of std::string value - * If the string doesn't represent a valid IPv6 address, an instance will store an unspecified address - * @param[in] addrAsString The std::string representation of the address - */ - IPv6Address(const std::string& addrAsString); - - /** - * Returns a pointer to 16-byte array representing the IPv6 address - */ - const uint8_t* toBytes() const { return m_Bytes; } - - /** - * Returns a std::string representation of the address - * @return A string representation of the address - */ - std::string toString() const; - - /** - * Determine whether the address is a multicast address - * @return True if an address is multicast - */ - bool isMulticast() const; - - /** - * Determine whether the address is unspecified - */ - bool isValid() const { return *this != Zero; } - - /** - * Overload of the equal-to operator - * @param[in] rhs The object to compare with - * @return True if the addresses are equal, false otherwise - */ - bool operator==(const IPv6Address& rhs) const { return memcmp(toBytes(), rhs.toBytes(), sizeof(m_Bytes)) == 0; } - - /** - * Overload of the less-than operator - * @param[in] rhs The object to compare with - * @return True if the address value is lower than the other address value, false otherwise - */ - bool operator<(const IPv6Address& rhs) const { return memcmp(toBytes(), rhs.toBytes(), sizeof(m_Bytes)) < 0; } - - /** - * Overload of the not-equal-to operator - * @param[in] rhs The object to compare with - * @return True if the addresses are not equal, false otherwise - */ - bool operator!=(const IPv6Address &rhs) const { return !(*this == rhs); } - - - /** - * Allocates a byte array and copies address value into it. Array deallocation is user responsibility - * @param[in] arr A pointer to where array will be allocated - * @param[out] length Returns the length in bytes of the array that was allocated - */ - void copyTo(uint8_t** arr, size_t& length) const; - - /** - * Gets a pointer to an already allocated byte array and copies the address value to it. - * This method assumes array allocated size is at least 16 (the size of an IPv6 address) - * @param[in] arr A pointer to the array which address will be copied to - */ - void copyTo(uint8_t* arr) const { memcpy(arr, m_Bytes, sizeof(m_Bytes)); } - - /** - * Checks whether the address matches a network. - * @param network An IPv6Network network - * @return True if the address matches the network or false otherwise - */ - bool matchNetwork(const IPv6Network& network) const; - - /** - * Checks whether the address matches a network. - * For example: this method will return true for address d6e5:83dc:0c58:bc5d:1449:5898:: and network - * which is one of: - * d6e5:83dc:0c58:bc5d::/64, d6e5:83dc:0c58:bc5d::/ffff:ffff:ffff:ffff:: - * Another example: this method will return false for address d6e5:83dc:: and network which is one of: - * d6e5:83dc:0c58:bc5d::/64, d6e5:83dc:0c58:bc5d::/ffff:ffff:ffff:ffff:: - * @param[in] network A string in one of these formats: - * - IPV6_ADDRESS/Y where IPV6_ADDRESS is a valid IPv6 address and Y is a number between 0 and 128 - * - IPV6_ADDRESS/IPV6_NETMASK where IPV6_ADDRESS is a valid IPv6 address and IPV6_NETMASK is a valid - * IPv6 netmask - * @return True if the address matches the network or false if it doesn't or if the network is invalid - */ - bool matchNetwork(const std::string& network) const; - - /** - * @deprecated This method is deprecated, please use matchNetwork(const IPv6Network& network) - */ - PCPP_DEPRECATED bool matchSubnet(const IPv6Address& subnet, uint8_t prefixLength) const; - - /** - * A static value representing a zero value of IPv6 address, meaning address of value "0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0" - * Notice this value can be omitted in the user code because the default constructor creates an instance with an unspecified/zero address. - * In order to check whether the address is zero the method isValid can be used - */ - static const IPv6Address Zero; - - /** - * A static value representing the lower bound of IPv6 multicast ranges. The bound is inclusive. - * MulticastRangeLowerBound is initialized to "ff00:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0". - * In order to check whether the address is a multicast address the isMulticast method can be used. - */ - static const IPv6Address MulticastRangeLowerBound; - - private: - uint8_t m_Bytes[16]; - }; // class IPv6Address - - - /** - * @class IPAddress - * The class is a version-independent representation for an IP address - */ - class IPAddress - { - public: - /** - * An enum representing the address type: IPv4 or IPv6 - */ - enum AddressType - { - /** - * IPv4 address type - */ - IPv4AddressType, - /** - * IPv6 address type - */ - IPv6AddressType - }; - - /** - * A default constructor that creates an instance of the class with unspecified IPv4 address - */ - IPAddress() : m_Type(IPv4AddressType) {} - - /** - * A constructor that creates an instance of the class out of IPv4Address. - * @param[in] addr A const reference to instance of IPv4Address - */ - IPAddress(const IPv4Address& addr) : m_Type(IPv4AddressType), m_IPv4(addr) {} - - /** - * A constructor that creates an instance of the class out of IPv6Address. - * @param[in] addr A const reference to instance of IPv6Address - */ - IPAddress(const IPv6Address& addr) : m_Type(IPv6AddressType), m_IPv6(addr) {} - - /** - * A constructor that creates an instance of the class out of std::string value - * If the string doesn't represent a valid IPv4 or IPv6 address, an instance will store an unspecified address - * @param[in] addrAsString The std::string representation of the address - */ - IPAddress(const std::string& addrAsString); - - /** - * Overload of an assignment operator. - * @param[in] addr A const reference to instance of IPv4Address - * @return A reference to the assignee - */ - inline IPAddress& operator=(const IPv4Address& addr); - - /** - * Overload of an assignment operator. - * @param[in] addr A const reference to instance of IPv6Address - * @return A reference to the assignee - */ - inline IPAddress& operator=(const IPv6Address& addr); - - /** - * Gets the address type: IPv4 or IPv6 - * @return The address type - */ - AddressType getType() const { return static_cast(m_Type); } - - /** - * Returns a std::string representation of the address - * @return A string representation of the address - */ - std::string toString() const { return (getType() == IPv4AddressType) ? m_IPv4.toString() : m_IPv6.toString(); } - - /** - * @return Determine whether the address is unspecified - */ - bool isValid() const { return (getType() == IPv4AddressType) ? m_IPv4.isValid() : m_IPv6.isValid(); } - - /** - * @return Determine whether the object contains an IP version 4 address - */ - bool isIPv4() const { return getType() == IPv4AddressType; } - - /** - * @return Determine whether the object contains an IP version 6 address - */ - bool isIPv6() const { return getType() == IPv6AddressType; } - - /** - * Determine whether the address is a multicast address - * @return True if an address is multicast - */ - bool isMulticast() const { return (getType() == IPv4AddressType) ? m_IPv4.isMulticast() : m_IPv6.isMulticast(); } - - /** - * Get a reference to IPv4 address instance - * @return The const reference to IPv4Address instance - */ - const IPv4Address& getIPv4() const { return m_IPv4; } - - /** - * Get a reference to IPv6 address instance - * @return The const reference to IPv6Address instance - */ - const IPv6Address& getIPv6() const { return m_IPv6; } - - /** - * Overload of the equal-to operator - * @param[in] rhs The object to compare with - * @return True if the addresses are equal, false otherwise - */ - inline bool operator==(const IPAddress& rhs) const; - - /** - * Overload of the less-than operator - * @param[in] rhs The object to compare with - * @return True if the address value is lower than the other address value, false otherwise - */ - inline bool operator<(const IPAddress& rhs) const; - - /** - * Overload of the not-equal-to operator - * @param[in] rhs The object to compare with - * @return True if the addresses are not equal, false otherwise - */ - bool operator!=(const IPAddress& rhs) const { return !(*this == rhs); } - - private: - uint8_t m_Type; - IPv4Address m_IPv4; - IPv6Address m_IPv6; - }; - - - // implementation of inline methods - - bool IPAddress::operator==(const IPAddress& rhs) const - { - if (isIPv4()) - return rhs.isIPv4() ? (m_IPv4 == rhs.m_IPv4) : false; - - return rhs.isIPv6() ? m_IPv6 == rhs.m_IPv6 : false; - } - - bool IPAddress::operator<(const IPAddress& rhs) const - { - if(isIPv4()) - { - // treat IPv4 as less than IPv6 - // If current obj is IPv4 and other is IPv6 return true - return rhs.isIPv4() ? (m_IPv4 < rhs.m_IPv4) : true; - } - return rhs.isIPv6() ? m_IPv6 < rhs.m_IPv6 : false; - } - - IPAddress& IPAddress::operator=(const IPv4Address& addr) - { - m_Type = IPv4AddressType; - m_IPv4 = addr; - return *this; - } - - IPAddress& IPAddress::operator=(const IPv6Address& addr) - { - m_Type = IPv6AddressType; - m_IPv6 = addr; - return *this; - } - - - /** - * @class IPv4Network - * A class representing IPv4 network definition - */ - class IPv4Network - { - public: - /** - * A constructor that creates an instance of the class out of an address representing the network prefix - * and a prefix length - * @param address An address representing the network prefix. If the address is invalid std::invalid_argument - * exception is thrown - * @param prefixLen A number between 0 and 32 representing the prefix length. If another value is provided - * std::invalid_argument exception is thrown - */ - IPv4Network(const IPv4Address& address, uint8_t prefixLen); - - /** - * A constructor that creates an instance of the class out of an address representing the network prefix - * and a netmask - * @param address An address representing the network prefix. If the address is invalid std::invalid_argument - * exception is thrown - * @param netmask A string representing a netmask in the format of X.X.X.X, for example: 255.255.0.0. - * Please notice that netmasks that start with zeros are invalid, for example: 0.0.255.255. The only netmask - * starting with zeros that is valid is 0.0.0.0. If the netmask is invalid std::invalid_argument - * exception is thrown - */ - IPv4Network(const IPv4Address& address, const std::string& netmask); - - /** - * A constructor that creates an instance of the class out of a string representing the network prefix and - * a prefix length or a netmask - * @param addressAndNetmask A string in one of these formats: - * - X.X.X.X/Y where X.X.X.X is a valid IPv4 address representing the network prefix and Y is a number between - * 0 and 32 representing the network prefix - * - X.X.X.X/Y.Y.Y.Y where X.X.X.X is a valid IPv4 address representing the network prefix and Y.Y.Y.Y is - * a valid netmask - * For any invalid value std::invalid_argument is thrown - */ - IPv4Network(const std::string& addressAndNetmask); - - /** - * @return The prefix length, for example: the prefix length of 10.10.10.10/255.0.0.0 is 8 - */ - uint8_t getPrefixLen() const; - - /** - * @return The netmask, for example: the netmask of 10.10.10.10/8 is 255.0.0.0 - */ - std::string getNetmask() const { return IPv4Address(m_Mask).toString(); } - - /** - * @return The network prefix, for example: the network prefix of 10.10.10.10/16 is 10.10.0.0 - */ - IPv4Address getNetworkPrefix() const { return IPv4Address(m_NetworkPrefix); } - - /** - * @return The lowest non-reserved IPv4 address in this network, for example: the lowest address - * in 10.10.10.10/16 is 10.10.0.1 - */ - IPv4Address getLowestAddress() const; - - /** - * @return The highest non-reserved IPv4 address in this network, for example: the highest address - * in 10.10.10.10/16 is 10.10.255.254 - */ - IPv4Address getHighestAddress() const; - - /** - * @return The number of addresses in this network including reserved addresses, for example: - * the number of addresses in 10.10.0.0/24 is 256 - */ - uint64_t getTotalAddressCount() const; - - /** - * @param address An IPv4 address - * @return True is the address belongs to the network, false otherwise or if the address isn't valid - */ - bool includes(const IPv4Address& address) const; - - /** - * @param network An IPv4 network - * @return True is the input network is completely included within this network, false otherwise, for example: - * 10.10.10.10/16 includes 10.10.10.10/24 but doesn't include 10.10.10.10/8 - */ - bool includes(const IPv4Network& network) const; - - /** - * @return A string representation of the network in a format of NETWORK_PREFIX/PREFIX_LEN, for example: - * 192.168.0.0/16 - */ - std::string toString() const; - - private: - uint32_t m_NetworkPrefix; - uint32_t m_Mask; - - bool isValidNetmask(const std::string& netmask); - void initFromAddressAndPrefixLength(const IPv4Address& address, uint8_t prefixLen); - void initFromAddressAndNetmask(const IPv4Address& address, const std::string& netmask); - }; - - - /** - * @class IPv6Network - * A class representing IPv6 network definition - */ - class IPv6Network - { - public: - /** - * A constructor that creates an instance of the class out of an address representing the network prefix - * and a prefix length - * @param address An address representing the network prefix. If the address is invalid std::invalid_argument - * exception is thrown - * @param prefixLen A number between 0 and 128 representing the prefix length. If another value is provided - * std::invalid_argument exception is thrown - */ - IPv6Network(const IPv6Address& address, uint8_t prefixLen); - - /** - * A constructor that creates an instance of the class out of an address representing the network prefix - * and a netmask - * @param address An address representing the network prefix. If the address is invalid std::invalid_argument - * exception is thrown - * @param netmask A string representing a netmask in valid IPv6 format, for example: ffff:ffff::. - * Please notice that netmasks that start with zeros are invalid, for example: 0:ffff::. The only netmask - * starting with zeros that is valid is all zeros (::). If the netmask is invalid std::invalid_argument - * exception is thrown - */ - IPv6Network(const IPv6Address& address, const std::string& netmask); - - /** - * A constructor that creates an instance of the class out of a string representing the network prefix and - * a prefix length or a netmask - * @param addressAndNetmask A string in one of these formats: - * - IPV6_ADDRESS/Y where IPV6_ADDRESS is a valid IPv6 address representing the network prefix and Y is - * a number between 0 and 128 representing the network prefix - * - IPV6_ADDRESS/IPV6_NETMASK where IPV6_ADDRESS is a valid IPv6 address representing the network prefix - * and IPV6_NETMASK is a valid IPv6 netmask - * For any invalid value std::invalid_argument is thrown - */ - IPv6Network(const std::string& addressAndNetmask); - - /** - * @return The prefix length, for example: the prefix length of 3546::/ffff:: is 16 - */ - uint8_t getPrefixLen() const; - - /** - * @return The netmask, for example: the netmask of 3546::/16 is ffff:: - */ - std::string getNetmask() const { return IPv6Address(m_Mask).toString(); } - - /** - * @return The network prefix, for example: the network prefix of 3546:f321::/16 is 3546:: - */ - IPv6Address getNetworkPrefix() const { return IPv6Address(m_NetworkPrefix); } - - /** - * @return The lowest non-reserved IPv6 address in this network, for example: the lowest address in 3546::/16 is - * 3546::1 - */ - IPv6Address getLowestAddress() const; - - /** - * @return The highest IPv6 address in this network, for example: the highest address in 3546::/16 is - * 3546:ffff:ffff:ffff:ffff:ffff:ffff:ffff - */ - IPv6Address getHighestAddress() const; - - /** - * @return The number of addresses in this network, for example: the number of addresses in 16ff::/120 is 256. - * If the number of addresses exceeds the size of uint64_t a std::out_of_range exception is thrown - */ - uint64_t getTotalAddressCount() const; - - /** - * @param address An IPv6 address - * @return True is the address belongs to the network, false otherwise or if the address isn't valid - */ - bool includes(const IPv6Address& address) const; - - /** - * @param network An IPv6 network - * @return True is the input network is completely included within this network, false otherwise, for example: - * 3546::/64 includes 3546::/120 but doesn't include 3546::/16 - */ - bool includes(const IPv6Network& network) const; - - /** - * @return A string representation of the network in a format of NETWORK_PREFIX/PREFIX_LEN, for example: - * fda7:9f81:6c23:275::/64 - */ - std::string toString() const; - - private: - uint8_t m_NetworkPrefix[16]; - uint8_t m_Mask[16]; - - bool isValidNetmask(const std::string& netmask); - void initFromAddressAndPrefixLength(const IPv6Address& address, uint8_t prefixLen); - void initFromAddressAndNetmask(const IPv6Address& address, const std::string& netmask); - }; - - - /** - * @class IPNetwork - * A class representing version independent IP network definition, both IPv4 and IPv6 are included - */ - class IPNetwork - { - public: - /** - * A constructor that creates an instance of the class out of an address representing the network prefix - * and a prefix length - * @param address An address representing the network prefix. If the address is invalid std::invalid_argument - * exception is thrown - * @param prefixLen A number representing the prefix length. If the value isn't in the range allowed for the - * network (0 - 32 for IPv4 networks or 0 - 128 for IPv6 networks) and std::invalid_argument exception is thrown - */ - IPNetwork(const IPAddress& address, uint8_t prefixLen) - { - if (address.isIPv4()) - { - m_IPv4Network = new IPv4Network(address.getIPv4(), prefixLen); - m_IPv6Network = nullptr; - } - else - { - m_IPv6Network = new IPv6Network(address.getIPv6(), prefixLen); - m_IPv4Network = nullptr; - } - } - - /** - * A constructor that creates an instance of the class out of an address representing the network prefix - * and a netmask - * @param address An address representing the network prefix. If the address is invalid std::invalid_argument - * exception is thrown - * @param netmask A string representing a netmask in valid format, for example: ffff:ffff:: for IPv6 networks - * or 255.255.0.0 for IPv4 networks. - * Please notice that netmasks that start with zeros are invalid, for example: 0:ffff:: or 0.255.255.255. - * The only netmask starting with zeros that is valid is all zeros (:: or 0.0.0.0). - * If the netmask is invalid std::invalid_argument exception is thrown - */ - IPNetwork(const IPAddress& address, const std::string& netmask) - { - if (address.isIPv4()) - { - m_IPv4Network = new IPv4Network(address.getIPv4(), netmask); - m_IPv6Network = nullptr; - } - else - { - m_IPv6Network = new IPv6Network(address.getIPv6(), netmask); - m_IPv4Network = nullptr; - } - } - - /** - * A constructor that creates an instance of the class out of a string representing the network prefix and - * a prefix length or a netmask - * @param addressAndNetmask A string in one of these formats: - * - IP_ADDRESS/Y where IP_ADDRESS is a valid IP address representing the network prefix and Y is - * a number representing the network prefix - * - IP_ADDRESS/NETMASK where IP_ADDRESS is a valid IP address representing the network prefix and NETMASK - * is a valid netmask for this type of network (IPv4 or IPv6 network) - * For any invalid value std::invalid_argument is thrown - */ - IPNetwork(const std::string& addressAndNetmask) - { - try - { - m_IPv4Network = new IPv4Network(addressAndNetmask); - m_IPv6Network = nullptr; - } - catch (const std::invalid_argument&) - { - m_IPv6Network = new IPv6Network(addressAndNetmask); - m_IPv4Network = nullptr; - } - } - - /** - * A copy c'tor for this class - * @param other The instance to copy from - */ - IPNetwork(const IPNetwork& other) - { - m_IPv4Network = nullptr; - m_IPv6Network = nullptr; - - if (other.m_IPv4Network) - { - m_IPv4Network = new IPv4Network(*other.m_IPv4Network); - } - - if (other.m_IPv6Network) - { - m_IPv6Network = new IPv6Network(*other.m_IPv6Network); - } - } - - /** - * A destructor for this class - */ - ~IPNetwork() - { - if (m_IPv4Network) - { - delete m_IPv4Network; - } - - if (m_IPv6Network) - { - delete m_IPv6Network; - } - } - - /** - * Overload of an assignment operator. - * @param[in] other An instance of IPNetwork to assign - * @return A reference to the assignee - */ - IPNetwork& operator=(const IPNetwork& other) - { - if (other.isIPv4Network()) - { - return this->operator=(*other.m_IPv4Network); - } - else - { - return this->operator=(*other.m_IPv6Network); - } - } - - /** - * Overload of an assignment operator. - * @param[in] other An instance of IPv4Network to assign - * @return A reference to the assignee - */ - IPNetwork& operator=(const IPv4Network& other) - { - if (m_IPv4Network) - { - delete m_IPv4Network; - m_IPv4Network = nullptr; - } - - if (m_IPv6Network) - { - delete m_IPv6Network; - m_IPv6Network = nullptr; - } - - m_IPv4Network = new IPv4Network(other); - - return *this; - } - - /** - * Overload of an assignment operator. - * @param[in] other An instance of IPv6Network to assign - * @return A reference to the assignee - */ - IPNetwork& operator=(const IPv6Network& other) - { - if (m_IPv4Network) - { - delete m_IPv4Network; - m_IPv4Network = nullptr; - } - - if (m_IPv6Network) - { - delete m_IPv6Network; - m_IPv6Network = nullptr; - } - - m_IPv6Network = new IPv6Network(other); - - return *this; - } - - /** - * @return The prefix length, for example: the prefix length of 3546::/ffff:: is 16, the prefix length of - * 10.10.10.10/255.0.0.0 is 8 - */ - uint8_t getPrefixLen() const - { - return (m_IPv4Network != nullptr ? m_IPv4Network->getPrefixLen() : m_IPv6Network->getPrefixLen()); - } - - /** - * @return The netmask, for example: the netmask of 3546::/16 is ffff::, the netmask of 10.10.10.10/8 is 255.0.0.0 - */ - std::string getNetmask() const - { - return (m_IPv4Network != nullptr ? m_IPv4Network->getNetmask() : m_IPv6Network->getNetmask()); - } - - /** - * @return The network prefix, for example: the network prefix of 3546:f321::/16 is 3546::, the network prefix - * of 10.10.10.10/16 is 10.10.0.0 - */ - IPAddress getNetworkPrefix() const - { - return (m_IPv4Network != nullptr ? IPAddress(m_IPv4Network->getNetworkPrefix()) : IPAddress(m_IPv6Network->getNetworkPrefix())); - } - - /** - * @return The lowest non-reserved IP address in this network, for example: the lowest address in 3546::/16 is - * 3546::1, the lowest address in 10.10.10.10/16 is 10.10.0.1 - */ - IPAddress getLowestAddress() const - { - return (m_IPv4Network != nullptr ? IPAddress(m_IPv4Network->getLowestAddress()) : IPAddress(m_IPv6Network->getLowestAddress())); - } - - /** - * @return The highest non-reserved IP address in this network, for example: the highest address in 3546::/16 is - * 3546:ffff:ffff:ffff:ffff:ffff:ffff:ffff, the highest address in 10.10.10.10/16 is 10.10.255.254 - */ - IPAddress getHighestAddress() const - { - return (m_IPv4Network != nullptr ? IPAddress(m_IPv4Network->getHighestAddress()) : IPAddress(m_IPv6Network->getHighestAddress())); - } - - /** - * @return The number of addresses in this network, for example: the number of addresses in 16ff::/120 is 256, - * the number of addresses in 10.10.0.0/24 is 256. If the number of addresses exceeds the size of uint64_t - * a std::out_of_range exception is thrown - */ - uint64_t getTotalAddressCount() const - { - return (m_IPv4Network != nullptr ? m_IPv4Network->getTotalAddressCount() : m_IPv6Network->getTotalAddressCount()); - } - - /** - * @return True if this is an IPv4 network, false otherwise - */ - bool isIPv4Network() const - { - return m_IPv4Network != nullptr; - } - - /** - * @return True if this is an IPv6 network, false otherwise - */ - bool isIPv6Network() const - { - return m_IPv6Network != nullptr; - } - - /** - * @param address An IP address - * @return True is the address belongs to the network, false otherwise or if the address isn't valid - */ - bool includes(const IPAddress& address) const - { - if (m_IPv4Network != nullptr) - { - if (address.isIPv6()) - { - return false; - } - - return m_IPv4Network->includes(address.getIPv4()); - } - else - { - if (address.isIPv4()) - { - return false; - } - - return m_IPv6Network->includes(address.getIPv6()); - } - } - - /** - * @param network An IP network - * @return True is the input network is completely included within this network, false otherwise - */ - bool includes(const IPNetwork& network) const - { - if (m_IPv4Network != nullptr) - { - if (network.isIPv6Network()) - { - return false; - } - - return m_IPv4Network->includes(*network.m_IPv4Network); - } - else - { - if (network.isIPv4Network()) - { - return false; - } - - return m_IPv6Network->includes(*network.m_IPv6Network); - } - } - - /** - * @return A string representation of the network in a format of NETWORK_PREFIX/PREFIX_LEN, for example: - * fda7:9f81:6c23:275::/64 or 192.168.0.0/16 - */ - std::string toString() const - { - return (m_IPv4Network != nullptr ? m_IPv4Network->toString() : m_IPv6Network->toString()); - } - - private: - IPv4Network* m_IPv4Network; - IPv6Network* m_IPv6Network; - }; +namespace pcpp { + +// forward declarations +class IPv4Network; +class IPv6Network; + +// The implementation of the classes is based on document N4771 "Working Draft, +// C++ Extensions for Networking" +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/n4771.pdf + +/** + * @class IPv4Address + * Represents an IPv4 address (of type XXX.XXX.XXX.XXX) + */ +class IPv4Address { + public: + /** + * A default constructor that creates an instance of the class with + * unspecified/zero address + */ + IPv4Address() { memset(m_Bytes, 0, sizeof(m_Bytes)); } + + /** + * A constructor that creates an instance of the class out of 4-byte integer + * value. + * @param[in] addrAsInt The address as 4-byte integer in network byte order + */ + IPv4Address(uint32_t addrAsInt) { + memcpy(m_Bytes, &addrAsInt, sizeof(m_Bytes)); + } + + /** + * A constructor that creates an instance of the class out of 4-byte array. + * @param[in] bytes The address as 4-byte array in network byte order + */ + IPv4Address(const uint8_t bytes[4]) { + memcpy(m_Bytes, bytes, sizeof(m_Bytes)); + } + + /** + * A constructor that creates an instance of the class out of std::string + * value If the string doesn't represent a valid IPv4 address, an instance + * will store an unspecified address + * @param[in] addrAsString The std::string representation of the address + */ + IPv4Address(const std::string& addrAsString); + + /** + * Converts the IPv4 address into a 4B integer + * @return a 4B integer in network byte order representing the IPv4 address + */ + inline uint32_t toInt() const; + + /** + * Returns a pointer to 4-byte array representing the IPv4 address + */ + const uint8_t* toBytes() const { return m_Bytes; } + + /** + * Returns a std::string representation of the address + * @return A string representation of the address + */ + std::string toString() const; + + /** + * Determine whether the address is a multicast address + * @return True if an address is multicast + */ + bool isMulticast() const; + + /** + * Determine whether the address is valid (it's not an unspecified/zero) + * @return True if an address is not unspecified/zero + */ + bool isValid() const { return toInt() != 0; } + + /** + * Overload of the equal-to operator + * @param[in] rhs The object to compare with + * @return True if the addresses are equal, false otherwise + */ + bool operator==(const IPv4Address& rhs) const { + return toInt() == rhs.toInt(); + } + + /** + * Overload of the less-than operator + * @param[in] rhs The object to compare with + * @return True if the address value is lower than the other address value, + * false otherwise + */ + bool operator<(const IPv4Address& rhs) const { + uint32_t intVal = toInt(); + std::reverse((uint8_t*)(&intVal), (uint8_t*)(&intVal) + sizeof(intVal)); + + uint32_t rhsIntVal = rhs.toInt(); + std::reverse((uint8_t*)(&rhsIntVal), + (uint8_t*)(&rhsIntVal) + sizeof(rhsIntVal)); + + return intVal < rhsIntVal; + } + + /** + * Overload of the not-equal-to operator + * @param[in] rhs The object to compare with + * @return True if the addresses are not equal, false otherwise + */ + bool operator!=(const IPv4Address& rhs) const { return !(*this == rhs); } + + /** + * Checks whether the address matches a network. + * @param network An IPv4Network network + * @return True if the address matches the network or false otherwise + */ + bool matchNetwork(const IPv4Network& network) const; + + /** + * Checks whether the address matches a network. + * For example: this method will return true for address 10.1.1.9 and network + * which is one of: 10.1.1.1/24, 10.1.1.1/255.255.255.0 Another example: this + * method will return false for address 11.1.1.9 and network which is one of: + * 10.1.1.1/16, 10.1.1.1/255.255.0.0 + * @param[in] network A string in one of these formats: + * - X.X.X.X/Y where X.X.X.X is a valid IP address and Y is a number between + * 0 and 32 + * - X.X.X.X/Y.Y.Y.Y where X.X.X.X is a valid IP address and Y.Y.Y.Y is a + * valid netmask + * @return True if the address matches the network or false if it doesn't or + * if the network is invalid + */ + bool matchNetwork(const std::string& network) const; + + /** + * @deprecated This method is deprecated, please use matchNetwork(const + * IPv4Network& network) + */ + PCPP_DEPRECATED bool matchSubnet(const IPv4Address& subnet, + const std::string& subnetMask) const; + + /** + * @deprecated This method is deprecated, please use matchNetwork(const + * IPv4Network& network) + */ + PCPP_DEPRECATED bool matchSubnet(const IPv4Address& subnet, + const IPv4Address& subnetMask) const; + + /** + * A static value representing a zero value of IPv4 address, meaning address + * of value "0.0.0.0" Notice this value can be omitted in the user code + * because the default constructor creates an instance with an + * unspecified/zero address. In order to check whether the address is zero the + * method isValid can be used + */ + static const IPv4Address Zero; + + /** + * A static values representing the lower and upper bound of IPv4 multicast + * ranges. The bounds are inclusive. MulticastRangeLowerBound is initialized + * to "224.0.0.0". MulticastRangeUpperBound is initialized to + * "239.255.255.255". In order to check whether the address is a multicast + * address the isMulticast method can be used. + */ + static const IPv4Address MulticastRangeLowerBound; + static const IPv4Address MulticastRangeUpperBound; + + private: + uint8_t m_Bytes[4]; +}; // class IPv4Address + +// Implementation of inline methods + +uint32_t IPv4Address::toInt() const { + uint32_t addr; + memcpy(&addr, m_Bytes, sizeof(m_Bytes)); + return addr; +} + +/** + * @class IPv6Address + * Represents an IPv6 address (of type xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx). + */ +class IPv6Address { + public: + /** + * A default constructor that creates an instance of the class with + * unspecified/zero address + */ + IPv6Address() { memset(m_Bytes, 0, sizeof(m_Bytes)); } + + /** + * A constructor that creates an instance of the class out of 16-byte array. + * @param[in] bytes The address as 16-byte array in network byte order + */ + IPv6Address(const uint8_t bytes[16]) { + memcpy(m_Bytes, bytes, sizeof(m_Bytes)); + } + + /** + * A constructor that creates an instance of the class out of std::string + * value If the string doesn't represent a valid IPv6 address, an instance + * will store an unspecified address + * @param[in] addrAsString The std::string representation of the address + */ + IPv6Address(const std::string& addrAsString); + + /** + * Returns a pointer to 16-byte array representing the IPv6 address + */ + const uint8_t* toBytes() const { return m_Bytes; } + + /** + * Returns a std::string representation of the address + * @return A string representation of the address + */ + std::string toString() const; + + /** + * Determine whether the address is a multicast address + * @return True if an address is multicast + */ + bool isMulticast() const; + + /** + * Determine whether the address is unspecified + */ + bool isValid() const { return *this != Zero; } + + /** + * Overload of the equal-to operator + * @param[in] rhs The object to compare with + * @return True if the addresses are equal, false otherwise + */ + bool operator==(const IPv6Address& rhs) const { + return memcmp(toBytes(), rhs.toBytes(), sizeof(m_Bytes)) == 0; + } + + /** + * Overload of the less-than operator + * @param[in] rhs The object to compare with + * @return True if the address value is lower than the other address value, + * false otherwise + */ + bool operator<(const IPv6Address& rhs) const { + return memcmp(toBytes(), rhs.toBytes(), sizeof(m_Bytes)) < 0; + } + + /** + * Overload of the not-equal-to operator + * @param[in] rhs The object to compare with + * @return True if the addresses are not equal, false otherwise + */ + bool operator!=(const IPv6Address& rhs) const { return !(*this == rhs); } + + /** + * Allocates a byte array and copies address value into it. Array deallocation + * is user responsibility + * @param[in] arr A pointer to where array will be allocated + * @param[out] length Returns the length in bytes of the array that was + * allocated + */ + void copyTo(uint8_t** arr, size_t& length) const; + + /** + * Gets a pointer to an already allocated byte array and copies the address + * value to it. This method assumes array allocated size is at least 16 (the + * size of an IPv6 address) + * @param[in] arr A pointer to the array which address will be copied to + */ + void copyTo(uint8_t* arr) const { memcpy(arr, m_Bytes, sizeof(m_Bytes)); } + + /** + * Checks whether the address matches a network. + * @param network An IPv6Network network + * @return True if the address matches the network or false otherwise + */ + bool matchNetwork(const IPv6Network& network) const; + + /** + * Checks whether the address matches a network. + * For example: this method will return true for address + * d6e5:83dc:0c58:bc5d:1449:5898:: and network which is one of: + * d6e5:83dc:0c58:bc5d::/64, d6e5:83dc:0c58:bc5d::/ffff:ffff:ffff:ffff:: + * Another example: this method will return false for address d6e5:83dc:: and + * network which is one of: d6e5:83dc:0c58:bc5d::/64, + * d6e5:83dc:0c58:bc5d::/ffff:ffff:ffff:ffff:: + * @param[in] network A string in one of these formats: + * - IPV6_ADDRESS/Y where IPV6_ADDRESS is a valid IPv6 address and Y is a + * number between 0 and 128 + * - IPV6_ADDRESS/IPV6_NETMASK where IPV6_ADDRESS is a valid IPv6 address and + * IPV6_NETMASK is a valid IPv6 netmask + * @return True if the address matches the network or false if it doesn't or + * if the network is invalid + */ + bool matchNetwork(const std::string& network) const; + + /** + * @deprecated This method is deprecated, please use matchNetwork(const + * IPv6Network& network) + */ + PCPP_DEPRECATED bool matchSubnet(const IPv6Address& subnet, + uint8_t prefixLength) const; + + /** + * A static value representing a zero value of IPv6 address, meaning address + * of value "0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0" Notice this value can be omitted + * in the user code because the default constructor creates an instance with + * an unspecified/zero address. In order to check whether the address is zero + * the method isValid can be used + */ + static const IPv6Address Zero; + + /** + * A static value representing the lower bound of IPv6 multicast ranges. The + * bound is inclusive. MulticastRangeLowerBound is initialized to + * "ff00:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0". In order to check whether the address + * is a multicast address the isMulticast method can be used. + */ + static const IPv6Address MulticastRangeLowerBound; + + private: + uint8_t m_Bytes[16]; +}; // class IPv6Address + +/** + * @class IPAddress + * The class is a version-independent representation for an IP address + */ +class IPAddress { + public: + /** + * An enum representing the address type: IPv4 or IPv6 + */ + enum AddressType { + /** + * IPv4 address type + */ + IPv4AddressType, + /** + * IPv6 address type + */ + IPv6AddressType + }; + + /** + * A default constructor that creates an instance of the class with + * unspecified IPv4 address + */ + IPAddress() : m_Type(IPv4AddressType) {} + + /** + * A constructor that creates an instance of the class out of IPv4Address. + * @param[in] addr A const reference to instance of IPv4Address + */ + IPAddress(const IPv4Address& addr) : m_Type(IPv4AddressType), m_IPv4(addr) {} + + /** + * A constructor that creates an instance of the class out of IPv6Address. + * @param[in] addr A const reference to instance of IPv6Address + */ + IPAddress(const IPv6Address& addr) : m_Type(IPv6AddressType), m_IPv6(addr) {} + + /** + * A constructor that creates an instance of the class out of std::string + * value If the string doesn't represent a valid IPv4 or IPv6 address, an + * instance will store an unspecified address + * @param[in] addrAsString The std::string representation of the address + */ + IPAddress(const std::string& addrAsString); + + /** + * Overload of an assignment operator. + * @param[in] addr A const reference to instance of IPv4Address + * @return A reference to the assignee + */ + inline IPAddress& operator=(const IPv4Address& addr); + + /** + * Overload of an assignment operator. + * @param[in] addr A const reference to instance of IPv6Address + * @return A reference to the assignee + */ + inline IPAddress& operator=(const IPv6Address& addr); + + /** + * Gets the address type: IPv4 or IPv6 + * @return The address type + */ + AddressType getType() const { return static_cast(m_Type); } + + /** + * Returns a std::string representation of the address + * @return A string representation of the address + */ + std::string toString() const { + return (getType() == IPv4AddressType) ? m_IPv4.toString() + : m_IPv6.toString(); + } + + /** + * @return Determine whether the address is unspecified + */ + bool isValid() const { + return (getType() == IPv4AddressType) ? m_IPv4.isValid() : m_IPv6.isValid(); + } + + /** + * @return Determine whether the object contains an IP version 4 address + */ + bool isIPv4() const { return getType() == IPv4AddressType; } + + /** + * @return Determine whether the object contains an IP version 6 address + */ + bool isIPv6() const { return getType() == IPv6AddressType; } + + /** + * Determine whether the address is a multicast address + * @return True if an address is multicast + */ + bool isMulticast() const { + return (getType() == IPv4AddressType) ? m_IPv4.isMulticast() + : m_IPv6.isMulticast(); + } + + /** + * Get a reference to IPv4 address instance + * @return The const reference to IPv4Address instance + */ + const IPv4Address& getIPv4() const { return m_IPv4; } + + /** + * Get a reference to IPv6 address instance + * @return The const reference to IPv6Address instance + */ + const IPv6Address& getIPv6() const { return m_IPv6; } + + /** + * Overload of the equal-to operator + * @param[in] rhs The object to compare with + * @return True if the addresses are equal, false otherwise + */ + inline bool operator==(const IPAddress& rhs) const; + + /** + * Overload of the less-than operator + * @param[in] rhs The object to compare with + * @return True if the address value is lower than the other address value, + * false otherwise + */ + inline bool operator<(const IPAddress& rhs) const; + + /** + * Overload of the not-equal-to operator + * @param[in] rhs The object to compare with + * @return True if the addresses are not equal, false otherwise + */ + bool operator!=(const IPAddress& rhs) const { return !(*this == rhs); } + + private: + uint8_t m_Type; + IPv4Address m_IPv4; + IPv6Address m_IPv6; +}; + +// implementation of inline methods + +bool IPAddress::operator==(const IPAddress& rhs) const { + if (isIPv4()) + return rhs.isIPv4() ? (m_IPv4 == rhs.m_IPv4) : false; + + return rhs.isIPv6() ? m_IPv6 == rhs.m_IPv6 : false; +} + +bool IPAddress::operator<(const IPAddress& rhs) const { + if (isIPv4()) { + // treat IPv4 as less than IPv6 + // If current obj is IPv4 and other is IPv6 return true + return rhs.isIPv4() ? (m_IPv4 < rhs.m_IPv4) : true; + } + return rhs.isIPv6() ? m_IPv6 < rhs.m_IPv6 : false; +} + +IPAddress& IPAddress::operator=(const IPv4Address& addr) { + m_Type = IPv4AddressType; + m_IPv4 = addr; + return *this; +} + +IPAddress& IPAddress::operator=(const IPv6Address& addr) { + m_Type = IPv6AddressType; + m_IPv6 = addr; + return *this; +} + +/** + * @class IPv4Network + * A class representing IPv4 network definition + */ +class IPv4Network { + public: + /** + * A constructor that creates an instance of the class out of an address + * representing the network prefix and a prefix length + * @param address An address representing the network prefix. If the address + * is invalid std::invalid_argument exception is thrown + * @param prefixLen A number between 0 and 32 representing the prefix length. + * If another value is provided std::invalid_argument exception is thrown + */ + IPv4Network(const IPv4Address& address, uint8_t prefixLen); + + /** + * A constructor that creates an instance of the class out of an address + * representing the network prefix and a netmask + * @param address An address representing the network prefix. If the address + * is invalid std::invalid_argument exception is thrown + * @param netmask A string representing a netmask in the format of X.X.X.X, + * for example: 255.255.0.0. Please notice that netmasks that start with zeros + * are invalid, for example: 0.0.255.255. The only netmask starting with zeros + * that is valid is 0.0.0.0. If the netmask is invalid std::invalid_argument + * exception is thrown + */ + IPv4Network(const IPv4Address& address, const std::string& netmask); + + /** + * A constructor that creates an instance of the class out of a string + * representing the network prefix and a prefix length or a netmask + * @param addressAndNetmask A string in one of these formats: + * - X.X.X.X/Y where X.X.X.X is a valid IPv4 address representing the network + * prefix and Y is a number between 0 and 32 representing the network prefix + * - X.X.X.X/Y.Y.Y.Y where X.X.X.X is a valid IPv4 address representing the + * network prefix and Y.Y.Y.Y is a valid netmask For any invalid value + * std::invalid_argument is thrown + */ + IPv4Network(const std::string& addressAndNetmask); + + /** + * @return The prefix length, for example: the prefix length + * of 10.10.10.10/255.0.0.0 is 8 + */ + uint8_t getPrefixLen() const; + + /** + * @return The netmask, for example: the netmask of 10.10.10.10/8 is 255.0.0.0 + */ + std::string getNetmask() const { return IPv4Address(m_Mask).toString(); } + + /** + * @return The network prefix, for example: the network prefix + * of 10.10.10.10/16 is 10.10.0.0 + */ + IPv4Address getNetworkPrefix() const { return IPv4Address(m_NetworkPrefix); } + + /** + * @return The lowest non-reserved IPv4 address in this network, for example: + * the lowest address in 10.10.10.10/16 is 10.10.0.1 + */ + IPv4Address getLowestAddress() const; + + /** + * @return The highest non-reserved IPv4 address in this network, for example: + * the highest address in 10.10.10.10/16 is 10.10.255.254 + */ + IPv4Address getHighestAddress() const; + + /** + * @return The number of addresses in this network including reserved + * addresses, for example: the number of addresses in 10.10.0.0/24 is 256 + */ + uint64_t getTotalAddressCount() const; + + /** + * @param address An IPv4 address + * @return True is the address belongs to the network, false otherwise or if + * the address isn't valid + */ + bool includes(const IPv4Address& address) const; + + /** + * @param network An IPv4 network + * @return True is the input network is completely included within this + * network, false otherwise, for example: 10.10.10.10/16 + * includes 10.10.10.10/24 but doesn't include 10.10.10.10/8 + */ + bool includes(const IPv4Network& network) const; + + /** + * @return A string representation of the network in a format of + * NETWORK_PREFIX/PREFIX_LEN, for example: 192.168.0.0/16 + */ + std::string toString() const; + + private: + uint32_t m_NetworkPrefix; + uint32_t m_Mask; + + bool isValidNetmask(const std::string& netmask); + void initFromAddressAndPrefixLength(const IPv4Address& address, + uint8_t prefixLen); + void initFromAddressAndNetmask(const IPv4Address& address, + const std::string& netmask); +}; + +/** + * @class IPv6Network + * A class representing IPv6 network definition + */ +class IPv6Network { + public: + /** + * A constructor that creates an instance of the class out of an address + * representing the network prefix and a prefix length + * @param address An address representing the network prefix. If the address + * is invalid std::invalid_argument exception is thrown + * @param prefixLen A number between 0 and 128 representing the prefix length. + * If another value is provided std::invalid_argument exception is thrown + */ + IPv6Network(const IPv6Address& address, uint8_t prefixLen); + + /** + * A constructor that creates an instance of the class out of an address + * representing the network prefix and a netmask + * @param address An address representing the network prefix. If the address + * is invalid std::invalid_argument exception is thrown + * @param netmask A string representing a netmask in valid IPv6 format, for + * example: ffff:ffff::. Please notice that netmasks that start with zeros are + * invalid, for example: 0:ffff::. The only netmask starting with zeros that + * is valid is all zeros (::). If the netmask is invalid std::invalid_argument + * exception is thrown + */ + IPv6Network(const IPv6Address& address, const std::string& netmask); + + /** + * A constructor that creates an instance of the class out of a string + * representing the network prefix and a prefix length or a netmask + * @param addressAndNetmask A string in one of these formats: + * - IPV6_ADDRESS/Y where IPV6_ADDRESS is a valid IPv6 address representing + * the network prefix and Y is a number between 0 and 128 representing the + * network prefix + * - IPV6_ADDRESS/IPV6_NETMASK where IPV6_ADDRESS is a valid IPv6 address + * representing the network prefix and IPV6_NETMASK is a valid IPv6 netmask + * For any invalid value std::invalid_argument is thrown + */ + IPv6Network(const std::string& addressAndNetmask); + + /** + * @return The prefix length, for example: the prefix length of 3546::/ffff:: + * is 16 + */ + uint8_t getPrefixLen() const; + + /** + * @return The netmask, for example: the netmask of 3546::/16 is ffff:: + */ + std::string getNetmask() const { return IPv6Address(m_Mask).toString(); } + + /** + * @return The network prefix, for example: the network prefix of + * 3546:f321::/16 is 3546:: + */ + IPv6Address getNetworkPrefix() const { return IPv6Address(m_NetworkPrefix); } + + /** + * @return The lowest non-reserved IPv6 address in this network, for example: + * the lowest address in 3546::/16 is 3546::1 + */ + IPv6Address getLowestAddress() const; + + /** + * @return The highest IPv6 address in this network, for example: the highest + * address in 3546::/16 is 3546:ffff:ffff:ffff:ffff:ffff:ffff:ffff + */ + IPv6Address getHighestAddress() const; + + /** + * @return The number of addresses in this network, for example: the number of + * addresses in 16ff::/120 is 256. If the number of addresses exceeds the size + * of uint64_t a std::out_of_range exception is thrown + */ + uint64_t getTotalAddressCount() const; + + /** + * @param address An IPv6 address + * @return True is the address belongs to the network, false otherwise or if + * the address isn't valid + */ + bool includes(const IPv6Address& address) const; + + /** + * @param network An IPv6 network + * @return True is the input network is completely included within this + * network, false otherwise, for example: 3546::/64 includes 3546::/120 but + * doesn't include 3546::/16 + */ + bool includes(const IPv6Network& network) const; + + /** + * @return A string representation of the network in a format of + * NETWORK_PREFIX/PREFIX_LEN, for example: fda7:9f81:6c23:275::/64 + */ + std::string toString() const; + + private: + uint8_t m_NetworkPrefix[16]; + uint8_t m_Mask[16]; + + bool isValidNetmask(const std::string& netmask); + void initFromAddressAndPrefixLength(const IPv6Address& address, + uint8_t prefixLen); + void initFromAddressAndNetmask(const IPv6Address& address, + const std::string& netmask); +}; + +/** + * @class IPNetwork + * A class representing version independent IP network definition, both IPv4 and + * IPv6 are included + */ +class IPNetwork { + public: + /** + * A constructor that creates an instance of the class out of an address + * representing the network prefix and a prefix length + * @param address An address representing the network prefix. If the address + * is invalid std::invalid_argument exception is thrown + * @param prefixLen A number representing the prefix length. If the value + * isn't in the range allowed for the network (0 - 32 for IPv4 networks or 0 - + * 128 for IPv6 networks) and std::invalid_argument exception is thrown + */ + IPNetwork(const IPAddress& address, uint8_t prefixLen) { + if (address.isIPv4()) { + m_IPv4Network = new IPv4Network(address.getIPv4(), prefixLen); + m_IPv6Network = nullptr; + } else { + m_IPv6Network = new IPv6Network(address.getIPv6(), prefixLen); + m_IPv4Network = nullptr; + } + } + + /** + * A constructor that creates an instance of the class out of an address + * representing the network prefix and a netmask + * @param address An address representing the network prefix. If the address + * is invalid std::invalid_argument exception is thrown + * @param netmask A string representing a netmask in valid format, for + * example: ffff:ffff:: for IPv6 networks or 255.255.0.0 for IPv4 networks. + * Please notice that netmasks that start with zeros are invalid, for example: + * 0:ffff:: or 0.255.255.255. The only netmask starting with zeros that is + * valid is all zeros (:: or 0.0.0.0). If the netmask is invalid + * std::invalid_argument exception is thrown + */ + IPNetwork(const IPAddress& address, const std::string& netmask) { + if (address.isIPv4()) { + m_IPv4Network = new IPv4Network(address.getIPv4(), netmask); + m_IPv6Network = nullptr; + } else { + m_IPv6Network = new IPv6Network(address.getIPv6(), netmask); + m_IPv4Network = nullptr; + } + } + + /** + * A constructor that creates an instance of the class out of a string + * representing the network prefix and a prefix length or a netmask + * @param addressAndNetmask A string in one of these formats: + * - IP_ADDRESS/Y where IP_ADDRESS is a valid IP address representing the + * network prefix and Y is a number representing the network prefix + * - IP_ADDRESS/NETMASK where IP_ADDRESS is a valid IP address representing + * the network prefix and NETMASK is a valid netmask for this type of network + * (IPv4 or IPv6 network) For any invalid value std::invalid_argument is + * thrown + */ + IPNetwork(const std::string& addressAndNetmask) { + try { + m_IPv4Network = new IPv4Network(addressAndNetmask); + m_IPv6Network = nullptr; + } catch (const std::invalid_argument&) { + m_IPv6Network = new IPv6Network(addressAndNetmask); + m_IPv4Network = nullptr; + } + } + + /** + * A copy c'tor for this class + * @param other The instance to copy from + */ + IPNetwork(const IPNetwork& other) { + m_IPv4Network = nullptr; + m_IPv6Network = nullptr; + + if (other.m_IPv4Network) { + m_IPv4Network = new IPv4Network(*other.m_IPv4Network); + } + + if (other.m_IPv6Network) { + m_IPv6Network = new IPv6Network(*other.m_IPv6Network); + } + } + + /** + * A destructor for this class + */ + ~IPNetwork() { + if (m_IPv4Network) { + delete m_IPv4Network; + } + + if (m_IPv6Network) { + delete m_IPv6Network; + } + } + + /** + * Overload of an assignment operator. + * @param[in] other An instance of IPNetwork to assign + * @return A reference to the assignee + */ + IPNetwork& operator=(const IPNetwork& other) { + if (other.isIPv4Network()) { + return this->operator=(*other.m_IPv4Network); + } else { + return this->operator=(*other.m_IPv6Network); + } + } + + /** + * Overload of an assignment operator. + * @param[in] other An instance of IPv4Network to assign + * @return A reference to the assignee + */ + IPNetwork& operator=(const IPv4Network& other) { + if (m_IPv4Network) { + delete m_IPv4Network; + m_IPv4Network = nullptr; + } + + if (m_IPv6Network) { + delete m_IPv6Network; + m_IPv6Network = nullptr; + } + + m_IPv4Network = new IPv4Network(other); + + return *this; + } + + /** + * Overload of an assignment operator. + * @param[in] other An instance of IPv6Network to assign + * @return A reference to the assignee + */ + IPNetwork& operator=(const IPv6Network& other) { + if (m_IPv4Network) { + delete m_IPv4Network; + m_IPv4Network = nullptr; + } + + if (m_IPv6Network) { + delete m_IPv6Network; + m_IPv6Network = nullptr; + } + + m_IPv6Network = new IPv6Network(other); + + return *this; + } + + /** + * @return The prefix length, for example: the prefix length of 3546::/ffff:: + * is 16, the prefix length of 10.10.10.10/255.0.0.0 is 8 + */ + uint8_t getPrefixLen() const { + return (m_IPv4Network != nullptr ? m_IPv4Network->getPrefixLen() + : m_IPv6Network->getPrefixLen()); + } + + /** + * @return The netmask, for example: the netmask of 3546::/16 is ffff::, the + * netmask of 10.10.10.10/8 is 255.0.0.0 + */ + std::string getNetmask() const { + return (m_IPv4Network != nullptr ? m_IPv4Network->getNetmask() + : m_IPv6Network->getNetmask()); + } + + /** + * @return The network prefix, for example: the network prefix of + * 3546:f321::/16 is 3546::, the network prefix of 10.10.10.10/16 is 10.10.0.0 + */ + IPAddress getNetworkPrefix() const { + return (m_IPv4Network != nullptr + ? IPAddress(m_IPv4Network->getNetworkPrefix()) + : IPAddress(m_IPv6Network->getNetworkPrefix())); + } + + /** + * @return The lowest non-reserved IP address in this network, for example: + * the lowest address in 3546::/16 is 3546::1, the lowest address + * in 10.10.10.10/16 is 10.10.0.1 + */ + IPAddress getLowestAddress() const { + return (m_IPv4Network != nullptr + ? IPAddress(m_IPv4Network->getLowestAddress()) + : IPAddress(m_IPv6Network->getLowestAddress())); + } + + /** + * @return The highest non-reserved IP address in this network, for example: + * the highest address in 3546::/16 is + * 3546:ffff:ffff:ffff:ffff:ffff:ffff:ffff, the highest address + * in 10.10.10.10/16 is 10.10.255.254 + */ + IPAddress getHighestAddress() const { + return (m_IPv4Network != nullptr + ? IPAddress(m_IPv4Network->getHighestAddress()) + : IPAddress(m_IPv6Network->getHighestAddress())); + } + + /** + * @return The number of addresses in this network, for example: the number of + * addresses in 16ff::/120 is 256, the number of addresses in 10.10.0.0/24 is + * 256. If the number of addresses exceeds the size of uint64_t a + * std::out_of_range exception is thrown + */ + uint64_t getTotalAddressCount() const { + return (m_IPv4Network != nullptr ? m_IPv4Network->getTotalAddressCount() + : m_IPv6Network->getTotalAddressCount()); + } + + /** + * @return True if this is an IPv4 network, false otherwise + */ + bool isIPv4Network() const { return m_IPv4Network != nullptr; } + + /** + * @return True if this is an IPv6 network, false otherwise + */ + bool isIPv6Network() const { return m_IPv6Network != nullptr; } + + /** + * @param address An IP address + * @return True is the address belongs to the network, false otherwise or if + * the address isn't valid + */ + bool includes(const IPAddress& address) const { + if (m_IPv4Network != nullptr) { + if (address.isIPv6()) { + return false; + } + + return m_IPv4Network->includes(address.getIPv4()); + } else { + if (address.isIPv4()) { + return false; + } + + return m_IPv6Network->includes(address.getIPv6()); + } + } + + /** + * @param network An IP network + * @return True is the input network is completely included within this + * network, false otherwise + */ + bool includes(const IPNetwork& network) const { + if (m_IPv4Network != nullptr) { + if (network.isIPv6Network()) { + return false; + } + + return m_IPv4Network->includes(*network.m_IPv4Network); + } else { + if (network.isIPv4Network()) { + return false; + } + + return m_IPv6Network->includes(*network.m_IPv6Network); + } + } + + /** + * @return A string representation of the network in a format of + * NETWORK_PREFIX/PREFIX_LEN, for example: fda7:9f81:6c23:275::/64 or + * 192.168.0.0/16 + */ + std::string toString() const { + return (m_IPv4Network != nullptr ? m_IPv4Network->toString() + : m_IPv6Network->toString()); + } + + private: + IPv4Network* m_IPv4Network; + IPv6Network* m_IPv6Network; +}; } // namespace pcpp -inline std::ostream& operator<<(std::ostream& os, const pcpp::IPv4Address& ipv4Address) -{ - os << ipv4Address.toString(); - return os; +inline std::ostream& operator<<(std::ostream& os, + const pcpp::IPv4Address& ipv4Address) { + os << ipv4Address.toString(); + return os; } -inline std::ostream& operator<<(std::ostream& os, const pcpp::IPv6Address& ipv6Address) -{ - os << ipv6Address.toString(); - return os; +inline std::ostream& operator<<(std::ostream& os, + const pcpp::IPv6Address& ipv6Address) { + os << ipv6Address.toString(); + return os; } -inline std::ostream& operator<<(std::ostream& os, const pcpp::IPAddress& ipAddress) -{ - os << ipAddress.toString(); - return os; +inline std::ostream& operator<<(std::ostream& os, + const pcpp::IPAddress& ipAddress) { + os << ipAddress.toString(); + return os; } -inline std::ostream& operator<<(std::ostream& os, const pcpp::IPv4Network& network) -{ - os << network.toString(); - return os; +inline std::ostream& operator<<(std::ostream& os, + const pcpp::IPv4Network& network) { + os << network.toString(); + return os; } -inline std::ostream& operator<<(std::ostream& os, const pcpp::IPv6Network& network) -{ - os << network.toString(); - return os; +inline std::ostream& operator<<(std::ostream& os, + const pcpp::IPv6Network& network) { + os << network.toString(); + return os; } -inline std::ostream& operator<<(std::ostream& os, const pcpp::IPNetwork& network) -{ - os << network.toString(); - return os; +inline std::ostream& operator<<(std::ostream& os, + const pcpp::IPNetwork& network) { + os << network.toString(); + return os; } #endif /* PCAPPP_IPADDRESS */ diff --git a/Common++/header/IpUtils.h b/Common++/header/IpUtils.h index 2f109d7f2a..c83ebafb15 100644 --- a/Common++/header/IpUtils.h +++ b/Common++/header/IpUtils.h @@ -3,33 +3,35 @@ #include #ifdef __linux__ -#include #include +#include #endif #if defined(__APPLE__) -#include #include +#include #endif #if defined(_WIN32) #include #endif #if defined(__FreeBSD__) -#include -#include #include +#include +#include #endif /// @file // Both Visual C++ Compiler and MinGW-w64 define inet_ntop() and inet_pton() // Add compatibility functions for old MinGW (aka MinGW32) -// We use "__MINGW64_VERSION_MAJOR" and not __MINGW64__ to detect MinGW-w64 compiler -// because the second one is not defined for MinGW-w64 in 32bits mode -#if defined(_WIN32) && !defined(_MSC_VER) && (!defined(__MINGW64_VERSION_MAJOR) || (__MINGW64_VERSION_MAJOR < 8)) +// We use "__MINGW64_VERSION_MAJOR" and not __MINGW64__ to detect MinGW-w64 +// compiler because the second one is not defined for MinGW-w64 in 32bits mode +#if defined(_WIN32) && !defined(_MSC_VER) && \ + (!defined(__MINGW64_VERSION_MAJOR) || (__MINGW64_VERSION_MAJOR < 8)) /** * Convert a network format address to presentation format. * @param[in] af Address family, can be either AF_INET (IPv4) or AF_INET6 (IPv6) - * @param[in] src Network address structure, can be either in_addr (IPv4) or in6_addr (IPv6) + * @param[in] src Network address structure, can be either in_addr (IPv4) or + * in6_addr (IPv6) * @param[out] dst Network address string representation * @param[in] size 'dst' Maximum size * @return pointer to presentation format address ('dst'), or NULL (see errno). @@ -41,7 +43,8 @@ const char* inet_ntop(int af, const void* src, char* dst, size_t size); * to network format (which is usually some kind of binary format). * @param[in] af Address family, can be either AF_INET (IPv4) or AF_INET6 (IPv6) * @param[in] src Network address string representation - * @param[out] dst Network address structure result, can be either in_addr (IPv4) or in6_addr (IPv6) + * @param[out] dst Network address structure result, can be either in_addr + * (IPv4) or in6_addr (IPv6) * @return * 1 if the address was valid for the specified address family; * 0 if the address wasn't valid ('dst' is untouched in this case); @@ -50,42 +53,39 @@ const char* inet_ntop(int af, const void* src, char* dst, size_t size); int inet_pton(int af, const char* src, void* dst); #endif - /** * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - namespace internal - { - /** - * Extract IPv4 address from sockaddr - * @param[in] sa - input sockaddr - * @return Address in in_addr format - */ - in_addr* sockaddr2in_addr(struct sockaddr *sa); +namespace pcpp { +namespace internal { +/** + * Extract IPv4 address from sockaddr + * @param[in] sa - input sockaddr + * @return Address in in_addr format + */ +in_addr* sockaddr2in_addr(struct sockaddr* sa); - /** - * Extract IPv6 address from sockaddr - * @param[in] sa - input sockaddr - * @return Address in in6_addr format - */ - in6_addr* sockaddr2in6_addr(struct sockaddr *sa); +/** + * Extract IPv6 address from sockaddr + * @param[in] sa - input sockaddr + * @return Address in in6_addr format + */ +in6_addr* sockaddr2in6_addr(struct sockaddr* sa); - /** - * Converts a sockaddr format address to its string representation - * @param[in] sa Address in sockaddr format - * @param[out] resultString String representation of the address - */ - void sockaddr2string(struct sockaddr *sa, char* resultString); +/** + * Converts a sockaddr format address to its string representation + * @param[in] sa Address in sockaddr format + * @param[out] resultString String representation of the address + */ +void sockaddr2string(struct sockaddr* sa, char* resultString); - /** - * Convert a in_addr format address to 32bit representation - * @param[in] inAddr Address in in_addr format - * @return Address in 32bit format - */ - uint32_t in_addr2int(in_addr inAddr); - } // namespace internal +/** + * Convert a in_addr format address to 32bit representation + * @param[in] inAddr Address in in_addr format + * @return Address in 32bit format + */ +uint32_t in_addr2int(in_addr inAddr); +} // namespace internal } // namespace pcpp #endif diff --git a/Common++/header/LRUList.h b/Common++/header/LRUList.h index b11f1389e7..68918bd3f5 100644 --- a/Common++/header/LRUList.h +++ b/Common++/header/LRUList.h @@ -1,8 +1,8 @@ #ifndef PCAPPP_LRU_LIST #define PCAPPP_LRU_LIST -#include #include +#include #if __cplusplus > 199711L || _MSC_VER >= 1800 #include @@ -14,121 +14,117 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - - /** - * @class LRUList - * A template class that implements a LRU cache with limited size. Each time the user puts an element it goes to head of the - * list as the most recently used element (if the element was already in the list it advances to the head of the list). - * The last element in the list is the one least recently used and will be pulled out of the list if it reaches its max size - * and a new element comes in. All actions on this LRU list are O(1) - */ - template - class LRUList - { - public: - - typedef typename std::list::iterator ListIterator; - typedef typename std::map::iterator MapIterator; - - /** - * A c'tor for this class - * @param[in] maxSize The max size this list can go - */ - explicit LRUList(size_t maxSize) - { - m_MaxSize = maxSize; - } - - /** - * Puts an element in the list. This element will be inserted (or advanced if it already exists) to the head of the - * list as the most recently used element. If the list already reached its max size and the element is new this method - * will remove the least recently used element and return a value in deletedValue. Method complexity is O(log(getSize())). - * This is a optimized version of the method T* put(const T&). - * @param[in] element The element to insert or to advance to the head of the list (if already exists) - * @param[out] deletedValue The value of deleted element if a pointer is not NULL. This parameter is optional. - * @return 0 if the list didn't reach its max size, 1 otherwise. In case the list already reached its max size - * and deletedValue is not NULL the value of deleted element is copied into the place the deletedValue points to. - */ - int put(const T& element, T* deletedValue = NULL) - { - m_CacheItemsList.push_front(element); - - // Inserting a new element. If an element with an equivalent key already exists the method returns an iterator to the element that prevented the insertion - std::pair pair = m_CacheItemsMap.insert(std::make_pair(element, m_CacheItemsList.begin())); - if (pair.second == false) // already exists - { - m_CacheItemsList.erase(pair.first->second); - pair.first->second = m_CacheItemsList.begin(); - } - - if (m_CacheItemsMap.size() > m_MaxSize) - { - ListIterator lruIter = m_CacheItemsList.end(); - lruIter--; - - if (deletedValue != NULL) +namespace pcpp { + +/** + * @class LRUList + * A template class that implements a LRU cache with limited size. Each time the + * user puts an element it goes to head of the list as the most recently used + * element (if the element was already in the list it advances to the head of + * the list). The last element in the list is the one least recently used and + * will be pulled out of the list if it reaches its max size and a new element + * comes in. All actions on this LRU list are O(1) + */ +template +class LRUList { + public: + typedef typename std::list::iterator ListIterator; + typedef typename std::map::iterator MapIterator; + + /** + * A c'tor for this class + * @param[in] maxSize The max size this list can go + */ + explicit LRUList(size_t maxSize) { m_MaxSize = maxSize; } + + /** + * Puts an element in the list. This element will be inserted (or advanced if + * it already exists) to the head of the list as the most recently used + * element. If the list already reached its max size and the element is new + * this method will remove the least recently used element and return a value + * in deletedValue. Method complexity is O(log(getSize())). This is a + * optimized version of the method T* put(const T&). + * @param[in] element The element to insert or to advance to the head of the + * list (if already exists) + * @param[out] deletedValue The value of deleted element if a pointer is not + * NULL. This parameter is optional. + * @return 0 if the list didn't reach its max size, 1 otherwise. In case the + * list already reached its max size and deletedValue is not NULL the value of + * deleted element is copied into the place the deletedValue points to. + */ + int put(const T& element, T* deletedValue = NULL) { + m_CacheItemsList.push_front(element); + + // Inserting a new element. If an element with an equivalent key already + // exists the method returns an iterator to the element that prevented the + // insertion + std::pair pair = m_CacheItemsMap.insert( + std::make_pair(element, m_CacheItemsList.begin())); + if (pair.second == false) // already exists + { + m_CacheItemsList.erase(pair.first->second); + pair.first->second = m_CacheItemsList.begin(); + } + + if (m_CacheItemsMap.size() > m_MaxSize) { + ListIterator lruIter = m_CacheItemsList.end(); + lruIter--; + + if (deletedValue != NULL) #if __cplusplus > 199711L || _MSC_VER >= 1800 - *deletedValue = std::move(*lruIter); + *deletedValue = std::move(*lruIter); #else - *deletedValue = *lruIter; + *deletedValue = *lruIter; #endif - m_CacheItemsMap.erase(*lruIter); - m_CacheItemsList.erase(lruIter); - return 1; - } - - return 0; - } - - /** - * Get the most recently used element (the one at the beginning of the list) - * @return The most recently used element - */ - const T& getMRUElement() const - { - return m_CacheItemsList.front(); - } - - /** - * Get the least recently used element (the one at the end of the list) - * @return The least recently used element - */ - const T& getLRUElement() const - { - return m_CacheItemsList.back(); - } - - /** - * Erase an element from the list. If element isn't found in the list nothing happens - * @param[in] element The element to erase - */ - void eraseElement(const T& element) - { - MapIterator iter = m_CacheItemsMap.find(element); - if (iter == m_CacheItemsMap.end()) - return; - - m_CacheItemsList.erase(iter->second); - m_CacheItemsMap.erase(iter); - } - - /** - * @return The max size of this list as determined in the c'tor - */ - size_t getMaxSize() const { return m_MaxSize; } - - /** - * @return The number of elements currently in this list - */ - size_t getSize() const { return m_CacheItemsMap.size(); } - - private: - std::list m_CacheItemsList; - std::map m_CacheItemsMap; - size_t m_MaxSize; - }; + m_CacheItemsMap.erase(*lruIter); + m_CacheItemsList.erase(lruIter); + return 1; + } + + return 0; + } + + /** + * Get the most recently used element (the one at the beginning of the list) + * @return The most recently used element + */ + const T& getMRUElement() const { return m_CacheItemsList.front(); } + + /** + * Get the least recently used element (the one at the end of the list) + * @return The least recently used element + */ + const T& getLRUElement() const { return m_CacheItemsList.back(); } + + /** + * Erase an element from the list. If element isn't found in the list nothing + * happens + * @param[in] element The element to erase + */ + void eraseElement(const T& element) { + MapIterator iter = m_CacheItemsMap.find(element); + if (iter == m_CacheItemsMap.end()) + return; + + m_CacheItemsList.erase(iter->second); + m_CacheItemsMap.erase(iter); + } + + /** + * @return The max size of this list as determined in the c'tor + */ + size_t getMaxSize() const { return m_MaxSize; } + + /** + * @return The number of elements currently in this list + */ + size_t getSize() const { return m_CacheItemsMap.size(); } + + private: + std::list m_CacheItemsList; + std::map m_CacheItemsMap; + size_t m_MaxSize; +}; } // namespace pcpp diff --git a/Common++/header/Logger.h b/Common++/header/Logger.h index 282f798813..90bb968d4f 100644 --- a/Common++/header/Logger.h +++ b/Common++/header/Logger.h @@ -1,11 +1,11 @@ #ifndef PCAPPP_LOGGER #define PCAPPP_LOGGER -#include +#include #include #include -#include #include +#include #ifndef LOG_MODULE #define LOG_MODULE UndefinedLogModule @@ -18,25 +18,27 @@ #define PCAPPP_FILENAME __FILE__ #endif -#define PCPP_LOG(level, message) do \ - { \ - std::ostringstream* sstream = pcpp::Logger::getInstance().internalCreateLogStream(); \ - (*sstream) << message; \ - pcpp::Logger::getInstance().internalPrintLogMessage(sstream, level, PCAPPP_FILENAME, __FUNCTION__, __LINE__); \ - } while(0) +#define PCPP_LOG(level, message) \ + do { \ + std::ostringstream* sstream = \ + pcpp::Logger::getInstance().internalCreateLogStream(); \ + (*sstream) << message; \ + pcpp::Logger::getInstance().internalPrintLogMessage( \ + sstream, level, PCAPPP_FILENAME, __FUNCTION__, __LINE__); \ + } while (0) -#define PCPP_LOG_DEBUG(message) do \ - { \ - if (pcpp::Logger::getInstance().logsEnabled() && pcpp::Logger::getInstance().isDebugEnabled(LOG_MODULE)) \ - { \ - PCPP_LOG(pcpp::Logger::Debug, message); \ - } \ - } while(0) +#define PCPP_LOG_DEBUG(message) \ + do { \ + if (pcpp::Logger::getInstance().logsEnabled() && \ + pcpp::Logger::getInstance().isDebugEnabled(LOG_MODULE)) { \ + PCPP_LOG(pcpp::Logger::Debug, message); \ + } \ + } while (0) -#define PCPP_LOG_ERROR(message) do \ - { \ - PCPP_LOG(pcpp::Logger::Error, message); \ - } while (0) +#define PCPP_LOG_ERROR(message) \ + do { \ + PCPP_LOG(pcpp::Logger::Error, message); \ + } while (0) /// @file @@ -44,219 +46,239 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ +namespace pcpp { - /** - * An enum representing all PcapPlusPlus modules - */ - enum LogModule - { - UndefinedLogModule, - CommonLogModuleIpUtils, ///< IP Utils module (Common++) - CommonLogModuleTablePrinter, ///< Table printer module (Common++) - CommonLogModuleGenericUtils, ///< Generic Utils (Common++) - PacketLogModuleRawPacket, ///< RawPacket module (Packet++) - PacketLogModulePacket, ///< Packet module (Packet++) - PacketLogModuleLayer, ///< Layer module (Packet++) - PacketLogModuleArpLayer, ///< ArpLayer module (Packet++) - PacketLogModuleEthLayer, ///< EthLayer module (Packet++) - PacketLogModuleIPv4Layer, ///< IPv4Layer module (Packet++) - PacketLogModuleIPv6Layer, ///< IPv6Layer module (Packet++) - PacketLogModulePayloadLayer, ///< PayloadLayer module (Packet++) - PacketLogModuleTcpLayer, ///< TcpLayer module (Packet++) - PacketLogModuleUdpLayer, ///< UdpLayer module (Packet++) - PacketLogModuleVlanLayer, ///< VlanLayer module (Packet++) - PacketLogModuleHttpLayer, ///< HttpLayer module (Packet++) - PacketLogModulePPPoELayer, ///< PPPoELayer module (Packet++) - PacketLogModuleDnsLayer, ///< DnsLayer module (Packet++) - PacketLogModuleMplsLayer, ///< MplsLayer module (Packet++) - PacketLogModuleIcmpLayer, ///< IcmpLayer module (Packet++) - PacketLogModuleIcmpV6Layer, ///< IcmpV6Layer module (Packet++) - PacketLogModuleGreLayer, ///< GreLayer module (Packet++) - PacketLogModuleSSLLayer, ///< SSLLayer module (Packet++) - PacketLogModuleSllLayer, ///< SllLayer module (Packet++) - PacketLogModuleNflogLayer, ///< NflogLayer module (Packet++) - PacketLogModuleDhcpLayer, ///< DhcpLayer module (Packet++) - PacketLogModuleDhcpV6Layer, ///< DhcpV6Layer module (Packet++) - PacketLogModuleIgmpLayer, ///< IgmpLayer module (Packet++) - PacketLogModuleSipLayer, ///< SipLayer module (Packet++) - PacketLogModuleSdpLayer, ///< SdpLayer module (Packet++) - PacketLogModuleRadiusLayer, ///< RadiusLayer module (Packet++) - PacketLogModuleGtpLayer, ///< GtpLayer module (Packet++) - PacketLogModuleBgpLayer, ///< GtpLayer module (Packet++) - PacketLogModuleSSHLayer, ///< SSHLayer module (Packet++) - PacketLogModuleVrrpLayer, ///< Vrrp Record module (Packet++) - PacketLogModuleTcpReassembly, ///< TcpReassembly module (Packet++) - PacketLogModuleIPReassembly, ///< IPReassembly module (Packet++) - PacketLogModuleIPSecLayer, ///< IPSecLayers module (Packet++) - PacketLogModuleNtpLayer, ///< NtpLayer module (Packet++) - PacketLogModuleTelnetLayer, ///< TelnetLayer module (Packet++) - PacketLogModuleStpLayer, ///< StpLayer module (Packet++) - PacketLogModuleLLCLayer, ///< LLCLayer module (Packet++) - PacketLogModuleSingleCommandTextProtocolLayer, ///< SingleCommandTextProtocol module (Packet++) - PacketLogModuleNdpLayer, ///< NdpLayer module (Packet++) - PacketLogModuleFtpLayer, ///< FtpLayer module (Packet++) - PacketLogModuleSomeIpLayer, ///< SomeIpLayer module (Packet++) - PacketLogModuleSomeIpSdLayer, ///< SomeIpSdLayer module (Packet++) - PacketLogModuleWakeOnLanLayer, ///< WakeOnLanLayer module (Packet++) - PcapLogModuleWinPcapLiveDevice, ///< WinPcapLiveDevice module (Pcap++) - PcapLogModuleRemoteDevice, ///< WinPcapRemoteDevice module (Pcap++) - PcapLogModuleLiveDevice, ///< PcapLiveDevice module (Pcap++) - PcapLogModuleFileDevice, ///< FileDevice module (Pcap++) - PcapLogModulePfRingDevice, ///< PfRingDevice module (Pcap++) - PcapLogModuleMBufRawPacket, ///< MBufRawPacket module (Pcap++) - PcapLogModuleDpdkDevice, ///< DpdkDevice module (Pcap++) - PcapLogModuleKniDevice, ///< KniDevice module (Pcap++) - PcapLogModuleXdpDevice, ///< XdpDevice module (Pcap++) - NetworkUtils, ///< NetworkUtils module (Pcap++) - NumOfLogModules - }; +/** + * An enum representing all PcapPlusPlus modules + */ +enum LogModule { + UndefinedLogModule, + CommonLogModuleIpUtils, ///< IP Utils module (Common++) + CommonLogModuleTablePrinter, ///< Table printer module (Common++) + CommonLogModuleGenericUtils, ///< Generic Utils (Common++) + PacketLogModuleRawPacket, ///< RawPacket module (Packet++) + PacketLogModulePacket, ///< Packet module (Packet++) + PacketLogModuleLayer, ///< Layer module (Packet++) + PacketLogModuleArpLayer, ///< ArpLayer module (Packet++) + PacketLogModuleEthLayer, ///< EthLayer module (Packet++) + PacketLogModuleIPv4Layer, ///< IPv4Layer module (Packet++) + PacketLogModuleIPv6Layer, ///< IPv6Layer module (Packet++) + PacketLogModulePayloadLayer, ///< PayloadLayer module (Packet++) + PacketLogModuleTcpLayer, ///< TcpLayer module (Packet++) + PacketLogModuleUdpLayer, ///< UdpLayer module (Packet++) + PacketLogModuleVlanLayer, ///< VlanLayer module (Packet++) + PacketLogModuleHttpLayer, ///< HttpLayer module (Packet++) + PacketLogModulePPPoELayer, ///< PPPoELayer module (Packet++) + PacketLogModuleDnsLayer, ///< DnsLayer module (Packet++) + PacketLogModuleMplsLayer, ///< MplsLayer module (Packet++) + PacketLogModuleIcmpLayer, ///< IcmpLayer module (Packet++) + PacketLogModuleIcmpV6Layer, ///< IcmpV6Layer module (Packet++) + PacketLogModuleGreLayer, ///< GreLayer module (Packet++) + PacketLogModuleSSLLayer, ///< SSLLayer module (Packet++) + PacketLogModuleSllLayer, ///< SllLayer module (Packet++) + PacketLogModuleNflogLayer, ///< NflogLayer module (Packet++) + PacketLogModuleDhcpLayer, ///< DhcpLayer module (Packet++) + PacketLogModuleDhcpV6Layer, ///< DhcpV6Layer module (Packet++) + PacketLogModuleIgmpLayer, ///< IgmpLayer module (Packet++) + PacketLogModuleSipLayer, ///< SipLayer module (Packet++) + PacketLogModuleSdpLayer, ///< SdpLayer module (Packet++) + PacketLogModuleRadiusLayer, ///< RadiusLayer module (Packet++) + PacketLogModuleGtpLayer, ///< GtpLayer module (Packet++) + PacketLogModuleBgpLayer, ///< GtpLayer module (Packet++) + PacketLogModuleSSHLayer, ///< SSHLayer module (Packet++) + PacketLogModuleVrrpLayer, ///< Vrrp Record module (Packet++) + PacketLogModuleTcpReassembly, ///< TcpReassembly module (Packet++) + PacketLogModuleIPReassembly, ///< IPReassembly module (Packet++) + PacketLogModuleIPSecLayer, ///< IPSecLayers module (Packet++) + PacketLogModuleNtpLayer, ///< NtpLayer module (Packet++) + PacketLogModuleTelnetLayer, ///< TelnetLayer module (Packet++) + PacketLogModuleStpLayer, ///< StpLayer module (Packet++) + PacketLogModuleLLCLayer, ///< LLCLayer module (Packet++) + PacketLogModuleSingleCommandTextProtocolLayer, ///< SingleCommandTextProtocol + ///< module (Packet++) + PacketLogModuleNdpLayer, ///< NdpLayer module (Packet++) + PacketLogModuleFtpLayer, ///< FtpLayer module (Packet++) + PacketLogModuleSomeIpLayer, ///< SomeIpLayer module (Packet++) + PacketLogModuleSomeIpSdLayer, ///< SomeIpSdLayer module (Packet++) + PacketLogModuleWakeOnLanLayer, ///< WakeOnLanLayer module (Packet++) + PcapLogModuleWinPcapLiveDevice, ///< WinPcapLiveDevice module (Pcap++) + PcapLogModuleRemoteDevice, ///< WinPcapRemoteDevice module (Pcap++) + PcapLogModuleLiveDevice, ///< PcapLiveDevice module (Pcap++) + PcapLogModuleFileDevice, ///< FileDevice module (Pcap++) + PcapLogModulePfRingDevice, ///< PfRingDevice module (Pcap++) + PcapLogModuleMBufRawPacket, ///< MBufRawPacket module (Pcap++) + PcapLogModuleDpdkDevice, ///< DpdkDevice module (Pcap++) + PcapLogModuleKniDevice, ///< KniDevice module (Pcap++) + PcapLogModuleXdpDevice, ///< XdpDevice module (Pcap++) + NetworkUtils, ///< NetworkUtils module (Pcap++) + NumOfLogModules +}; + +/** + * @class Logger + * PcapPlusPlus logger manager. + * PcapPlusPlus uses this logger to output both error and debug logs. + * There are currently 3 log levels: Logger#Error, Logger#Info and Logger#Debug. + * + * PcapPlusPlus is divided into modules (described in #LogModule enum). The user + * can set the log level got each module or to all modules at once. The default + * is Logger#Info which outputs only error messages. Changing log level for + * modules can be done dynamically while the application is running. + * + * The logger also exposes a method to retrieve the last error log message. + * + * Logs are printed to console by default in a certain format. The user can set + * a different print function to change the format or to print to other media + * (such as files, etc.). + * + * PcapPlusPlus logger is a singleton which can be reached from anywhere in the + * code. + * + * Note: Logger#Info level logs are currently only used in DPDK devices to set + * DPDK log level to RTE_LOG_NOTICE. + */ +class Logger { + public: + /** + * An enum representing the log level. Currently 3 log levels are supported: + * Error, Info and Debug. Info is the default log level + */ + enum LogLevel { + Error, ///< Error log level + Info, ///< Info log level + Debug ///< Debug log level + }; - /** - * @class Logger - * PcapPlusPlus logger manager. - * PcapPlusPlus uses this logger to output both error and debug logs. - * There are currently 3 log levels: Logger#Error, Logger#Info and Logger#Debug. - * - * PcapPlusPlus is divided into modules (described in #LogModule enum). The user can set the log level got each module or to all modules at once. - * The default is Logger#Info which outputs only error messages. Changing log level for modules can be done dynamically while the application is running. - * - * The logger also exposes a method to retrieve the last error log message. - * - * Logs are printed to console by default in a certain format. The user can set a different print function to change the format or to print to - * other media (such as files, etc.). - * - * PcapPlusPlus logger is a singleton which can be reached from anywhere in the code. - * - * Note: Logger#Info level logs are currently only used in DPDK devices to set DPDK log level to RTE_LOG_NOTICE. - */ - class Logger - { - public: - /** - * An enum representing the log level. Currently 3 log levels are supported: Error, Info and Debug. Info is the default log level - */ - enum LogLevel - { - Error, ///< Error log level - Info, ///< Info log level - Debug ///< Debug log level - }; + /** + * @typedef LogPrinter + * Log printer callback. Used for printing the logs in a custom way. + * @param[in] logLevel The log level for this log message + * @param[in] logMessage The log message + * @param[in] file The source file in PcapPlusPlus code the log message is + * coming from + * @param[in] method The method in PcapPlusPlus code the log message is coming + * from + * @param[in] line The line in PcapPlusPlus code the log message is coming + * from + */ + typedef void (*LogPrinter)(LogLevel logLevel, const std::string& logMessage, + const std::string& file, const std::string& method, + const int line); - /** - * @typedef LogPrinter - * Log printer callback. Used for printing the logs in a custom way. - * @param[in] logLevel The log level for this log message - * @param[in] logMessage The log message - * @param[in] file The source file in PcapPlusPlus code the log message is coming from - * @param[in] method The method in PcapPlusPlus code the log message is coming from - * @param[in] line The line in PcapPlusPlus code the log message is coming from - */ - typedef void (*LogPrinter)(LogLevel logLevel, const std::string& logMessage, const std::string& file, const std::string& method, const int line); + /** + * A static method for converting the log level enum to a string. + * @param[in] logLevel A log level enum + * @return The log level as a string + */ + static std::string logLevelAsString(LogLevel logLevel); - /** - * A static method for converting the log level enum to a string. - * @param[in] logLevel A log level enum - * @return The log level as a string - */ - static std::string logLevelAsString(LogLevel logLevel); + /** + * Get the log level for a certain module + * @param[in] module PcapPlusPlus module + * @return The log level set for this module + */ + LogLevel getLogLevel(LogModule module) { return m_LogModulesArray[module]; } - /** - * Get the log level for a certain module - * @param[in] module PcapPlusPlus module - * @return The log level set for this module - */ - LogLevel getLogLevel(LogModule module) { return m_LogModulesArray[module]; } + /** + * Set the log level for a certain PcapPlusPlus module + * @param[in] module PcapPlusPlus module + * @param[in] level The log level to set the module to + */ + void setLogLevel(LogModule module, LogLevel level) { + m_LogModulesArray[module] = level; + } - /** - * Set the log level for a certain PcapPlusPlus module - * @param[in] module PcapPlusPlus module - * @param[in] level The log level to set the module to - */ - void setLogLevel(LogModule module, LogLevel level) { m_LogModulesArray[module] = level; } + /** + * Check whether a certain module is set to debug log level + * @param[in] module PcapPlusPlus module + * @return True if this module log level is "debug". False otherwise + */ + bool isDebugEnabled(LogModule module) const { + return m_LogModulesArray[module] == Debug; + } - /** - * Check whether a certain module is set to debug log level - * @param[in] module PcapPlusPlus module - * @return True if this module log level is "debug". False otherwise - */ - bool isDebugEnabled(LogModule module) const { return m_LogModulesArray[module] == Debug; } + /** + * Set all PcapPlusPlus modules to a certain log level + * @param[in] level The log level to set all modules to + */ + void setAllModulesToLogLevel(LogLevel level) { + for (int i = 1; i < NumOfLogModules; i++) + m_LogModulesArray[i] = level; + } - /** - * Set all PcapPlusPlus modules to a certain log level - * @param[in] level The log level to set all modules to - */ - void setAllModulesToLogLevel(LogLevel level) { for (int i=1; i + Logger& operator<<(const T& msg) { + (*m_LogStream) << msg; + return *this; + } - template - Logger& operator<<(const T& msg) - { - (*m_LogStream) << msg; - return *this; - } + std::ostringstream* internalCreateLogStream(); - std::ostringstream * internalCreateLogStream(); + /** + * An internal method to print log messages. Shouldn't be used externally. + */ + void internalPrintLogMessage(std::ostringstream* logStream, + Logger::LogLevel logLevel, const char* file, + const char* method, int line); - /** - * An internal method to print log messages. Shouldn't be used externally. - */ - void internalPrintLogMessage(std::ostringstream* logStream, Logger::LogLevel logLevel, const char* file, const char* method, int line); + /** + * Get access to Logger singleton + * @todo: make this singleton thread-safe/ + * @return a pointer to the Logger singleton + **/ + static Logger& getInstance() { + static Logger instance; + return instance; + } - /** - * Get access to Logger singleton - * @todo: make this singleton thread-safe/ - * @return a pointer to the Logger singleton - **/ - static Logger& getInstance() - { - static Logger instance; - return instance; - } - private: - bool m_LogsEnabled; - Logger::LogLevel m_LogModulesArray[NumOfLogModules]; - LogPrinter m_LogPrinter; - std::string m_LastError; - std::ostringstream* m_LogStream; + private: + bool m_LogsEnabled; + Logger::LogLevel m_LogModulesArray[NumOfLogModules]; + LogPrinter m_LogPrinter; + std::string m_LastError; + std::ostringstream* m_LogStream; - // private c'tor - this class is a singleton - Logger(); + // private c'tor - this class is a singleton + Logger(); - static void defaultLogPrinter(LogLevel logLevel, const std::string& logMessage, const std::string& file, const std::string& method, const int line); - }; + static void defaultLogPrinter(LogLevel logLevel, + const std::string& logMessage, + const std::string& file, + const std::string& method, const int line); +}; } // namespace pcpp #endif /* PCAPPP_LOGGER */ diff --git a/Common++/header/MacAddress.h b/Common++/header/MacAddress.h index 87d398a76d..044b8edc60 100644 --- a/Common++/header/MacAddress.h +++ b/Common++/header/MacAddress.h @@ -6,8 +6,8 @@ #include #if __cplusplus > 199711L || _MSC_VER >= 1800 -#include #include +#include #include #include #endif @@ -18,172 +18,188 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - - /** - * @class MacAddress - * Represents L2 MAC addresses. Can be constructed from string or a series of 6 byte octets - */ - class MacAddress - { - public: - /** - * Default constructor for this class. - * Initializes object to me MacAddress::Zero - */ - MacAddress() : m_IsValid(true) { memset(m_Address, 0, sizeof(m_Address)); } - - /** - * A constructor that creates an instance of the class out of a byte array. The byte array length must be equal or greater to 6 - * (as MAC address is 6-byte long) - * @todo there is no verification array length >= 6. If this is not the case, address will read uninitialized memory - * @param[in] addr A pointer to the byte array containing 6 bytes representing the MAC address - */ - MacAddress(const uint8_t* addr) : m_IsValid(true) { memcpy(m_Address, addr, sizeof(m_Address)); } - - /** - * A constructor that creates an instance of the class out of a (char*) string. - * If the string doesn't represent a valid MAC address, instance will be invalid, meaning isValid() will return false - * @param[in] addr A pointer to the (char*) string - */ - MacAddress(const char* addr) { init(addr); } - - /** - * A constructor that creates an instance of the class out of a std::string. - * If the string doesn't represent a valid MAC address, instance will be invalid, meaning isValid() will return false - * @param[in] addr A pointer to the string - */ - MacAddress(const std::string& addr) { init(addr.c_str()); } - - /** - * A constructor that creates an instance of 6 bytes representing the MAC address - * @param[in] firstOctest Represent the first octet in the address - * @param[in] secondOctet Represent the second octet in the address - * @param[in] thirdOctet Represent the third octet in the address - * @param[in] fourthOctet Represent the fourth octet in the address - * @param[in] fifthOctet Represent the fifth octet in the address - * @param[in] sixthOctet Represent the sixth octet in the address - */ - inline MacAddress(uint8_t firstOctest, uint8_t secondOctet, uint8_t thirdOctet, uint8_t fourthOctet, uint8_t fifthOctet, uint8_t sixthOctet); +namespace pcpp { + +/** + * @class MacAddress + * Represents L2 MAC addresses. Can be constructed from string or a series of 6 + * byte octets + */ +class MacAddress { + public: + /** + * Default constructor for this class. + * Initializes object to me MacAddress::Zero + */ + MacAddress() : m_IsValid(true) { memset(m_Address, 0, sizeof(m_Address)); } + + /** + * A constructor that creates an instance of the class out of a byte array. + * The byte array length must be equal or greater to 6 (as MAC address is + * 6-byte long) + * @todo there is no verification array length >= 6. If this is not the case, + * address will read uninitialized memory + * @param[in] addr A pointer to the byte array containing 6 bytes representing + * the MAC address + */ + MacAddress(const uint8_t* addr) : m_IsValid(true) { + memcpy(m_Address, addr, sizeof(m_Address)); + } + + /** + * A constructor that creates an instance of the class out of a (char*) + * string. If the string doesn't represent a valid MAC address, instance will + * be invalid, meaning isValid() will return false + * @param[in] addr A pointer to the (char*) string + */ + MacAddress(const char* addr) { init(addr); } + + /** + * A constructor that creates an instance of the class out of a std::string. + * If the string doesn't represent a valid MAC address, instance will be + * invalid, meaning isValid() will return false + * @param[in] addr A pointer to the string + */ + MacAddress(const std::string& addr) { init(addr.c_str()); } + + /** + * A constructor that creates an instance of 6 bytes representing the MAC + * address + * @param[in] firstOctest Represent the first octet in the address + * @param[in] secondOctet Represent the second octet in the address + * @param[in] thirdOctet Represent the third octet in the address + * @param[in] fourthOctet Represent the fourth octet in the address + * @param[in] fifthOctet Represent the fifth octet in the address + * @param[in] sixthOctet Represent the sixth octet in the address + */ + inline MacAddress(uint8_t firstOctest, uint8_t secondOctet, + uint8_t thirdOctet, uint8_t fourthOctet, uint8_t fifthOctet, + uint8_t sixthOctet); #if __cplusplus > 199711L || _MSC_VER >= 1800 - /** - * A constructor that creates an instance out of the initializer list. The length of the list must be equal to 6 (as MAC address is 6-byte long) - * @param[in] addr An initializer list containing the values of type uint8_t representing the MAC address - */ - MacAddress(std::initializer_list octets) : m_IsValid { octets.size() == sizeof(m_Address) } - { - if(m_IsValid) - { - #if _MSC_VER >= 1800 - std::copy(octets.begin(), octets.end(), stdext::checked_array_iterator(m_Address, 6)); - #else - std::copy(octets.begin(), octets.end(), std::begin(m_Address)); - #endif - } - else - memset(m_Address, 0, sizeof(m_Address)); - } + /** + * A constructor that creates an instance out of the initializer list. The + * length of the list must be equal to 6 (as MAC address is 6-byte long) + * @param[in] addr An initializer list containing the values of type uint8_t + * representing the MAC address + */ + MacAddress(std::initializer_list octets) + : m_IsValid{octets.size() == sizeof(m_Address)} { + if (m_IsValid) { +#if _MSC_VER >= 1800 + std::copy(octets.begin(), octets.end(), + stdext::checked_array_iterator(m_Address, 6)); +#else + std::copy(octets.begin(), octets.end(), std::begin(m_Address)); +#endif + } else + memset(m_Address, 0, sizeof(m_Address)); + } #endif - /** - * Overload of the comparison operator - * @param[in] other The object to compare with - * @return True if addresses are equal, false otherwise - */ - bool operator==(const MacAddress& other) const { return memcmp(m_Address, other.m_Address, sizeof(m_Address)) == 0; } - - /** - * Overload of the not-equal operator - * @param[in] other The object to compare with - * @return True if addresses are not equal, false otherwise - */ - bool operator!=(const MacAddress& other) const { return !operator==(other); } + /** + * Overload of the comparison operator + * @param[in] other The object to compare with + * @return True if addresses are equal, false otherwise + */ + bool operator==(const MacAddress& other) const { + return memcmp(m_Address, other.m_Address, sizeof(m_Address)) == 0; + } + + /** + * Overload of the not-equal operator + * @param[in] other The object to compare with + * @return True if addresses are not equal, false otherwise + */ + bool operator!=(const MacAddress& other) const { return !operator==(other); } #if __cplusplus > 199711L || _MSC_VER >= 1800 - /** - * Overload of the assignment operator - */ - MacAddress& operator=(std::initializer_list octets) - { - m_IsValid = (octets.size() == sizeof m_Address); - if(m_IsValid) - { - #if _MSC_VER >= 1800 - std::copy(octets.begin(), octets.end(), stdext::checked_array_iterator(m_Address, sizeof(m_Address))); - #else - std::copy(octets.begin(), octets.end(), std::begin(m_Address)); - #endif - } - return *this; - } + /** + * Overload of the assignment operator + */ + MacAddress& operator=(std::initializer_list octets) { + m_IsValid = (octets.size() == sizeof m_Address); + if (m_IsValid) { +#if _MSC_VER >= 1800 + std::copy(octets.begin(), octets.end(), + stdext::checked_array_iterator(m_Address, + sizeof(m_Address))); +#else + std::copy(octets.begin(), octets.end(), std::begin(m_Address)); +#endif + } + return *this; + } #endif - /** - * Returns the pointer to raw data - * @return The pointer to raw data - */ - const uint8_t* getRawData() const { return m_Address; } - - /** - * Get an indication whether the MAC address is valid. An address can be invalid if it was constructed from illegal input, for example: - * invalid string - * @return True if the address is valid, false otherwise - */ - bool isValid() const { return m_IsValid; } - - /** - * Returns a std::string representation of the address - * @return A string representation of the address - */ - std::string toString() const; - - /** - * Allocates a byte array of length 6 and copies address value into it. Array deallocation is user responsibility - * @param[in] arr A pointer to where array will be allocated - */ - void copyTo(uint8_t** arr) const - { - *arr = new uint8_t[sizeof(m_Address)]; - memcpy(*arr, m_Address, sizeof(m_Address)); - } - - /** - * Gets a pointer to an already allocated byte array and copies the address value to it. - * This method assumes array allocated size is at least 6 (the size of a MAC address) - * @param[in] arr A pointer to the array which address will be copied to - */ - void copyTo(uint8_t* arr) const { memcpy(arr, m_Address, sizeof(m_Address)); } - - /** - * A static value representing a zero value of MAC address, meaning address of value "00:00:00:00:00:00" - */ - static MacAddress Zero; - - private: - uint8_t m_Address[6]; - bool m_IsValid; - void init(const char* addr); - }; - - MacAddress::MacAddress(uint8_t firstOctest, uint8_t secondOctet, uint8_t thirdOctet, uint8_t fourthOctet, uint8_t fifthOctet, uint8_t sixthOctet) - : m_IsValid(true) - { - m_Address[0] = firstOctest; - m_Address[1] = secondOctet; - m_Address[2] = thirdOctet; - m_Address[3] = fourthOctet; - m_Address[4] = fifthOctet; - m_Address[5] = sixthOctet; - } + /** + * Returns the pointer to raw data + * @return The pointer to raw data + */ + const uint8_t* getRawData() const { return m_Address; } + + /** + * Get an indication whether the MAC address is valid. An address can be + * invalid if it was constructed from illegal input, for example: invalid + * string + * @return True if the address is valid, false otherwise + */ + bool isValid() const { return m_IsValid; } + + /** + * Returns a std::string representation of the address + * @return A string representation of the address + */ + std::string toString() const; + + /** + * Allocates a byte array of length 6 and copies address value into it. Array + * deallocation is user responsibility + * @param[in] arr A pointer to where array will be allocated + */ + void copyTo(uint8_t** arr) const { + *arr = new uint8_t[sizeof(m_Address)]; + memcpy(*arr, m_Address, sizeof(m_Address)); + } + + /** + * Gets a pointer to an already allocated byte array and copies the address + * value to it. This method assumes array allocated size is at least 6 (the + * size of a MAC address) + * @param[in] arr A pointer to the array which address will be copied to + */ + void copyTo(uint8_t* arr) const { memcpy(arr, m_Address, sizeof(m_Address)); } + + /** + * A static value representing a zero value of MAC address, meaning address of + * value "00:00:00:00:00:00" + */ + static MacAddress Zero; + + private: + uint8_t m_Address[6]; + bool m_IsValid; + void init(const char* addr); +}; + +MacAddress::MacAddress(uint8_t firstOctest, uint8_t secondOctet, + uint8_t thirdOctet, uint8_t fourthOctet, + uint8_t fifthOctet, uint8_t sixthOctet) + : m_IsValid(true) { + m_Address[0] = firstOctest; + m_Address[1] = secondOctet; + m_Address[2] = thirdOctet; + m_Address[3] = fourthOctet; + m_Address[4] = fifthOctet; + m_Address[5] = sixthOctet; +} } // namespace pcpp -inline std::ostream& operator<<(std::ostream& os, const pcpp::MacAddress& macAddress) -{ - os << macAddress.toString(); - return os; +inline std::ostream& operator<<(std::ostream& os, + const pcpp::MacAddress& macAddress) { + os << macAddress.toString(); + return os; } #endif /* PCAPPP_MACADDRESS */ diff --git a/Common++/header/OUILookup.h b/Common++/header/OUILookup.h index cdb00a2371..bf927c14a4 100644 --- a/Common++/header/OUILookup.h +++ b/Common++/header/OUILookup.h @@ -9,61 +9,62 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - /** - * @class OUILookup - * Provides vendor name matching functionality from MAC addresses. It uses an internal database to define name of the vendor. - * The class itself should be initialized by using initOUIDatabaseFromJson() otherwise all requests will return "Unknown" as vendor. - * The class itself currently does not support on-fly modifying the database but anyone who wants to add/modify/remove entries, - * should modify 3rdParty/OUILookup/PCPP_OUIDatabase.json file and call to initOUIDatabaseFromJson() function to renew the internal data. - */ - class OUILookup - { - private: - /** - * MAC addresses with mask values. For example for a MAC address "XX:XX:XX:XX:X0:00/36" the first element will - * be 36, and the second element will be unsigned integer equivalent of "XX:XX:XX:XX:X0:00" and vendor name. - */ - struct MaskedFilter - { - int mask; - std::unordered_map vendorMap; - }; - - /// Vendors for MAC addresses and mask filters if exists - struct VendorData - { - std::string vendorName; - std::vector maskedFilter; - }; +namespace pcpp { +/** + * @class OUILookup + * Provides vendor name matching functionality from MAC addresses. It uses an + * internal database to define name of the vendor. The class itself should be + * initialized by using initOUIDatabaseFromJson() otherwise all requests will + * return "Unknown" as vendor. The class itself currently does not support + * on-fly modifying the database but anyone who wants to add/modify/remove + * entries, should modify 3rdParty/OUILookup/PCPP_OUIDatabase.json file and call + * to initOUIDatabaseFromJson() function to renew the internal data. + */ +class OUILookup { + private: + /** + * MAC addresses with mask values. For example for a MAC address + * "XX:XX:XX:XX:X0:00/36" the first element will be 36, and the second element + * will be unsigned integer equivalent of "XX:XX:XX:XX:X0:00" and vendor name. + */ + struct MaskedFilter { + int mask; + std::unordered_map vendorMap; + }; - /** - * MAC addresses with only first three octets. The first element is unsigned integer equivalent of "XX:XX:XX" - * formatted MAC address - */ - typedef std::unordered_map OUIVendorMap; + /// Vendors for MAC addresses and mask filters if exists + struct VendorData { + std::string vendorName; + std::vector maskedFilter; + }; - /// Internal vendor list for MAC addresses - OUIVendorMap vendorMap; + /** + * MAC addresses with only first three octets. The first element is unsigned + * integer equivalent of "XX:XX:XX" formatted MAC address + */ + typedef std::unordered_map OUIVendorMap; - template - int64_t internalParser(T &jsonData); + /// Internal vendor list for MAC addresses + OUIVendorMap vendorMap; - public: + template + int64_t internalParser(T& jsonData); - /** - * Initialise internal OUI database from a JSON file - * @param[in] path Path to OUI database. The database itself is located at 3rdParty/OUILookup/PCPP_OUIDatabase.json - * @return Returns the number of total vendors, negative on errors - */ - int64_t initOUIDatabaseFromJson(const std::string &path = ""); + public: + /** + * Initialise internal OUI database from a JSON file + * @param[in] path Path to OUI database. The database itself is located at + * 3rdParty/OUILookup/PCPP_OUIDatabase.json + * @return Returns the number of total vendors, negative on errors + */ + int64_t initOUIDatabaseFromJson(const std::string& path = ""); - /** - * Returns the vendor of the MAC address. OUI database should be initialized with initOUIDatabaseFromJson() - * @param[in] addr MAC address to search - * @return Vendor name - */ - std::string getVendorName(const pcpp::MacAddress &addr); - }; + /** + * Returns the vendor of the MAC address. OUI database should be initialized + * with initOUIDatabaseFromJson() + * @param[in] addr MAC address to search + * @return Vendor name + */ + std::string getVendorName(const pcpp::MacAddress& addr); +}; } // namespace pcpp diff --git a/Common++/header/PcapPlusPlusVersion.h b/Common++/header/PcapPlusPlusVersion.h index 11142bbb3e..1f6263393e 100644 --- a/Common++/header/PcapPlusPlusVersion.h +++ b/Common++/header/PcapPlusPlusVersion.h @@ -9,46 +9,53 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - #define PCAPPLUSPLUS_VERSION "23.09+" - #define PCAPPLUSPLUS_VERSION_OFFICIAL "non-official release" - - #define PCAPPLUSPLUS_VERSION_FULL "v" PCAPPLUSPLUS_VERSION " (" PCAPPLUSPLUS_VERSION_OFFICIAL ")" - - /** - * @return PcapPlusPlus current version, e.g: 23.09. Notice that for non-official releases (which were pulled from GitHub) the version will end with a '+'. - * For example: '23.09+' means non-official release but '23.09' means official release - */ - inline std::string getPcapPlusPlusVersion() { return PCAPPLUSPLUS_VERSION; } - - /** - * @return PcapPlusPlus long version string which includes the version and info whether it's an official or non-official release. For example: "v23.09+ (non-official release)" - * or "v23.09 (official release)" - */ - inline std::string getPcapPlusPlusVersionFull() { return PCAPPLUSPLUS_VERSION_FULL; } - - /** - * @return The build date and time in a format of "Mmm dd yyyy hh:mm:ss" - */ - inline std::string getBuildDateTime() { return std::string(__DATE__) + " " + std::string(__TIME__); } - - /** - * @return The Git commit (revision) the binaries are built from - */ - std::string getGitCommit(); - - /** - * @return The Git branch the binaries are built from - */ - std::string getGitBranch(); - - /** - * @return Git branch and commit the binaries are built from. - * Aggregates data from getGitCommit() and getGitBranch() - */ - std::string getGitInfo(); +namespace pcpp { +#define PCAPPLUSPLUS_VERSION "23.09+" +#define PCAPPLUSPLUS_VERSION_OFFICIAL "non-official release" +#define PCAPPLUSPLUS_VERSION_FULL \ + "v" PCAPPLUSPLUS_VERSION " (" PCAPPLUSPLUS_VERSION_OFFICIAL ")" + +/** + * @return PcapPlusPlus current version, e.g: 23.09. Notice that for + * non-official releases (which were pulled from GitHub) the version will end + * with a '+'. For example: '23.09+' means non-official release but '23.09' + * means official release + */ +inline std::string getPcapPlusPlusVersion() { return PCAPPLUSPLUS_VERSION; } + +/** + * @return PcapPlusPlus long version string which includes the version and info + * whether it's an official or non-official release. For example: "v23.09+ + * (non-official release)" or "v23.09 (official release)" + */ +inline std::string getPcapPlusPlusVersionFull() { + return PCAPPLUSPLUS_VERSION_FULL; +} + +/** + * @return The build date and time in a format of "Mmm dd yyyy hh:mm:ss" + */ +inline std::string getBuildDateTime() { + return std::string(__DATE__) + " " + std::string(__TIME__); } +/** + * @return The Git commit (revision) the binaries are built from + */ +std::string getGitCommit(); + +/** + * @return The Git branch the binaries are built from + */ +std::string getGitBranch(); + +/** + * @return Git branch and commit the binaries are built from. + * Aggregates data from getGitCommit() and getGitBranch() + */ +std::string getGitInfo(); + +} // namespace pcpp + #endif /* PCAPPP_VERSION_H */ diff --git a/Common++/header/PointerVector.h b/Common++/header/PointerVector.h index 68eed1d1f5..5205dc40cc 100644 --- a/Common++/header/PointerVector.h +++ b/Common++/header/PointerVector.h @@ -1,8 +1,8 @@ #ifndef PCAPPP_POINTER_VECTOR #define PCAPPP_POINTER_VECTOR -#include #include +#include #include /// @file @@ -11,163 +11,157 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - - /** - * @class PointerVector - * A template class for representing a std::vector of pointers. Once (a pointer to) an element is added to this vector, - * the element responsibility moves to the vector, meaning the PointerVector will free the object once it's removed from the vector - * This class wraps std::vector and adds the capability of freeing objects once they're removed from it - */ - template - class PointerVector - { - public: - /** - * Iterator object that is used for iterating all elements in the vector - */ - typedef typename std::vector::iterator VectorIterator; - - /** - * Const iterator object that is used for iterating all elements in a constant vector - */ - typedef typename std::vector::const_iterator ConstVectorIterator; - - /** - * A constructor that create an empty instance of this object - */ - PointerVector() { } - - /** - * A destructor for this class. The destructor frees all elements that are binded to the vector - */ - ~PointerVector() - { - for (VectorIterator iter = m_Vector.begin(); iter != m_Vector.end(); iter++) - { - delete (*iter); - } - } - - /** - * Copy constructor. Once a vector is copied from another vector, all elements inside it are copied, - * meaning the new vector will contain pointers to copied elements, not pointers to the elements of the original vector - */ - PointerVector(const PointerVector& other) - { - for (ConstVectorIterator iter = other.begin(); iter != other.end(); iter++) - { - T* objCopy = new T(**iter); - m_Vector.push_back(objCopy); - } - } - - /** - * Clears all elements of the vector while freeing them - */ - void clear() - { - for (VectorIterator iter = m_Vector.begin(); iter != m_Vector.end(); iter++) - { - delete (*iter); - } - - m_Vector.clear(); - } - - /** - * Add a new (pointer to an) element to the vector - */ - void pushBack(T* element) { m_Vector.push_back(element); } - - /** - * Get the first element of the vector - * @return An iterator object pointing to the first element of the vector - */ - VectorIterator begin() { return m_Vector.begin(); } - - /** - * Get the first element of a constant vector - * @return A const iterator object pointing to the first element of the vector - */ - ConstVectorIterator begin() const { return m_Vector.begin(); } - - /** - * Get the last element of the vector - * @return An iterator object pointing to the last element of the vector - */ - VectorIterator end() { return m_Vector.end(); } - - /** - * Get the last element of a constant vector - * @return A const iterator object pointing to the last element of the vector - */ - ConstVectorIterator end() const { return m_Vector.end(); } - - - //inline size_t size() { return m_Vector.size(); } - - /** - * Get number of elements in the vector - * @return The number of elements in the vector - */ - size_t size() const { return m_Vector.size(); } - - /** - * Returns a pointer of the first element in the vector - * @return A pointer of the first element in the vector - */ - T* front() { return m_Vector.front(); } - - /** - * Removes from the vector a single element (position). Once the element is erased, it's also freed - * @param[in] position The position of the element to erase - * @return An iterator pointing to the new location of the element that followed the last element erased by the function call - */ - VectorIterator erase(VectorIterator position) - { - delete (*position); - return m_Vector.erase(position); - } - - /** - * Remove an element from the vector without freeing it - * param[in] position The position of the element to remove from the vector - * @return A pointer to the element which is no longer managed by the vector. It's user responsibility to free it - */ - T* getAndRemoveFromVector(VectorIterator& position) - { - T* result = (*position); - VectorIterator tempPos = position; - tempPos = m_Vector.erase(tempPos); - position = tempPos; - return result; - } - - /** - * Return a pointer to the element in a certain index - * @param[in] index The index to retrieve the element from - * @return The element at the specified position in the vector - */ - T* at(int index) - { - return m_Vector.at(index); - } - - /** - * Return a const pointer to the element in a certain index - * @param[in] index The index to retrieve the element from - * @return The element at the specified position in the vector - */ - const T* at(int index) const - { - return m_Vector.at(index); - } - - private: - std::vector m_Vector; - }; +namespace pcpp { + +/** + * @class PointerVector + * A template class for representing a std::vector of pointers. Once (a pointer + * to) an element is added to this vector, the element responsibility moves to + * the vector, meaning the PointerVector will free the object once it's removed + * from the vector This class wraps std::vector and adds the capability of + * freeing objects once they're removed from it + */ +template +class PointerVector { + public: + /** + * Iterator object that is used for iterating all elements in the vector + */ + typedef typename std::vector::iterator VectorIterator; + + /** + * Const iterator object that is used for iterating all elements in a constant + * vector + */ + typedef typename std::vector::const_iterator ConstVectorIterator; + + /** + * A constructor that create an empty instance of this object + */ + PointerVector() {} + + /** + * A destructor for this class. The destructor frees all elements that are + * binded to the vector + */ + ~PointerVector() { + for (VectorIterator iter = m_Vector.begin(); iter != m_Vector.end(); + iter++) { + delete (*iter); + } + } + + /** + * Copy constructor. Once a vector is copied from another vector, all elements + * inside it are copied, meaning the new vector will contain pointers to + * copied elements, not pointers to the elements of the original vector + */ + PointerVector(const PointerVector& other) { + for (ConstVectorIterator iter = other.begin(); iter != other.end(); + iter++) { + T* objCopy = new T(**iter); + m_Vector.push_back(objCopy); + } + } + + /** + * Clears all elements of the vector while freeing them + */ + void clear() { + for (VectorIterator iter = m_Vector.begin(); iter != m_Vector.end(); + iter++) { + delete (*iter); + } + + m_Vector.clear(); + } + + /** + * Add a new (pointer to an) element to the vector + */ + void pushBack(T* element) { m_Vector.push_back(element); } + + /** + * Get the first element of the vector + * @return An iterator object pointing to the first element of the vector + */ + VectorIterator begin() { return m_Vector.begin(); } + + /** + * Get the first element of a constant vector + * @return A const iterator object pointing to the first element of the vector + */ + ConstVectorIterator begin() const { return m_Vector.begin(); } + + /** + * Get the last element of the vector + * @return An iterator object pointing to the last element of the vector + */ + VectorIterator end() { return m_Vector.end(); } + + /** + * Get the last element of a constant vector + * @return A const iterator object pointing to the last element of the vector + */ + ConstVectorIterator end() const { return m_Vector.end(); } + + // inline size_t size() { return m_Vector.size(); } + + /** + * Get number of elements in the vector + * @return The number of elements in the vector + */ + size_t size() const { return m_Vector.size(); } + + /** + * Returns a pointer of the first element in the vector + * @return A pointer of the first element in the vector + */ + T* front() { return m_Vector.front(); } + + /** + * Removes from the vector a single element (position). Once the element is + * erased, it's also freed + * @param[in] position The position of the element to erase + * @return An iterator pointing to the new location of the element that + * followed the last element erased by the function call + */ + VectorIterator erase(VectorIterator position) { + delete (*position); + return m_Vector.erase(position); + } + + /** + * Remove an element from the vector without freeing it + * param[in] position The position of the element to remove from the vector + * @return A pointer to the element which is no longer managed by the vector. + * It's user responsibility to free it + */ + T* getAndRemoveFromVector(VectorIterator& position) { + T* result = (*position); + VectorIterator tempPos = position; + tempPos = m_Vector.erase(tempPos); + position = tempPos; + return result; + } + + /** + * Return a pointer to the element in a certain index + * @param[in] index The index to retrieve the element from + * @return The element at the specified position in the vector + */ + T* at(int index) { return m_Vector.at(index); } + + /** + * Return a const pointer to the element in a certain index + * @param[in] index The index to retrieve the element from + * @return The element at the specified position in the vector + */ + const T* at(int index) const { return m_Vector.at(index); } + + private: + std::vector m_Vector; +}; } // namespace pcpp diff --git a/Common++/header/SystemUtils.h b/Common++/header/SystemUtils.h index afbf9a40c2..e45873539c 100644 --- a/Common++/header/SystemUtils.h +++ b/Common++/header/SystemUtils.h @@ -10,386 +10,386 @@ #define MAX_NUM_OF_CORES 32 #ifdef _MSC_VER -int gettimeofday(struct timeval * tp, struct timezone * tzp); +int gettimeofday(struct timeval* tp, struct timezone* tzp); #endif /** * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - - /** - * @struct SystemCore - * Represents data of 1 CPU core. Current implementation supports up to 32 cores - */ - struct SystemCore - { - /** - * Core position in a 32-bit mask. For each core this attribute holds a 4B integer where only 1 bit is set, according to the core ID. - * For example: - * - In core #0 the right-most bit will be set (meaning the number 0x01); - * - in core #5 the 5th right-most bit will be set (meaning the number 0x20) - */ - uint32_t Mask; - - /** - * Core ID - a value between 0 and 31 - */ - uint8_t Id; - - /** - * Overload of the comparison operator - * @return true if 2 addresses are equal. False otherwise - */ - bool operator==(const SystemCore& other) const { return Id == other.Id; } - }; - - /** - * @struct SystemCores - * Contains static representation to all 32 cores and a static array to map core ID (integer) to a SystemCore struct - */ - struct SystemCores - { - /** - * Static representation of core #0 - */ - static const SystemCore Core0; - /** - * Static representation of core #1 - */ - static const SystemCore Core1; - /** - * Static representation of core #2 - */ - static const SystemCore Core2; - /** - * Static representation of core #3 - */ - static const SystemCore Core3; - /** - * Static representation of core #4 - */ - static const SystemCore Core4; - /** - * Static representation of core #5 - */ - static const SystemCore Core5; - /** - * Static representation of core #6 - */ - static const SystemCore Core6; - /** - * Static representation of core #7 - */ - static const SystemCore Core7; - /** - * Static representation of core #8 - */ - static const SystemCore Core8; - /** - * Static representation of core #9 - */ - static const SystemCore Core9; - /** - * Static representation of core #10 - */ - static const SystemCore Core10; - /** - * Static representation of core #11 - */ - static const SystemCore Core11; - /** - * Static representation of core #12 - */ - static const SystemCore Core12; - /** - * Static representation of core #13 - */ - static const SystemCore Core13; - /** - * Static representation of core #14 - */ - static const SystemCore Core14; - /** - * Static representation of core #15 - */ - static const SystemCore Core15; - /** - * Static representation of core #16 - */ - static const SystemCore Core16; - /** - * Static representation of core #17 - */ - static const SystemCore Core17; - /** - * Static representation of core #18 - */ - static const SystemCore Core18; - /** - * Static representation of core #19 - */ - static const SystemCore Core19; - /** - * Static representation of core #20 - */ - static const SystemCore Core20; - /** - * Static representation of core #21 - */ - static const SystemCore Core21; - /** - * Static representation of core #22 - */ - static const SystemCore Core22; - /** - * Static representation of core #23 - */ - static const SystemCore Core23; - /** - * Static representation of core #24 - */ - static const SystemCore Core24; - /** - * Static representation of core #25 - */ - static const SystemCore Core25; - /** - * Static representation of core #26 - */ - static const SystemCore Core26; - /** - * Static representation of core #27 - */ - static const SystemCore Core27; - /** - * Static representation of core #28 - */ - static const SystemCore Core28; - /** - * Static representation of core #29 - */ - static const SystemCore Core29; - /** - * Static representation of core #30 - */ - static const SystemCore Core30; - /** - * Static representation of core #31 - */ - static const SystemCore Core31; - - /** - * A static array for mapping core ID (integer) to the corresponding static SystemCore representation - */ - static const SystemCore IdToSystemCore[MAX_NUM_OF_CORES]; - }; - - typedef uint32_t CoreMask; - - /** - * Get total number of cores on device - * @return Total number of CPU cores on device - */ - int getNumOfCores(); - - /** - * Create a core mask for all cores available on machine - * @return A core mask for all cores available on machine - */ - CoreMask getCoreMaskForAllMachineCores(); - - - /** - * Create a core mask from a vector of system cores - * @param[in] cores A vector of SystemCore instances - * @return A core mask representing these cores - */ - CoreMask createCoreMaskFromCoreVector(std::vector cores); - - - /** - * Create a core mask from a vector of core IDs - * @param[in] coreIds A vector of core IDs - * @return A core mask representing these cores - */ - CoreMask createCoreMaskFromCoreIds(std::vector coreIds); - - - /** - * Convert a core mask into a vector of its appropriate system cores - * @param[in] coreMask The input core mask - * @param[out] resultVec The vector that will contain the system cores - */ - void createCoreVectorFromCoreMask(CoreMask coreMask, std::vector& resultVec); - - /** - * Execute a shell command and return its output - * @param[in] command The command to run - * @return The output of the command (both stdout and stderr) - */ - std::string executeShellCommand(const std::string &command); - - /** - * Check if a directory exists - * @param[in] dirPath Full path of the directory to search - * @return True if directory exists, false otherwise - */ - bool directoryExists(const std::string &dirPath); - - /** - * Retrieve a system-wide real-time accurate clock. It's actually a multi-platform version of clock_gettime() which is - * fully supported only on Linux - * @param[out] sec The second portion of the time - * @param[out] nsec The nanosecond portion of the time - * @return 0 for success, or -1 for failure - */ - int clockGetTime(long& sec, long& nsec); - - /** - * A multi-platform version of the popular sleep method. This method simply runs the right sleep method, according to the platform - * it is running on. - * @param[in] seconds Number of seconds to sleep - */ - void multiPlatformSleep(uint32_t seconds); - - /** - * A multi-platform version of sleep in milliseconds resolution. This method simply runs the right sleep method, according to the platform - * it is running on. - * @param[in] milliseconds Number of milliseconds to sleep - */ - void multiPlatformMSleep(uint32_t milliseconds); - - /** - * A multi-platform version of `htons` which convert host to network byte order - * @param[in] host Value in host byte order - * @return Value in network byte order - */ - uint16_t hostToNet16(uint16_t host); - - /** - * A multi-platform version of `ntohs` which convert network to host byte order - * @param[in] net Value in network byte order - * @return Value in host byte order - */ - uint16_t netToHost16(uint16_t net); - - /** - * A multi-platform version of `htonl` which convert host to network byte order - * @param[in] host Value in host byte order - * @return Value in network byte order - */ - uint32_t hostToNet32(uint32_t host); - - /** - * A multi-platform version of `ntohl` which convert network to host byte order - * @param[in] net Value in network byte order - * @return Value in host byte order - */ - uint32_t netToHost32(uint32_t net); - - /** - * @class AppName - * This class extracts the application name from the current running executable and stores it for usage of the application throughout its runtime. - * This class should be initialized once in the beginning of the main() method using AppName#init() and from then on the app name could be retrieved using AppName#get() - */ - class AppName - { - private: - static std::string m_AppName; - - public: - /** - * Static init method which should be called once at the beginning of the main method. - * @param[in] argc The argc param from main() - * @param[in] argv The argv param from main() - */ - // cppcheck-suppress constParameter - static void init(int argc, char* argv[]) - { - if (argc == 0) - { - m_AppName.clear(); - return; - } - - m_AppName = argv[0]; - - // remove Linux/Unix path - size_t lastPos = m_AppName.rfind('/'); - if (lastPos != std::string::npos) - { - m_AppName = m_AppName.substr(lastPos + 1); - } - - // remove Windows path - lastPos = m_AppName.rfind('\\'); - if (lastPos != std::string::npos) - { - m_AppName = m_AppName.substr(lastPos + 1); - } - - // remove file extension - lastPos = m_AppName.rfind('.'); - if (lastPos != std::string::npos) { - m_AppName.resize(lastPos); - } - } - - /** - * @return The app name as extracted from the current running executable - */ - static const std::string& get() { return m_AppName; } - }; - - /** - * @class ApplicationEventHandler - * A singleton class that provides callbacks for events that occur during application life-cycle such as ctrl+c pressed, - * application closed, killed, etc. - */ - class ApplicationEventHandler - { - public: - /** - * @typedef EventHandlerCallback - * The callback to be invoked when the event occurs - * @param[in] cookie A pointer the the cookie provided by the user in ApplicationEventHandler c'tor - */ - typedef void (*EventHandlerCallback)(void* cookie); - - /** - * As ApplicationEventHandler is a singleton, this is the static getter to retrieve its instance - * @return The singleton instance of ApplicationEventHandler - */ - static ApplicationEventHandler& getInstance() - { - static ApplicationEventHandler instance; - return instance; - } - - /** - * Register for an application-interrupted event, meaning ctrl+c was pressed - * @param[in] handler The callback to be activated when the event occurs - * @param[in] cookie A pointer to a user provided object. This object will be transferred to the EventHandlerCallback callback. - * This cookie is very useful for transferring objects that give context to the event callback - */ - void onApplicationInterrupted(EventHandlerCallback handler, void* cookie); - - private: - EventHandlerCallback m_ApplicationInterruptedHandler; - void* m_ApplicationInterruptedCookie; - - // private c'tor - ApplicationEventHandler(); +namespace pcpp { + +/** + * @struct SystemCore + * Represents data of 1 CPU core. Current implementation supports up to 32 cores + */ +struct SystemCore { + /** + * Core position in a 32-bit mask. For each core this attribute holds a 4B + * integer where only 1 bit is set, according to the core ID. For example: + * - In core #0 the right-most bit will be set (meaning the number 0x01); + * - in core #5 the 5th right-most bit will be set (meaning the number 0x20) + */ + uint32_t Mask; + + /** + * Core ID - a value between 0 and 31 + */ + uint8_t Id; + + /** + * Overload of the comparison operator + * @return true if 2 addresses are equal. False otherwise + */ + bool operator==(const SystemCore& other) const { return Id == other.Id; } +}; + +/** + * @struct SystemCores + * Contains static representation to all 32 cores and a static array to map core + * ID (integer) to a SystemCore struct + */ +struct SystemCores { + /** + * Static representation of core #0 + */ + static const SystemCore Core0; + /** + * Static representation of core #1 + */ + static const SystemCore Core1; + /** + * Static representation of core #2 + */ + static const SystemCore Core2; + /** + * Static representation of core #3 + */ + static const SystemCore Core3; + /** + * Static representation of core #4 + */ + static const SystemCore Core4; + /** + * Static representation of core #5 + */ + static const SystemCore Core5; + /** + * Static representation of core #6 + */ + static const SystemCore Core6; + /** + * Static representation of core #7 + */ + static const SystemCore Core7; + /** + * Static representation of core #8 + */ + static const SystemCore Core8; + /** + * Static representation of core #9 + */ + static const SystemCore Core9; + /** + * Static representation of core #10 + */ + static const SystemCore Core10; + /** + * Static representation of core #11 + */ + static const SystemCore Core11; + /** + * Static representation of core #12 + */ + static const SystemCore Core12; + /** + * Static representation of core #13 + */ + static const SystemCore Core13; + /** + * Static representation of core #14 + */ + static const SystemCore Core14; + /** + * Static representation of core #15 + */ + static const SystemCore Core15; + /** + * Static representation of core #16 + */ + static const SystemCore Core16; + /** + * Static representation of core #17 + */ + static const SystemCore Core17; + /** + * Static representation of core #18 + */ + static const SystemCore Core18; + /** + * Static representation of core #19 + */ + static const SystemCore Core19; + /** + * Static representation of core #20 + */ + static const SystemCore Core20; + /** + * Static representation of core #21 + */ + static const SystemCore Core21; + /** + * Static representation of core #22 + */ + static const SystemCore Core22; + /** + * Static representation of core #23 + */ + static const SystemCore Core23; + /** + * Static representation of core #24 + */ + static const SystemCore Core24; + /** + * Static representation of core #25 + */ + static const SystemCore Core25; + /** + * Static representation of core #26 + */ + static const SystemCore Core26; + /** + * Static representation of core #27 + */ + static const SystemCore Core27; + /** + * Static representation of core #28 + */ + static const SystemCore Core28; + /** + * Static representation of core #29 + */ + static const SystemCore Core29; + /** + * Static representation of core #30 + */ + static const SystemCore Core30; + /** + * Static representation of core #31 + */ + static const SystemCore Core31; + + /** + * A static array for mapping core ID (integer) to the corresponding static + * SystemCore representation + */ + static const SystemCore IdToSystemCore[MAX_NUM_OF_CORES]; +}; + +typedef uint32_t CoreMask; + +/** + * Get total number of cores on device + * @return Total number of CPU cores on device + */ +int getNumOfCores(); + +/** + * Create a core mask for all cores available on machine + * @return A core mask for all cores available on machine + */ +CoreMask getCoreMaskForAllMachineCores(); + +/** + * Create a core mask from a vector of system cores + * @param[in] cores A vector of SystemCore instances + * @return A core mask representing these cores + */ +CoreMask createCoreMaskFromCoreVector(std::vector cores); + +/** + * Create a core mask from a vector of core IDs + * @param[in] coreIds A vector of core IDs + * @return A core mask representing these cores + */ +CoreMask createCoreMaskFromCoreIds(std::vector coreIds); + +/** + * Convert a core mask into a vector of its appropriate system cores + * @param[in] coreMask The input core mask + * @param[out] resultVec The vector that will contain the system cores + */ +void createCoreVectorFromCoreMask(CoreMask coreMask, + std::vector& resultVec); + +/** + * Execute a shell command and return its output + * @param[in] command The command to run + * @return The output of the command (both stdout and stderr) + */ +std::string executeShellCommand(const std::string& command); + +/** + * Check if a directory exists + * @param[in] dirPath Full path of the directory to search + * @return True if directory exists, false otherwise + */ +bool directoryExists(const std::string& dirPath); + +/** + * Retrieve a system-wide real-time accurate clock. It's actually a + * multi-platform version of clock_gettime() which is fully supported only on + * Linux + * @param[out] sec The second portion of the time + * @param[out] nsec The nanosecond portion of the time + * @return 0 for success, or -1 for failure + */ +int clockGetTime(long& sec, long& nsec); + +/** + * A multi-platform version of the popular sleep method. This method simply runs + * the right sleep method, according to the platform it is running on. + * @param[in] seconds Number of seconds to sleep + */ +void multiPlatformSleep(uint32_t seconds); + +/** + * A multi-platform version of sleep in milliseconds resolution. This method + * simply runs the right sleep method, according to the platform it is running + * on. + * @param[in] milliseconds Number of milliseconds to sleep + */ +void multiPlatformMSleep(uint32_t milliseconds); + +/** + * A multi-platform version of `htons` which convert host to network byte order + * @param[in] host Value in host byte order + * @return Value in network byte order + */ +uint16_t hostToNet16(uint16_t host); + +/** + * A multi-platform version of `ntohs` which convert network to host byte order + * @param[in] net Value in network byte order + * @return Value in host byte order + */ +uint16_t netToHost16(uint16_t net); + +/** + * A multi-platform version of `htonl` which convert host to network byte order + * @param[in] host Value in host byte order + * @return Value in network byte order + */ +uint32_t hostToNet32(uint32_t host); + +/** + * A multi-platform version of `ntohl` which convert network to host byte order + * @param[in] net Value in network byte order + * @return Value in host byte order + */ +uint32_t netToHost32(uint32_t net); + +/** + * @class AppName + * This class extracts the application name from the current running executable + * and stores it for usage of the application throughout its runtime. This class + * should be initialized once in the beginning of the main() method using + * AppName#init() and from then on the app name could be retrieved using + * AppName#get() + */ +class AppName { + private: + static std::string m_AppName; + + public: + /** + * Static init method which should be called once at the beginning of the main + * method. + * @param[in] argc The argc param from main() + * @param[in] argv The argv param from main() + */ + // cppcheck-suppress constParameter + static void init(int argc, char* argv[]) { + if (argc == 0) { + m_AppName.clear(); + return; + } + + m_AppName = argv[0]; + + // remove Linux/Unix path + size_t lastPos = m_AppName.rfind('/'); + if (lastPos != std::string::npos) { + m_AppName = m_AppName.substr(lastPos + 1); + } + + // remove Windows path + lastPos = m_AppName.rfind('\\'); + if (lastPos != std::string::npos) { + m_AppName = m_AppName.substr(lastPos + 1); + } + + // remove file extension + lastPos = m_AppName.rfind('.'); + if (lastPos != std::string::npos) { + m_AppName.resize(lastPos); + } + } + + /** + * @return The app name as extracted from the current running executable + */ + static const std::string& get() { return m_AppName; } +}; + +/** + * @class ApplicationEventHandler + * A singleton class that provides callbacks for events that occur during + * application life-cycle such as ctrl+c pressed, application closed, killed, + * etc. + */ +class ApplicationEventHandler { + public: + /** + * @typedef EventHandlerCallback + * The callback to be invoked when the event occurs + * @param[in] cookie A pointer the the cookie provided by the user in + * ApplicationEventHandler c'tor + */ + typedef void (*EventHandlerCallback)(void* cookie); + + /** + * As ApplicationEventHandler is a singleton, this is the static getter to + * retrieve its instance + * @return The singleton instance of ApplicationEventHandler + */ + static ApplicationEventHandler& getInstance() { + static ApplicationEventHandler instance; + return instance; + } + + /** + * Register for an application-interrupted event, meaning ctrl+c was pressed + * @param[in] handler The callback to be activated when the event occurs + * @param[in] cookie A pointer to a user provided object. This object will be + * transferred to the EventHandlerCallback callback. This cookie is very + * useful for transferring objects that give context to the event callback + */ + void onApplicationInterrupted(EventHandlerCallback handler, void* cookie); + + private: + EventHandlerCallback m_ApplicationInterruptedHandler; + void* m_ApplicationInterruptedCookie; + + // private c'tor + ApplicationEventHandler(); #if defined(_WIN32) - static int handlerRoutine(unsigned long fdwCtrlType); + static int handlerRoutine(unsigned long fdwCtrlType); #else - static void handlerRoutine(int signum); + static void handlerRoutine(int signum); #endif - }; +}; } // namespace pcpp diff --git a/Common++/header/TablePrinter.h b/Common++/header/TablePrinter.h index 7fe29a7b3d..283595019a 100644 --- a/Common++/header/TablePrinter.h +++ b/Common++/header/TablePrinter.h @@ -6,66 +6,68 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - /** - * A class for printing tables in command-line - */ - class TablePrinter - { - public: - /** - * C'tor - get column names and column widths - * @param[in] columnNames A vector of strings containing column names - * @param[in] columnWidths A vector of integers containing column widths - */ - TablePrinter(std::vector columnNames, std::vector columnWidths); +namespace pcpp { +/** + * A class for printing tables in command-line + */ +class TablePrinter { + public: + /** + * C'tor - get column names and column widths + * @param[in] columnNames A vector of strings containing column names + * @param[in] columnWidths A vector of integers containing column widths + */ + TablePrinter(std::vector columnNames, + std::vector columnWidths); - /** - * A d'tor for this class. Closes the table if not closed - */ - virtual ~TablePrinter(); + /** + * A d'tor for this class. Closes the table if not closed + */ + virtual ~TablePrinter(); - /** - * Print a single row by providing a single string containing all values delimited by a specified character. - * For example: if specified delimiter is '|' and there are 3 columns an example input can be: - * "value for column1|value for column2|value for column3" - * @param[in] values A string delimited by a specified delimiter that contains values for all columns - * @param[in] delimiter A delimiter that separates between values of different columns in the values string - * @return True if row was printed successfully or false otherwise (in any case of error an appropriate message - * will be printed to log) - */ - bool printRow(const std::string& values, char delimiter); + /** + * Print a single row by providing a single string containing all values + * delimited by a specified character. For example: if specified delimiter is + * '|' and there are 3 columns an example input can be: "value for + * column1|value for column2|value for column3" + * @param[in] values A string delimited by a specified delimiter that contains + * values for all columns + * @param[in] delimiter A delimiter that separates between values of different + * columns in the values string + * @return True if row was printed successfully or false otherwise (in any + * case of error an appropriate message will be printed to log) + */ + bool printRow(const std::string& values, char delimiter); - /** - * Print a single row - * @param[in] values A vector of strings containing values for all columns - * @return True if row was printed successfully or false otherwise (in any case of error an appropriate message - * will be printed to log) - */ - bool printRow(std::vector values); + /** + * Print a single row + * @param[in] values A vector of strings containing values for all columns + * @return True if row was printed successfully or false otherwise (in any + * case of error an appropriate message will be printed to log) + */ + bool printRow(std::vector values); - /** - * Print a separator line - */ - void printSeparator(); + /** + * Print a separator line + */ + void printSeparator(); - /** - * Close the table - should be called after all rows were printed. Calling this method is not a must as it's called - * in the class d'tor - */ - void closeTable(); + /** + * Close the table - should be called after all rows were printed. Calling + * this method is not a must as it's called in the class d'tor + */ + void closeTable(); - private: - std::vector m_ColumnNames; - std::vector m_ColumnWidths; - bool m_FirstRow; - bool m_TableClosed; + private: + std::vector m_ColumnNames; + std::vector m_ColumnWidths; + bool m_FirstRow; + bool m_TableClosed; - /** - * Print the table headline - */ - void printHeadline(); - }; + /** + * Print the table headline + */ + void printHeadline(); +}; -} +} // namespace pcpp diff --git a/Common++/header/TimespecTimeval.h b/Common++/header/TimespecTimeval.h index 5262baf93c..2df5040f57 100644 --- a/Common++/header/TimespecTimeval.h +++ b/Common++/header/TimespecTimeval.h @@ -2,15 +2,17 @@ /// Windows #ifndef TIMEVAL_TO_TIMESPEC -#define TIMEVAL_TO_TIMESPEC(tv, ts) { \ - (ts)->tv_sec = (tv)->tv_sec; \ - (ts)->tv_nsec = (tv)->tv_usec * 1000; \ -} +#define TIMEVAL_TO_TIMESPEC(tv, ts) \ + { \ + (ts)->tv_sec = (tv)->tv_sec; \ + (ts)->tv_nsec = (tv)->tv_usec * 1000; \ + } #endif #ifndef TIMESPEC_TO_TIMEVAL -#define TIMESPEC_TO_TIMEVAL(tv, ts) { \ - (tv)->tv_sec = (ts)->tv_sec; \ - (tv)->tv_usec = (ts)->tv_nsec / 1000; \ -} +#define TIMESPEC_TO_TIMEVAL(tv, ts) \ + { \ + (tv)->tv_sec = (ts)->tv_sec; \ + (tv)->tv_usec = (ts)->tv_nsec / 1000; \ + } #endif diff --git a/Common++/src/GeneralUtils.cpp b/Common++/src/GeneralUtils.cpp index 7d8113184d..e80ef959e0 100644 --- a/Common++/src/GeneralUtils.cpp +++ b/Common++/src/GeneralUtils.cpp @@ -2,96 +2,86 @@ #include "GeneralUtils.h" #include "Logger.h" -#include #include -#include +#include #include +#include -namespace pcpp -{ +namespace pcpp { -std::string byteArrayToHexString(const uint8_t* byteArr, size_t byteArrSize, int stringSizeLimit) -{ - if (stringSizeLimit <= 0) - stringSizeLimit = byteArrSize; +std::string byteArrayToHexString(const uint8_t* byteArr, size_t byteArrSize, + int stringSizeLimit) { + if (stringSizeLimit <= 0) + stringSizeLimit = byteArrSize; - std::stringstream dataStream; - dataStream << std::hex; - for (size_t i = 0; i < byteArrSize; ++i) - { - if (i >= (size_t)stringSizeLimit) - break; + std::stringstream dataStream; + dataStream << std::hex; + for (size_t i = 0; i < byteArrSize; ++i) { + if (i >= (size_t)stringSizeLimit) + break; - dataStream << std::setw(2) << std::setfill('0') << (int)byteArr[i]; - } + dataStream << std::setw(2) << std::setfill('0') << (int)byteArr[i]; + } - return dataStream.str(); + return dataStream.str(); } -static int char2int(char input) -{ - if(input >= '0' && input <= '9') - return input - '0'; - if(input >= 'A' && input <= 'F') - return input - 'A' + 10; - if(input >= 'a' && input <= 'f') - return input - 'a' + 10; - return -1; +static int char2int(char input) { + if (input >= '0' && input <= '9') + return input - '0'; + if (input >= 'A' && input <= 'F') + return input - 'A' + 10; + if (input >= 'a' && input <= 'f') + return input - 'a' + 10; + return -1; } -size_t hexStringToByteArray(const std::string& hexString, uint8_t* resultByteArr, size_t resultByteArrSize) -{ - if (hexString.size() % 2 != 0) - { - PCPP_LOG_ERROR("Input string is in odd size"); - return 0; - } - - memset(resultByteArr, 0, resultByteArrSize); - for (size_t i = 0; i < hexString.length(); i += 2) - { - if (i >= resultByteArrSize * 2) - return resultByteArrSize; - - int firstChar = char2int(hexString[i]); - int secondChar = char2int(hexString[i + 1]); - if (firstChar < 0 || secondChar < 0) - { - PCPP_LOG_ERROR("Input string has an illegal character"); - resultByteArr[0] = '\0'; - return 0; - } - - resultByteArr[i / 2] = (firstChar << 4) | secondChar; - } - - return hexString.length() / 2; +size_t hexStringToByteArray(const std::string& hexString, + uint8_t* resultByteArr, size_t resultByteArrSize) { + if (hexString.size() % 2 != 0) { + PCPP_LOG_ERROR("Input string is in odd size"); + return 0; + } + + memset(resultByteArr, 0, resultByteArrSize); + for (size_t i = 0; i < hexString.length(); i += 2) { + if (i >= resultByteArrSize * 2) + return resultByteArrSize; + + int firstChar = char2int(hexString[i]); + int secondChar = char2int(hexString[i + 1]); + if (firstChar < 0 || secondChar < 0) { + PCPP_LOG_ERROR("Input string has an illegal character"); + resultByteArr[0] = '\0'; + return 0; + } + + resultByteArr[i / 2] = (firstChar << 4) | secondChar; + } + + return hexString.length() / 2; } - -char* cross_platform_memmem(const char* haystack, size_t haystackLen, const char* needle, size_t needleLen) -{ - char* ptr = (char*)haystack; - while (needleLen <= (haystackLen - (ptr - haystack))) - { - if (nullptr != (ptr = (char*)memchr(ptr, (int)(*needle), haystackLen - (ptr - haystack)))) - { - // check if there is room to do a memcmp - if(needleLen > (haystackLen - (ptr - haystack))) - { - return nullptr; - } - - if (0 == memcmp(ptr, needle, needleLen)) - return ptr; - else - ++ptr; - } - else - break; - } - - return nullptr; +char* cross_platform_memmem(const char* haystack, size_t haystackLen, + const char* needle, size_t needleLen) { + char* ptr = (char*)haystack; + while (needleLen <= (haystackLen - (ptr - haystack))) { + if (nullptr != (ptr = (char*)memchr(ptr, (int)(*needle), + haystackLen - (ptr - haystack)))) { + // check if there is room to do a memcmp + if (needleLen > (haystackLen - (ptr - haystack))) { + return nullptr; + } + + if (0 == memcmp(ptr, needle, needleLen)) + return ptr; + else + ++ptr; + } else + break; + } + + return nullptr; } -} +} // namespace pcpp diff --git a/Common++/src/IpAddress.cpp b/Common++/src/IpAddress.cpp index 5d8a353a4e..0514f96403 100644 --- a/Common++/src/IpAddress.cpp +++ b/Common++/src/IpAddress.cpp @@ -1,630 +1,502 @@ #define LOG_MODULE CommonLogModuleIpUtils +#include "IpAddress.h" +#include "EndianPortable.h" +#include "IpUtils.h" +#include "Logger.h" #include +#include #include #include #include #include #include -#include -#include "Logger.h" -#include "IpUtils.h" -#include "IpAddress.h" -#include "EndianPortable.h" // for AF_INET, AF_INET6 #if !defined(_WIN32) #include #endif - -namespace pcpp -{ - - const IPv4Address IPv4Address::Zero; - const IPv6Address IPv6Address::Zero; - - const IPv4Address IPv4Address::MulticastRangeLowerBound("224.0.0.0"); - const IPv4Address IPv4Address::MulticastRangeUpperBound("239.255.255.255"); - const IPv6Address IPv6Address::MulticastRangeLowerBound("ff00:0000:0000:0000:0000:0000:0000:0000"); - - // ~~~~~~~~~~~ - // IPv4Address - // ~~~~~~~~~~~ - - - std::string IPv4Address::toString() const - { - char addrBuffer[INET_ADDRSTRLEN]; - - if (inet_ntop(AF_INET, toBytes(), addrBuffer, sizeof(addrBuffer)) != nullptr) - return std::string(addrBuffer); - - return std::string(); - } - - - bool IPv4Address::isMulticast() const - { - return !operator<(MulticastRangeLowerBound) && (operator<(MulticastRangeUpperBound) || operator==(MulticastRangeUpperBound)); - } - - IPv4Address::IPv4Address(const std::string& addrAsString) - { - if (inet_pton(AF_INET, addrAsString.data(), m_Bytes) <= 0) - memset(m_Bytes, 0, sizeof(m_Bytes)); - } - - - bool IPv4Address::matchNetwork(const IPv4Network& network) const - { - return network.includes(*this); - } - - - bool IPv4Address::matchNetwork(const std::string& network) const - { - try - { - auto ipv4Network = IPv4Network(network); - return ipv4Network.includes(*this); - } - catch (const std::invalid_argument& e) - { - PCPP_LOG_ERROR(e.what()); - return false; - } - } - - - bool IPv4Address::matchSubnet(const IPv4Address& subnet, const std::string& subnetMask) const - { - try - { - auto ipv4Network = IPv4Network(subnet, subnetMask); - return ipv4Network.includes(*this); - } - catch (const std::invalid_argument& e) - { - PCPP_LOG_ERROR(e.what()); - return false; - } - } - - - bool IPv4Address::matchSubnet(const IPv4Address& subnet, const IPv4Address& subnetMask) const - { - try - { - auto ipv4Network = IPv4Network(subnet, subnetMask.toString()); - return ipv4Network.includes(*this); - } - catch (const std::invalid_argument& e) - { - PCPP_LOG_ERROR(e.what()); - return false; - } - } - - - // ~~~~~~~~~~~ - // IPv6Address - // ~~~~~~~~~~~ - - - std::string IPv6Address::toString() const - { - char addrBuffer[INET6_ADDRSTRLEN]; - - if (inet_ntop(AF_INET6, toBytes(), addrBuffer, sizeof(addrBuffer)) != nullptr) - return std::string(addrBuffer); - - return std::string(); - } - - - bool IPv6Address::isMulticast() const - { - return !operator<(MulticastRangeLowerBound); - } - - - IPv6Address::IPv6Address(const std::string& addrAsString) - { - if(inet_pton(AF_INET6, addrAsString.data(), m_Bytes) <= 0) - memset(m_Bytes, 0, sizeof(m_Bytes)); - } - - - void IPv6Address::copyTo(uint8_t** arr, size_t& length) const - { - const size_t addrLen = sizeof(m_Bytes); - length = addrLen; - *arr = new uint8_t[addrLen]; - memcpy(*arr, m_Bytes, addrLen); - } - - - bool IPv6Address::matchNetwork(const IPv6Network &network) const - { - return network.includes(*this); - } - - - bool IPv6Address::matchNetwork(const std::string& network) const - { - try - { - auto ipv6Network = IPv6Network(network); - return ipv6Network.includes(*this); - } - catch (const std::invalid_argument& e) - { - PCPP_LOG_ERROR(e.what()); - return false; - } - } - - - bool IPv6Address::matchSubnet(const IPv6Address& subnet, uint8_t prefixLength) const - { - try - { - auto ipv6Network = IPv6Network(subnet, prefixLength); - return ipv6Network.includes(*this); - } - catch (const std::invalid_argument& e) - { - PCPP_LOG_ERROR(e.what()); - return false; - } - } - - - // ~~~~~~~~~ - // IPAddress - // ~~~~~~~~~ - - - IPAddress::IPAddress(const std::string& addrAsString) : m_Type(IPv6AddressType), m_IPv6(addrAsString) - { - if (!m_IPv6.isValid()) // not IPv6 - { - m_Type = IPv4AddressType; - m_IPv4 = IPv4Address(addrAsString); - } - } - - - // ~~~~~~~~~~~ - // IPv4Network - // ~~~~~~~~~~~ - - - bool IPv4Network::isValidNetmask(const std::string& netmask) - { - if (netmask == "0.0.0.0") - { - return true; - } - - auto mask = IPv4Address(netmask); - if (!mask.isValid()) - { - return false; - } - - uint32_t maskAsInt = be32toh(mask.toInt()); - std::bitset<32> bitset(maskAsInt); - auto bitsetCount = bitset.count(); - - if (bitsetCount == 32) - { - return true; - } - else - { - return maskAsInt << bitsetCount == 0; - } - } - - - void IPv4Network::initFromAddressAndPrefixLength(const IPv4Address& address, uint8_t prefixLen) - { - m_Mask = be32toh(0xffffffff ^ (prefixLen < 32 ? 0xffffffff >> prefixLen: 0)); - m_NetworkPrefix = address.toInt() & m_Mask; - } - - - void IPv4Network::initFromAddressAndNetmask(const IPv4Address& address, const std::string& netmask) - { - IPv4Address netmaskAddr(netmask); - m_Mask = netmaskAddr.toInt(); - m_NetworkPrefix = address.toInt() & m_Mask; - } - - - IPv4Network::IPv4Network(const IPv4Address& address, uint8_t prefixLen) - { - if (!address.isValid()) - { - throw std::invalid_argument("address is not a valid IPv4 address"); - } - - if (prefixLen > 32) - { - throw std::invalid_argument("prefixLen must be an integer between 0 and 32"); - } - - initFromAddressAndPrefixLength(address, prefixLen); - } - - - IPv4Network::IPv4Network(const IPv4Address& address, const std::string& netmask) - { - if (!address.isValid()) - { - throw std::invalid_argument("address is not a valid IPv4 address"); - } - - if (!isValidNetmask(netmask)) - { - throw std::invalid_argument("netmask is not valid"); - } - - initFromAddressAndNetmask(address, netmask); - } - - - IPv4Network::IPv4Network(const std::string& addressAndNetmask) - { - std::stringstream stream(addressAndNetmask); - std::string networkPrefixStr, netmaskStr; - std::getline(stream, networkPrefixStr, '/'); - std::getline(stream, netmaskStr); - - if (netmaskStr.empty()) - { - throw std::invalid_argument("The input should be in the format of
/ or
/"); - } - - auto networkPrefix = IPv4Address(networkPrefixStr); - if (!networkPrefix.isValid()) - { - throw std::invalid_argument("The input doesn't contain a valid IPv4 network prefix"); - } - - if (std::all_of(netmaskStr.begin(), netmaskStr.end(), ::isdigit)) - { - uint32_t prefixLen = std::stoi(netmaskStr); - if (prefixLen > 32) - { - throw std::invalid_argument("Prefix length must be an integer between 0 and 32"); - } - - initFromAddressAndPrefixLength(networkPrefix, prefixLen); - } - else - { - if (!isValidNetmask(netmaskStr)) - { - throw std::invalid_argument("Netmask is not valid"); - } - - initFromAddressAndNetmask(networkPrefix, netmaskStr); - } - } - - - uint8_t IPv4Network::getPrefixLen() const - { - std::bitset<32> bitset(m_Mask); - return bitset.count(); - } - - - IPv4Address IPv4Network::getLowestAddress() const - { - std::bitset<32> bitset(m_Mask); - return bitset.count() < 32 ? m_NetworkPrefix + htobe32(1) : m_NetworkPrefix; - } - - - IPv4Address IPv4Network::getHighestAddress() const - { - auto tempAddress = static_cast(m_NetworkPrefix | ~m_Mask); - std::bitset<32> bitset(m_Mask); - return bitset.count() < 32 ? tempAddress - htobe32(1) : tempAddress; - } - - - uint64_t IPv4Network::getTotalAddressCount() const - { - std::bitset<32> bitset(static_cast(~m_Mask)); - return 1ULL << bitset.count(); - } - - - bool IPv4Network::includes(const IPv4Address& address) const - { - if (!address.isValid()) - { - return false; - } - return (address.toInt() & m_Mask) == m_NetworkPrefix; - } - - - bool IPv4Network::includes(const IPv4Network& network) const - { - uint32_t lowestAddress = network.m_NetworkPrefix; - uint32_t highestAddress = network.m_NetworkPrefix | ~network.m_Mask; - return ((lowestAddress & m_Mask) == m_NetworkPrefix && (highestAddress & m_Mask) == m_NetworkPrefix); - } - - - std::string IPv4Network::toString() const - { - std::ostringstream stream; - stream << getNetworkPrefix() << "/" << static_cast(getPrefixLen()); - return stream.str(); - } - - - // ~~~~~~~~~~~ - // IPv6Network - // ~~~~~~~~~~~ - +namespace pcpp { + +const IPv4Address IPv4Address::Zero; +const IPv6Address IPv6Address::Zero; + +const IPv4Address IPv4Address::MulticastRangeLowerBound("224.0.0.0"); +const IPv4Address IPv4Address::MulticastRangeUpperBound("239.255.255.255"); +const IPv6Address IPv6Address::MulticastRangeLowerBound( + "ff00:0000:0000:0000:0000:0000:0000:0000"); + +// ~~~~~~~~~~~ +// IPv4Address +// ~~~~~~~~~~~ + +std::string IPv4Address::toString() const { + char addrBuffer[INET_ADDRSTRLEN]; + + if (inet_ntop(AF_INET, toBytes(), addrBuffer, sizeof(addrBuffer)) != nullptr) + return std::string(addrBuffer); + + return std::string(); +} + +bool IPv4Address::isMulticast() const { + return !operator<(MulticastRangeLowerBound) && + (operator<(MulticastRangeUpperBound) || operator==( + MulticastRangeUpperBound)); +} + +IPv4Address::IPv4Address(const std::string& addrAsString) { + if (inet_pton(AF_INET, addrAsString.data(), m_Bytes) <= 0) + memset(m_Bytes, 0, sizeof(m_Bytes)); +} + +bool IPv4Address::matchNetwork(const IPv4Network& network) const { + return network.includes(*this); +} + +bool IPv4Address::matchNetwork(const std::string& network) const { + try { + auto ipv4Network = IPv4Network(network); + return ipv4Network.includes(*this); + } catch (const std::invalid_argument& e) { + PCPP_LOG_ERROR(e.what()); + return false; + } +} + +bool IPv4Address::matchSubnet(const IPv4Address& subnet, + const std::string& subnetMask) const { + try { + auto ipv4Network = IPv4Network(subnet, subnetMask); + return ipv4Network.includes(*this); + } catch (const std::invalid_argument& e) { + PCPP_LOG_ERROR(e.what()); + return false; + } +} + +bool IPv4Address::matchSubnet(const IPv4Address& subnet, + const IPv4Address& subnetMask) const { + try { + auto ipv4Network = IPv4Network(subnet, subnetMask.toString()); + return ipv4Network.includes(*this); + } catch (const std::invalid_argument& e) { + PCPP_LOG_ERROR(e.what()); + return false; + } +} + +// ~~~~~~~~~~~ +// IPv6Address +// ~~~~~~~~~~~ + +std::string IPv6Address::toString() const { + char addrBuffer[INET6_ADDRSTRLEN]; + + if (inet_ntop(AF_INET6, toBytes(), addrBuffer, sizeof(addrBuffer)) != nullptr) + return std::string(addrBuffer); + + return std::string(); +} + +bool IPv6Address::isMulticast() const { + return !operator<(MulticastRangeLowerBound); +} + +IPv6Address::IPv6Address(const std::string& addrAsString) { + if (inet_pton(AF_INET6, addrAsString.data(), m_Bytes) <= 0) + memset(m_Bytes, 0, sizeof(m_Bytes)); +} + +void IPv6Address::copyTo(uint8_t** arr, size_t& length) const { + const size_t addrLen = sizeof(m_Bytes); + length = addrLen; + *arr = new uint8_t[addrLen]; + memcpy(*arr, m_Bytes, addrLen); +} + +bool IPv6Address::matchNetwork(const IPv6Network& network) const { + return network.includes(*this); +} + +bool IPv6Address::matchNetwork(const std::string& network) const { + try { + auto ipv6Network = IPv6Network(network); + return ipv6Network.includes(*this); + } catch (const std::invalid_argument& e) { + PCPP_LOG_ERROR(e.what()); + return false; + } +} + +bool IPv6Address::matchSubnet(const IPv6Address& subnet, + uint8_t prefixLength) const { + try { + auto ipv6Network = IPv6Network(subnet, prefixLength); + return ipv6Network.includes(*this); + } catch (const std::invalid_argument& e) { + PCPP_LOG_ERROR(e.what()); + return false; + } +} + +// ~~~~~~~~~ +// IPAddress +// ~~~~~~~~~ + +IPAddress::IPAddress(const std::string& addrAsString) + : m_Type(IPv6AddressType), m_IPv6(addrAsString) { + if (!m_IPv6.isValid()) // not IPv6 + { + m_Type = IPv4AddressType; + m_IPv4 = IPv4Address(addrAsString); + } +} + +// ~~~~~~~~~~~ +// IPv4Network +// ~~~~~~~~~~~ + +bool IPv4Network::isValidNetmask(const std::string& netmask) { + if (netmask == "0.0.0.0") { + return true; + } + + auto mask = IPv4Address(netmask); + if (!mask.isValid()) { + return false; + } + + uint32_t maskAsInt = be32toh(mask.toInt()); + std::bitset<32> bitset(maskAsInt); + auto bitsetCount = bitset.count(); + + if (bitsetCount == 32) { + return true; + } else { + return maskAsInt << bitsetCount == 0; + } +} + +void IPv4Network::initFromAddressAndPrefixLength(const IPv4Address& address, + uint8_t prefixLen) { + m_Mask = be32toh(0xffffffff ^ (prefixLen < 32 ? 0xffffffff >> prefixLen : 0)); + m_NetworkPrefix = address.toInt() & m_Mask; +} + +void IPv4Network::initFromAddressAndNetmask(const IPv4Address& address, + const std::string& netmask) { + IPv4Address netmaskAddr(netmask); + m_Mask = netmaskAddr.toInt(); + m_NetworkPrefix = address.toInt() & m_Mask; +} + +IPv4Network::IPv4Network(const IPv4Address& address, uint8_t prefixLen) { + if (!address.isValid()) { + throw std::invalid_argument("address is not a valid IPv4 address"); + } + + if (prefixLen > 32) { + throw std::invalid_argument( + "prefixLen must be an integer between 0 and 32"); + } + + initFromAddressAndPrefixLength(address, prefixLen); +} + +IPv4Network::IPv4Network(const IPv4Address& address, + const std::string& netmask) { + if (!address.isValid()) { + throw std::invalid_argument("address is not a valid IPv4 address"); + } + + if (!isValidNetmask(netmask)) { + throw std::invalid_argument("netmask is not valid"); + } + + initFromAddressAndNetmask(address, netmask); +} + +IPv4Network::IPv4Network(const std::string& addressAndNetmask) { + std::stringstream stream(addressAndNetmask); + std::string networkPrefixStr, netmaskStr; + std::getline(stream, networkPrefixStr, '/'); + std::getline(stream, netmaskStr); + + if (netmaskStr.empty()) { + throw std::invalid_argument( + "The input should be in the format of
/ or " + "
/"); + } + + auto networkPrefix = IPv4Address(networkPrefixStr); + if (!networkPrefix.isValid()) { + throw std::invalid_argument( + "The input doesn't contain a valid IPv4 network prefix"); + } + + if (std::all_of(netmaskStr.begin(), netmaskStr.end(), ::isdigit)) { + uint32_t prefixLen = std::stoi(netmaskStr); + if (prefixLen > 32) { + throw std::invalid_argument( + "Prefix length must be an integer between 0 and 32"); + } + + initFromAddressAndPrefixLength(networkPrefix, prefixLen); + } else { + if (!isValidNetmask(netmaskStr)) { + throw std::invalid_argument("Netmask is not valid"); + } + + initFromAddressAndNetmask(networkPrefix, netmaskStr); + } +} + +uint8_t IPv4Network::getPrefixLen() const { + std::bitset<32> bitset(m_Mask); + return bitset.count(); +} + +IPv4Address IPv4Network::getLowestAddress() const { + std::bitset<32> bitset(m_Mask); + return bitset.count() < 32 ? m_NetworkPrefix + htobe32(1) : m_NetworkPrefix; +} + +IPv4Address IPv4Network::getHighestAddress() const { + auto tempAddress = static_cast(m_NetworkPrefix | ~m_Mask); + std::bitset<32> bitset(m_Mask); + return bitset.count() < 32 ? tempAddress - htobe32(1) : tempAddress; +} + +uint64_t IPv4Network::getTotalAddressCount() const { + std::bitset<32> bitset(static_cast(~m_Mask)); + return 1ULL << bitset.count(); +} + +bool IPv4Network::includes(const IPv4Address& address) const { + if (!address.isValid()) { + return false; + } + return (address.toInt() & m_Mask) == m_NetworkPrefix; +} + +bool IPv4Network::includes(const IPv4Network& network) const { + uint32_t lowestAddress = network.m_NetworkPrefix; + uint32_t highestAddress = network.m_NetworkPrefix | ~network.m_Mask; + return ((lowestAddress & m_Mask) == m_NetworkPrefix && + (highestAddress & m_Mask) == m_NetworkPrefix); +} + +std::string IPv4Network::toString() const { + std::ostringstream stream; + stream << getNetworkPrefix() << "/" << static_cast(getPrefixLen()); + return stream.str(); +} + +// ~~~~~~~~~~~ +// IPv6Network +// ~~~~~~~~~~~ #define IPV6_ADDR_SIZE 16 - - bool IPv6Network::isValidNetmask(const std::string &netmask) - { - bool isAllZeros = std::all_of(netmask.begin(), netmask.end(), [](const char &c){ - return (c == '0' || c == ':'); - }); - - if (isAllZeros) - { - return true; - } - - auto mask = IPv6Address(netmask); - if (!mask.isValid()) - { - return false; - } - - const uint8_t *addressAsBytes = mask.toBytes(); - int expectingValue = 1; - for (auto byteIndex = 0; byteIndex < IPV6_ADDR_SIZE; byteIndex++) - { - auto curByte = addressAsBytes[byteIndex]; - if (expectingValue == 1) - { - if (curByte == 0xff) - { - continue; - } - std::bitset<8> bitset(curByte); - if (((curByte << bitset.count()) & 0xff) != 0) - { - return false; - } - expectingValue = 0; - } else if (expectingValue == 0 && curByte != 0) - { - return false; - } - } - - return true; - } - - - void IPv6Network::initFromAddressAndPrefixLength(const IPv6Address &address, uint8_t prefixLen) - { - memset(m_Mask, 0, IPV6_ADDR_SIZE); - int remainingPrefixLen = prefixLen; - for (auto byteIndex = 0; byteIndex < IPV6_ADDR_SIZE; byteIndex++) - { - if (remainingPrefixLen >= 8) - { - m_Mask[byteIndex] = 0xff; - } - else if (remainingPrefixLen > 0) - { - m_Mask[byteIndex] = 0xff << (8 - remainingPrefixLen); - } - else - { - break; - } - - remainingPrefixLen -= 8; - } - - address.copyTo(m_NetworkPrefix); - - for (auto byteIndex = 0; byteIndex < IPV6_ADDR_SIZE; byteIndex++) - { - m_NetworkPrefix[byteIndex] &= m_Mask[byteIndex]; - } - } - - - void IPv6Network::initFromAddressAndNetmask(const IPv6Address &address, const std::string &netmask) - { - IPv6Address netmaskAddr(netmask); - netmaskAddr.copyTo(m_Mask); - - address.copyTo(m_NetworkPrefix); - - for (auto byteIndex = 0; byteIndex < IPV6_ADDR_SIZE; byteIndex++) - { - m_NetworkPrefix[byteIndex] &= m_Mask[byteIndex]; - } - } - - - IPv6Network::IPv6Network(const IPv6Address &address, uint8_t prefixLen) - { - if (!address.isValid()) - { - throw std::invalid_argument("address is not a valid IPv6 address"); - } - - if (prefixLen > 128) - { - throw std::invalid_argument("prefixLen must be an integer between 0 and 128"); - } - - initFromAddressAndPrefixLength(address, prefixLen); - } - - - IPv6Network::IPv6Network(const IPv6Address &address, const std::string &netmask) - { - if (!address.isValid()) - { - throw std::invalid_argument("address is not a valid IPv6 address"); - } - - if (!isValidNetmask(netmask)) - { - throw std::invalid_argument("netmask is not valid"); - } - - initFromAddressAndNetmask(address, netmask); - } - - - IPv6Network::IPv6Network(const std::string &addressAndNetmask) - { - std::stringstream stream(addressAndNetmask); - std::string networkPrefixStr, netmaskStr; - std::getline(stream, networkPrefixStr, '/'); - std::getline(stream, netmaskStr); - - if (netmaskStr.empty()) - { - throw std::invalid_argument("The input should be in the format of
/ or
/"); - } - - auto networkPrefix = IPv6Address(networkPrefixStr); - if (!networkPrefix.isValid()) - { - throw std::invalid_argument("The input doesn't contain a valid IPv6 network prefix"); - } - - if (std::all_of(netmaskStr.begin(), netmaskStr.end(), ::isdigit)) - { - uint32_t prefixLen = std::stoi(netmaskStr); - if (prefixLen > 128) - { - throw std::invalid_argument("Prefix length must be an integer between 0 and 128"); - } - - initFromAddressAndPrefixLength(networkPrefix, prefixLen); - } - else - { - if (!isValidNetmask(netmaskStr)) - { - throw std::invalid_argument("netmask is not valid"); - } - - initFromAddressAndNetmask(networkPrefix, netmaskStr); - } - } - - - uint8_t IPv6Network::getPrefixLen() const - { - uint8_t result = 0; - for (auto byteIndex = 0; byteIndex < IPV6_ADDR_SIZE; byteIndex++) - { - std::bitset<8> bs(m_Mask[byteIndex]); - result += static_cast(bs.count()); - } - return result; - } - - - IPv6Address IPv6Network::getLowestAddress() const - { - if (getPrefixLen() == 128) - { - return m_NetworkPrefix; - } - - uint8_t lowestAddress[IPV6_ADDR_SIZE]; - memcpy(lowestAddress, m_NetworkPrefix, IPV6_ADDR_SIZE); - lowestAddress[IPV6_ADDR_SIZE - 1]++; - return lowestAddress; - } - - - IPv6Address IPv6Network::getHighestAddress() const - { - uint8_t result[IPV6_ADDR_SIZE]; - - for (auto byteIndex = 0; byteIndex < IPV6_ADDR_SIZE; byteIndex++) - { - result[byteIndex] = m_NetworkPrefix[byteIndex] | ~m_Mask[byteIndex]; - } - - return result; - } - - - uint64_t IPv6Network::getTotalAddressCount() const - { - int numOfBitset = 0; - for (auto byteIndex = 0; byteIndex < IPV6_ADDR_SIZE; byteIndex++) - { - std::bitset<8> bitset(static_cast(~m_Mask[byteIndex])); - numOfBitset += bitset.count(); - } - - if (numOfBitset >= 64) - { - throw std::out_of_range("Number of addresses exceeds uint64_t"); - } - return 1ULL << numOfBitset; - } - - - bool IPv6Network::includes(const IPv6Address& address) const - { - if (!address.isValid()) - { - return false; - } - - uint8_t maskedBytes[IPV6_ADDR_SIZE]; - address.copyTo(maskedBytes); - - for (auto byteIndex = 0; byteIndex < IPV6_ADDR_SIZE; byteIndex++) - { - maskedBytes[byteIndex] &= m_Mask[byteIndex]; - } - return memcmp(m_NetworkPrefix, maskedBytes, IPV6_ADDR_SIZE) == 0; - } - - - bool IPv6Network::includes(const IPv6Network& network) const - { - return includes(network.getLowestAddress()) && includes(network.getHighestAddress()); - } - - - std::string IPv6Network::toString() const - { - std::ostringstream stream; - stream << getNetworkPrefix() << "/" << static_cast(getPrefixLen()); - return stream.str(); - } +bool IPv6Network::isValidNetmask(const std::string& netmask) { + bool isAllZeros = + std::all_of(netmask.begin(), netmask.end(), + [](const char& c) { return (c == '0' || c == ':'); }); + + if (isAllZeros) { + return true; + } + + auto mask = IPv6Address(netmask); + if (!mask.isValid()) { + return false; + } + + const uint8_t* addressAsBytes = mask.toBytes(); + int expectingValue = 1; + for (auto byteIndex = 0; byteIndex < IPV6_ADDR_SIZE; byteIndex++) { + auto curByte = addressAsBytes[byteIndex]; + if (expectingValue == 1) { + if (curByte == 0xff) { + continue; + } + std::bitset<8> bitset(curByte); + if (((curByte << bitset.count()) & 0xff) != 0) { + return false; + } + expectingValue = 0; + } else if (expectingValue == 0 && curByte != 0) { + return false; + } + } + + return true; +} + +void IPv6Network::initFromAddressAndPrefixLength(const IPv6Address& address, + uint8_t prefixLen) { + memset(m_Mask, 0, IPV6_ADDR_SIZE); + int remainingPrefixLen = prefixLen; + for (auto byteIndex = 0; byteIndex < IPV6_ADDR_SIZE; byteIndex++) { + if (remainingPrefixLen >= 8) { + m_Mask[byteIndex] = 0xff; + } else if (remainingPrefixLen > 0) { + m_Mask[byteIndex] = 0xff << (8 - remainingPrefixLen); + } else { + break; + } + + remainingPrefixLen -= 8; + } + + address.copyTo(m_NetworkPrefix); + + for (auto byteIndex = 0; byteIndex < IPV6_ADDR_SIZE; byteIndex++) { + m_NetworkPrefix[byteIndex] &= m_Mask[byteIndex]; + } +} + +void IPv6Network::initFromAddressAndNetmask(const IPv6Address& address, + const std::string& netmask) { + IPv6Address netmaskAddr(netmask); + netmaskAddr.copyTo(m_Mask); + + address.copyTo(m_NetworkPrefix); + + for (auto byteIndex = 0; byteIndex < IPV6_ADDR_SIZE; byteIndex++) { + m_NetworkPrefix[byteIndex] &= m_Mask[byteIndex]; + } +} + +IPv6Network::IPv6Network(const IPv6Address& address, uint8_t prefixLen) { + if (!address.isValid()) { + throw std::invalid_argument("address is not a valid IPv6 address"); + } + + if (prefixLen > 128) { + throw std::invalid_argument( + "prefixLen must be an integer between 0 and 128"); + } + + initFromAddressAndPrefixLength(address, prefixLen); +} + +IPv6Network::IPv6Network(const IPv6Address& address, + const std::string& netmask) { + if (!address.isValid()) { + throw std::invalid_argument("address is not a valid IPv6 address"); + } + + if (!isValidNetmask(netmask)) { + throw std::invalid_argument("netmask is not valid"); + } + + initFromAddressAndNetmask(address, netmask); +} + +IPv6Network::IPv6Network(const std::string& addressAndNetmask) { + std::stringstream stream(addressAndNetmask); + std::string networkPrefixStr, netmaskStr; + std::getline(stream, networkPrefixStr, '/'); + std::getline(stream, netmaskStr); + + if (netmaskStr.empty()) { + throw std::invalid_argument( + "The input should be in the format of
/ or " + "
/"); + } + + auto networkPrefix = IPv6Address(networkPrefixStr); + if (!networkPrefix.isValid()) { + throw std::invalid_argument( + "The input doesn't contain a valid IPv6 network prefix"); + } + + if (std::all_of(netmaskStr.begin(), netmaskStr.end(), ::isdigit)) { + uint32_t prefixLen = std::stoi(netmaskStr); + if (prefixLen > 128) { + throw std::invalid_argument( + "Prefix length must be an integer between 0 and 128"); + } + + initFromAddressAndPrefixLength(networkPrefix, prefixLen); + } else { + if (!isValidNetmask(netmaskStr)) { + throw std::invalid_argument("netmask is not valid"); + } + + initFromAddressAndNetmask(networkPrefix, netmaskStr); + } +} + +uint8_t IPv6Network::getPrefixLen() const { + uint8_t result = 0; + for (auto byteIndex = 0; byteIndex < IPV6_ADDR_SIZE; byteIndex++) { + std::bitset<8> bs(m_Mask[byteIndex]); + result += static_cast(bs.count()); + } + return result; +} + +IPv6Address IPv6Network::getLowestAddress() const { + if (getPrefixLen() == 128) { + return m_NetworkPrefix; + } + + uint8_t lowestAddress[IPV6_ADDR_SIZE]; + memcpy(lowestAddress, m_NetworkPrefix, IPV6_ADDR_SIZE); + lowestAddress[IPV6_ADDR_SIZE - 1]++; + return lowestAddress; +} + +IPv6Address IPv6Network::getHighestAddress() const { + uint8_t result[IPV6_ADDR_SIZE]; + + for (auto byteIndex = 0; byteIndex < IPV6_ADDR_SIZE; byteIndex++) { + result[byteIndex] = m_NetworkPrefix[byteIndex] | ~m_Mask[byteIndex]; + } + + return result; +} + +uint64_t IPv6Network::getTotalAddressCount() const { + int numOfBitset = 0; + for (auto byteIndex = 0; byteIndex < IPV6_ADDR_SIZE; byteIndex++) { + std::bitset<8> bitset(static_cast(~m_Mask[byteIndex])); + numOfBitset += bitset.count(); + } + + if (numOfBitset >= 64) { + throw std::out_of_range("Number of addresses exceeds uint64_t"); + } + return 1ULL << numOfBitset; +} + +bool IPv6Network::includes(const IPv6Address& address) const { + if (!address.isValid()) { + return false; + } + + uint8_t maskedBytes[IPV6_ADDR_SIZE]; + address.copyTo(maskedBytes); + + for (auto byteIndex = 0; byteIndex < IPV6_ADDR_SIZE; byteIndex++) { + maskedBytes[byteIndex] &= m_Mask[byteIndex]; + } + return memcmp(m_NetworkPrefix, maskedBytes, IPV6_ADDR_SIZE) == 0; +} + +bool IPv6Network::includes(const IPv6Network& network) const { + return includes(network.getLowestAddress()) && + includes(network.getHighestAddress()); +} + +std::string IPv6Network::toString() const { + std::ostringstream stream; + stream << getNetworkPrefix() << "/" << static_cast(getPrefixLen()); + return stream.str(); +} } // namespace pcpp diff --git a/Common++/src/IpUtils.cpp b/Common++/src/IpUtils.cpp index c78a1807f5..bc1f8532a5 100644 --- a/Common++/src/IpUtils.cpp +++ b/Common++/src/IpUtils.cpp @@ -2,68 +2,62 @@ #include "IpUtils.h" #include "Logger.h" -#include #include +#include #ifndef NS_INADDRSZ -#define NS_INADDRSZ 4 +#define NS_INADDRSZ 4 #endif #ifndef NS_IN6ADDRSZ -#define NS_IN6ADDRSZ 16 +#define NS_IN6ADDRSZ 16 #endif #ifndef NS_INT16SZ -#define NS_INT16SZ 2 +#define NS_INT16SZ 2 #endif -namespace pcpp -{ - namespace internal - { - in_addr* sockaddr2in_addr(struct sockaddr* sa) - { - if (sa == nullptr) - return nullptr; - if (sa->sa_family == AF_INET) - return &(((struct sockaddr_in*)sa)->sin_addr); - PCPP_LOG_DEBUG("sockaddr family is not AF_INET. Returning NULL"); - return nullptr; - } +namespace pcpp { +namespace internal { +in_addr* sockaddr2in_addr(struct sockaddr* sa) { + if (sa == nullptr) + return nullptr; + if (sa->sa_family == AF_INET) + return &(((struct sockaddr_in*)sa)->sin_addr); + PCPP_LOG_DEBUG("sockaddr family is not AF_INET. Returning NULL"); + return nullptr; +} - in6_addr* sockaddr2in6_addr(struct sockaddr* sa) - { - if (sa->sa_family == AF_INET6) - return &(((struct sockaddr_in6*)sa)->sin6_addr); - PCPP_LOG_DEBUG("sockaddr family is not AF_INET6. Returning NULL"); - return nullptr; - } +in6_addr* sockaddr2in6_addr(struct sockaddr* sa) { + if (sa->sa_family == AF_INET6) + return &(((struct sockaddr_in6*)sa)->sin6_addr); + PCPP_LOG_DEBUG("sockaddr family is not AF_INET6. Returning NULL"); + return nullptr; +} - void sockaddr2string(struct sockaddr* sa, char* resultString) - { - in_addr* ipv4Addr = sockaddr2in_addr(sa); - if (ipv4Addr != nullptr) - { - PCPP_LOG_DEBUG("IPv4 packet address"); - inet_ntop(AF_INET, &(((sockaddr_in*)sa)->sin_addr), resultString, INET_ADDRSTRLEN); - } - else - { - PCPP_LOG_DEBUG("Not IPv4 packet address. Assuming IPv6 packet"); - inet_ntop(AF_INET6, &(((sockaddr_in6*)sa)->sin6_addr), resultString, INET6_ADDRSTRLEN); - } - } +void sockaddr2string(struct sockaddr* sa, char* resultString) { + in_addr* ipv4Addr = sockaddr2in_addr(sa); + if (ipv4Addr != nullptr) { + PCPP_LOG_DEBUG("IPv4 packet address"); + inet_ntop(AF_INET, &(((sockaddr_in*)sa)->sin_addr), resultString, + INET_ADDRSTRLEN); + } else { + PCPP_LOG_DEBUG("Not IPv4 packet address. Assuming IPv6 packet"); + inet_ntop(AF_INET6, &(((sockaddr_in6*)sa)->sin6_addr), resultString, + INET6_ADDRSTRLEN); + } +} - uint32_t in_addr2int(in_addr inAddr) - { - #ifdef _WIN32 - return inAddr.S_un.S_addr; - #else - return inAddr.s_addr; - #endif - } - } // namespace internal +uint32_t in_addr2int(in_addr inAddr) { +#ifdef _WIN32 + return inAddr.S_un.S_addr; +#else + return inAddr.s_addr; +#endif +} +} // namespace internal } // namespace pcpp // Only MinGW32 doesn't have these functions (not MinGW-w64 nor Visual C++) -#if defined(_WIN32) && !defined(_MSC_VER) && (!defined(__MINGW64_VERSION_MAJOR) || (__MINGW64_VERSION_MAJOR < 8)) +#if defined(_WIN32) && !defined(_MSC_VER) && \ + (!defined(__MINGW64_VERSION_MAJOR) || (__MINGW64_VERSION_MAJOR < 8)) /* const char * * inet_ntop4(src, dst, size) * format an IPv4 address @@ -75,20 +69,17 @@ namespace pcpp * author: * Paul Vixie, 1996. */ -static const char * -inet_ntop4(const uint8_t* src, char* dst, size_t size) -{ - static const char fmt[] = "%u.%u.%u.%u"; - char tmp[sizeof "255.255.255.255"]; - int nprinted; - nprinted = snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]); - /* Note: nprinted *excludes* the trailing '\0' character */ - if ((size_t)nprinted >= size) - { - return (NULL); - } - strncpy(dst, tmp, size); - return (dst); +static const char* inet_ntop4(const uint8_t* src, char* dst, size_t size) { + static const char fmt[] = "%u.%u.%u.%u"; + char tmp[sizeof "255.255.255.255"]; + int nprinted; + nprinted = snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]); + /* Note: nprinted *excludes* the trailing '\0' character */ + if ((size_t)nprinted >= size) { + return (NULL); + } + strncpy(dst, tmp, size); + return (dst); } /* const char * @@ -97,104 +88,94 @@ inet_ntop4(const uint8_t* src, char* dst, size_t size) * author: * Paul Vixie, 1996. */ -static const char * -inet_ntop6(const uint8_t* src, char* dst, size_t size) -{ - /* - * Note that int32_t and int16_t need only be "at least" large enough - * to contain a value of the specified size. On some systems, like - * Crays, there is no such thing as an integer variable with 16 bits. - * Keep this in mind if you think this function should have been coded - * to use pointer overlays. All the world's not a VAX. - */ - char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp; - struct { int base, len; } best, cur; - u_int words[NS_IN6ADDRSZ / NS_INT16SZ]; - int i; +static const char* inet_ntop6(const uint8_t* src, char* dst, size_t size) { + /* + * Note that int32_t and int16_t need only be "at least" large enough + * to contain a value of the specified size. On some systems, like + * Crays, there is no such thing as an integer variable with 16 bits. + * Keep this in mind if you think this function should have been coded + * to use pointer overlays. All the world's not a VAX. + */ + char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp; + struct { + int base, len; + } best, cur; + u_int words[NS_IN6ADDRSZ / NS_INT16SZ]; + int i; - /* - * Preprocess: - * Copy the input (bytewise) array into a wordwise array. - * Find the longest run of 0x00's in src[] for :: shorthanding. - */ - memset(words, '\0', sizeof words); - for (i = 0; i < NS_IN6ADDRSZ; i++) - words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); - best.base = -1; - best.len = 0; - cur.base = -1; - cur.len = 0; - for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) - { - if (words[i] == 0) - { - if (cur.base == -1) - cur.base = i, cur.len = 1; - else - cur.len++; - } - else - { - if (cur.base != -1) - { - if (best.base == -1 || cur.len > best.len) - best = cur; - cur.base = -1; - } - } - } - if (cur.base != -1) - { - if (best.base == -1 || cur.len > best.len) - best = cur; - } - if (best.base != -1 && best.len < 2) - best.base = -1; + /* + * Preprocess: + * Copy the input (bytewise) array into a wordwise array. + * Find the longest run of 0x00's in src[] for :: shorthanding. + */ + memset(words, '\0', sizeof words); + for (i = 0; i < NS_IN6ADDRSZ; i++) + words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); + best.base = -1; + best.len = 0; + cur.base = -1; + cur.len = 0; + for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { + if (words[i] == 0) { + if (cur.base == -1) + cur.base = i, cur.len = 1; + else + cur.len++; + } else { + if (cur.base != -1) { + if (best.base == -1 || cur.len > best.len) + best = cur; + cur.base = -1; + } + } + } + if (cur.base != -1) { + if (best.base == -1 || cur.len > best.len) + best = cur; + } + if (best.base != -1 && best.len < 2) + best.base = -1; - /* - * Format the result. - */ - tp = tmp; - for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) - { - /* Are we inside the best run of 0x00's? */ - if (best.base != -1 && i >= best.base && i < (best.base + best.len)) - { - if (i == best.base) - *tp++ = ':'; - continue; - } - /* Are we following an initial run of 0x00s or any real hex? */ - if (i != 0) - *tp++ = ':'; - /* Is this address an encapsulated IPv4? */ - if (i == 6 && best.base == 0 && (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) - { - if (!inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp))) - return (NULL); - tp += strlen(tp); - break; - } - tp += snprintf(tp, (unsigned long) (sizeof tmp - (tp - tmp)), "%x", words[i]); - } - /* Was it a trailing run of 0x00's? */ - if (best.base != -1 && (best.base + best.len) == - (NS_IN6ADDRSZ / NS_INT16SZ)) - *tp++ = ':'; - *tp++ = '\0'; + /* + * Format the result. + */ + tp = tmp; + for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { + /* Are we inside the best run of 0x00's? */ + if (best.base != -1 && i >= best.base && i < (best.base + best.len)) { + if (i == best.base) + *tp++ = ':'; + continue; + } + /* Are we following an initial run of 0x00s or any real hex? */ + if (i != 0) + *tp++ = ':'; + /* Is this address an encapsulated IPv4? */ + if (i == 6 && best.base == 0 && + (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) { + if (!inet_ntop4(src + 12, tp, sizeof tmp - (tp - tmp))) + return (NULL); + tp += strlen(tp); + break; + } + tp += + snprintf(tp, (unsigned long)(sizeof tmp - (tp - tmp)), "%x", words[i]); + } + /* Was it a trailing run of 0x00's? */ + if (best.base != -1 && (best.base + best.len) == (NS_IN6ADDRSZ / NS_INT16SZ)) + *tp++ = ':'; + *tp++ = '\0'; - /* - * Check for overflow, copy, and we're done. - */ - if ((size_t)(tp - tmp) > size) - { - return (NULL); - } - strncpy(dst, tmp, size); - return (dst); + /* + * Check for overflow, copy, and we're done. + */ + if ((size_t)(tp - tmp) > size) { + return (NULL); + } + strncpy(dst, tmp, size); + return (dst); } - /* int * inet_pton4(src, dst) * like inet_aton() but without all the hexadecimal and shorthand. @@ -205,47 +186,40 @@ inet_ntop6(const uint8_t* src, char* dst, size_t size) * author: * Paul Vixie, 1996. */ -static int -inet_pton4(const char* src, uint8_t* dst) -{ - static const char digits[] = "0123456789"; - int saw_digit, octets, ch; - u_char tmp[NS_INADDRSZ], *tp; +static int inet_pton4(const char* src, uint8_t* dst) { + static const char digits[] = "0123456789"; + int saw_digit, octets, ch; + u_char tmp[NS_INADDRSZ], *tp; - saw_digit = 0; - octets = 0; - *(tp = tmp) = 0; - while ((ch = *src++) != '\0') - { - const char *pch; + saw_digit = 0; + octets = 0; + *(tp = tmp) = 0; + while ((ch = *src++) != '\0') { + const char* pch; - if ((pch = strchr(digits, ch)) != NULL) - { - size_t newSize = *tp * 10 + (pch - digits); + if ((pch = strchr(digits, ch)) != NULL) { + size_t newSize = *tp * 10 + (pch - digits); - if (newSize > 255) - return (0); - *tp = (u_char) newSize; - if (! saw_digit) - { - if (++octets > 4) - return (0); - saw_digit = 1; - } - } - else if (ch == '.' && saw_digit) - { - if (octets == 4) - return (0); - *++tp = 0; - saw_digit = 0; - } else - return (0); - } - if (octets < 4) - return (0); - memcpy(dst, tmp, NS_INADDRSZ); - return (1); + if (newSize > 255) + return (0); + *tp = (u_char)newSize; + if (!saw_digit) { + if (++octets > 4) + return (0); + saw_digit = 1; + } + } else if (ch == '.' && saw_digit) { + if (octets == 4) + return (0); + *++tp = 0; + saw_digit = 0; + } else + return (0); + } + if (octets < 4) + return (0); + memcpy(dst, tmp, NS_INADDRSZ); + return (1); } /* int @@ -261,133 +235,117 @@ inet_pton4(const char* src, uint8_t* dst) * author: * Paul Vixie, 1996. */ -static int -inet_pton6(const char* src, uint8_t* dst) -{ - static const char xdigits_l[] = "0123456789abcdef", - xdigits_u[] = "0123456789ABCDEF"; - u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp; - const char *curtok; - int ch, saw_xdigit; - u_int val; +static int inet_pton6(const char* src, uint8_t* dst) { + static const char xdigits_l[] = "0123456789abcdef", + xdigits_u[] = "0123456789ABCDEF"; + u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp; + const char* curtok; + int ch, saw_xdigit; + u_int val; - memset((tp = tmp), '\0', NS_IN6ADDRSZ); - endp = tp + NS_IN6ADDRSZ; - colonp = NULL; - /* Leading :: requires some special handling. */ - if (*src == ':') - if (*++src != ':') - return (0); - curtok = src; - saw_xdigit = 0; - val = 0; - while ((ch = *src++) != '\0') - { - const char* pch, *xdigits; + memset((tp = tmp), '\0', NS_IN6ADDRSZ); + endp = tp + NS_IN6ADDRSZ; + colonp = NULL; + /* Leading :: requires some special handling. */ + if (*src == ':') + if (*++src != ':') + return (0); + curtok = src; + saw_xdigit = 0; + val = 0; + while ((ch = *src++) != '\0') { + const char *pch, *xdigits; - if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL) - pch = strchr((xdigits = xdigits_u), ch); - if (pch != NULL) - { - val <<= 4; - val |= (pch - xdigits); - if (val > 0xffff) - return (0); - saw_xdigit = 1; - continue; - } - if (ch == ':') - { - curtok = src; - if (!saw_xdigit) - { - if (colonp) - return (0); - colonp = tp; - continue; - } - else if (*src == '\0') - { - return (0); - } - if (tp + NS_INT16SZ > endp) - return (0); - *tp++ = (u_char) (val >> 8) & 0xff; - *tp++ = (u_char) val & 0xff; - saw_xdigit = 0; - val = 0; - continue; - } - if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) && inet_pton4(curtok, tp) > 0) - { - tp += NS_INADDRSZ; - saw_xdigit = 0; - break; /* '\0' was seen by inet_pton4(). */ - } - return (0); - } - if (saw_xdigit) - { - if (tp + NS_INT16SZ > endp) - return (0); - *tp++ = (u_char) (val >> 8) & 0xff; - *tp++ = (u_char) val & 0xff; - } - if (colonp != NULL) - { - /* - * Since some memmove()'s erroneously fail to handle - * overlapping regions, we'll do the shift by hand. - */ - const int n = (int) (tp - colonp); - int i; + if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL) + pch = strchr((xdigits = xdigits_u), ch); + if (pch != NULL) { + val <<= 4; + val |= (pch - xdigits); + if (val > 0xffff) + return (0); + saw_xdigit = 1; + continue; + } + if (ch == ':') { + curtok = src; + if (!saw_xdigit) { + if (colonp) + return (0); + colonp = tp; + continue; + } else if (*src == '\0') { + return (0); + } + if (tp + NS_INT16SZ > endp) + return (0); + *tp++ = (u_char)(val >> 8) & 0xff; + *tp++ = (u_char)val & 0xff; + saw_xdigit = 0; + val = 0; + continue; + } + if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) && + inet_pton4(curtok, tp) > 0) { + tp += NS_INADDRSZ; + saw_xdigit = 0; + break; /* '\0' was seen by inet_pton4(). */ + } + return (0); + } + if (saw_xdigit) { + if (tp + NS_INT16SZ > endp) + return (0); + *tp++ = (u_char)(val >> 8) & 0xff; + *tp++ = (u_char)val & 0xff; + } + if (colonp != NULL) { + /* + * Since some memmove()'s erroneously fail to handle + * overlapping regions, we'll do the shift by hand. + */ + const int n = (int)(tp - colonp); + int i; - if (tp == endp) - return (0); - for (i = 1; i <= n; i++) - { - endp[- i] = colonp[n - i]; - colonp[n - i] = 0; - } - tp = endp; - } - if (tp != endp) - return (0); - memcpy(dst, tmp, NS_IN6ADDRSZ); - return (1); + if (tp == endp) + return (0); + for (i = 1; i <= n; i++) { + endp[-i] = colonp[n - i]; + colonp[n - i] = 0; + } + tp = endp; + } + if (tp != endp) + return (0); + memcpy(dst, tmp, NS_IN6ADDRSZ); + return (1); } - -const char* inet_ntop(int af, const void* src, char* dst, size_t size) -{ - switch (af) - { - case AF_INET: - return (inet_ntop4((const uint8_t*)src, dst, size)); - case AF_INET6: - return (inet_ntop6((const uint8_t*)src, dst, size)); - default: - return (NULL); - } - /* NOTREACHED */ +const char* inet_ntop(int af, const void* src, char* dst, size_t size) { + switch (af) { + case AF_INET: + return (inet_ntop4((const uint8_t*)src, dst, size)); + case AF_INET6: + return (inet_ntop6((const uint8_t*)src, dst, size)); + default: + return (NULL); + } + /* NOTREACHED */ } -int inet_pton(int af, const char* src, void* dst) -{ - switch (af) - { +int inet_pton(int af, const char* src, void* dst) { + switch (af) { #ifdef AF_INET - case AF_INET: - return (inet_pton4(src, (uint8_t*)dst)); + case AF_INET: + return (inet_pton4(src, (uint8_t*)dst)); #endif #ifdef AF_INET6 - case AF_INET6: - return (inet_pton6(src, (uint8_t*)dst)); + case AF_INET6: + return (inet_pton6(src, (uint8_t*)dst)); #endif - default: - return (-1); - } - /* NOTREACHED */ + default: + return (-1); + } + /* NOTREACHED */ } #endif diff --git a/Common++/src/Logger.cpp b/Common++/src/Logger.cpp index f78641caff..c6ead53128 100644 --- a/Common++/src/Logger.cpp +++ b/Common++/src/Logger.cpp @@ -1,58 +1,51 @@ -#include #include "Logger.h" +#include -namespace pcpp -{ +namespace pcpp { -Logger::Logger() : m_LogsEnabled(true), m_LogPrinter(&defaultLogPrinter) -{ - m_LastError.reserve(200); - for (int i = 0; istr(); - delete logStream; - if (logLevel == Logger::Error) - { - m_LastError = logMessage; - } - if (m_LogsEnabled) - { - m_LogPrinter(logLevel, logMessage, file, method, line); - } +void Logger::internalPrintLogMessage(std::ostringstream* logStream, + Logger::LogLevel logLevel, + const char* file, const char* method, + int line) { + std::string logMessage = logStream->str(); + delete logStream; + if (logLevel == Logger::Error) { + m_LastError = logMessage; + } + if (m_LogsEnabled) { + m_LogPrinter(logLevel, logMessage, file, method, line); + } } } // namespace pcpp diff --git a/Common++/src/MacAddress.cpp b/Common++/src/MacAddress.cpp index 1e3a7d2a14..ddb673762c 100644 --- a/Common++/src/MacAddress.cpp +++ b/Common++/src/MacAddress.cpp @@ -3,44 +3,43 @@ #include "MacAddress.h" -namespace pcpp -{ +namespace pcpp { -MacAddress MacAddress::Zero(0,0,0,0,0,0); +MacAddress MacAddress::Zero(0, 0, 0, 0, 0, 0); -std::string MacAddress::toString() const -{ - char str[19]; - snprintf(str, sizeof str, "%02x:%02x:%02x:%02x:%02x:%02x", m_Address[0], m_Address[1], m_Address[2], m_Address[3], m_Address[4], m_Address[5]); - return std::string(str); +std::string MacAddress::toString() const { + char str[19]; + snprintf(str, sizeof str, "%02x:%02x:%02x:%02x:%02x:%02x", m_Address[0], + m_Address[1], m_Address[2], m_Address[3], m_Address[4], + m_Address[5]); + return std::string(str); } -void MacAddress::init(const char* addr) -{ - const unsigned int addrLen = sizeof m_Address; - unsigned int i = 0; - - for(; *addr != 0 && i < addrLen; ++i) - { - char byte[3]; - memset(byte, 0, sizeof byte); - byte[0] = *addr++; - if(*addr == '\0') break; - byte[1] = *addr++; - if(*addr != '\0') // holds the ":" char or end of string - ++addr; // ignore the ":" char - m_Address[i] = static_cast(strtol(byte, nullptr, 16)); - - // The strtol function returns zero value in two cases: when an error occurs or the string '00' is converted. - // This code verifies that it's the second case. - if(m_Address[i] == 0 && (byte[0] != '0' || byte[1] != '0')) - { - m_IsValid = false; - return; - } - } - - m_IsValid = (i == addrLen && *addr == '\0'); +void MacAddress::init(const char* addr) { + const unsigned int addrLen = sizeof m_Address; + unsigned int i = 0; + + for (; *addr != 0 && i < addrLen; ++i) { + char byte[3]; + memset(byte, 0, sizeof byte); + byte[0] = *addr++; + if (*addr == '\0') + break; + byte[1] = *addr++; + if (*addr != '\0') // holds the ":" char or end of string + ++addr; // ignore the ":" char + m_Address[i] = static_cast(strtol(byte, nullptr, 16)); + + // The strtol function returns zero value in two cases: when an error occurs + // or the string '00' is converted. This code verifies that it's the second + // case. + if (m_Address[i] == 0 && (byte[0] != '0' || byte[1] != '0')) { + m_IsValid = false; + return; + } + } + + m_IsValid = (i == addrLen && *addr == '\0'); } } // namespace pcpp diff --git a/Common++/src/OUILookup.cpp b/Common++/src/OUILookup.cpp index 0d5f5fc4e4..fa54ad1bbc 100644 --- a/Common++/src/OUILookup.cpp +++ b/Common++/src/OUILookup.cpp @@ -5,106 +5,97 @@ #include -namespace pcpp -{ +namespace pcpp { template -int64_t OUILookup::internalParser(T &jsonData) -{ - // Clear all entries before adding - vendorMap.clear(); - - int64_t ctrRead = 0; - nlohmann::json parsedJson = nlohmann::json::parse(jsonData); - for (const auto &line : parsedJson.items()) - { - if (!(line.value().is_object())) - continue; - auto val = line.value().get(); - if (!(val.contains("vendor"))) - continue; - - std::vector vLocalMaskedFilter; - if (val.contains("maskedFilters") && val["maskedFilters"].is_array()) - { - // Iterate through masked filters - for (const auto &entry : val["maskedFilters"]) - { - if (!entry.is_object()) - continue; - auto subVal = entry.get(); - if (subVal.contains("mask") && subVal.contains("vendors") && subVal["mask"].is_number_integer() && - subVal["vendors"].is_object()) - { - int maskValue = subVal["mask"].get(); - vLocalMaskedFilter.push_back({maskValue, {}}); - - // Parse masked filter - for (const auto &subentry : subVal["vendors"].items()) - { - if (subentry.value().is_string()) - { - vLocalMaskedFilter.back().vendorMap.insert( - {std::stoull(subentry.key()), subentry.value()}); - ++ctrRead; - } - } - } - } - } - - vendorMap.insert({std::stoull(line.key()), {val["vendor"], vLocalMaskedFilter}}); - ++ctrRead; - } - - PCPP_LOG_DEBUG(std::to_string(ctrRead) + " vendors read successfully"); - return ctrRead; +int64_t OUILookup::internalParser(T& jsonData) { + // Clear all entries before adding + vendorMap.clear(); + + int64_t ctrRead = 0; + nlohmann::json parsedJson = nlohmann::json::parse(jsonData); + for (const auto& line : parsedJson.items()) { + if (!(line.value().is_object())) + continue; + auto val = line.value().get(); + if (!(val.contains("vendor"))) + continue; + + std::vector vLocalMaskedFilter; + if (val.contains("maskedFilters") && val["maskedFilters"].is_array()) { + // Iterate through masked filters + for (const auto& entry : val["maskedFilters"]) { + if (!entry.is_object()) + continue; + auto subVal = entry.get(); + if (subVal.contains("mask") && subVal.contains("vendors") && + subVal["mask"].is_number_integer() && + subVal["vendors"].is_object()) { + int maskValue = subVal["mask"].get(); + vLocalMaskedFilter.push_back({maskValue, {}}); + + // Parse masked filter + for (const auto& subentry : subVal["vendors"].items()) { + if (subentry.value().is_string()) { + vLocalMaskedFilter.back().vendorMap.insert( + {std::stoull(subentry.key()), subentry.value()}); + ++ctrRead; + } + } + } + } + } + + vendorMap.insert( + {std::stoull(line.key()), {val["vendor"], vLocalMaskedFilter}}); + ++ctrRead; + } + + PCPP_LOG_DEBUG(std::to_string(ctrRead) + " vendors read successfully"); + return ctrRead; } -int64_t OUILookup::initOUIDatabaseFromJson(const std::string &path) -{ - std::ifstream dataFile; +int64_t OUILookup::initOUIDatabaseFromJson(const std::string& path) { + std::ifstream dataFile; - // Open database - dataFile.open(path); - if (!dataFile.is_open()) - { - PCPP_LOG_ERROR(std::string("Can't open OUI database: ") + strerror(errno)); - return -1; - } + // Open database + dataFile.open(path); + if (!dataFile.is_open()) { + PCPP_LOG_ERROR(std::string("Can't open OUI database: ") + strerror(errno)); + return -1; + } - // Parse values - return internalParser(dataFile); + // Parse values + return internalParser(dataFile); } -std::string OUILookup::getVendorName(const pcpp::MacAddress &addr) -{ - if (vendorMap.empty()) - PCPP_LOG_DEBUG("Vendor map is empty"); +std::string OUILookup::getVendorName(const pcpp::MacAddress& addr) { + if (vendorMap.empty()) + PCPP_LOG_DEBUG("Vendor map is empty"); - // Get MAC address - uint8_t buffArray[6]; - addr.copyTo(buffArray); + // Get MAC address + uint8_t buffArray[6]; + addr.copyTo(buffArray); - uint64_t macAddr = (((uint64_t)((buffArray)[5]) << 0) + ((uint64_t)((buffArray)[4]) << 8) + - ((uint64_t)((buffArray)[3]) << 16) + ((uint64_t)((buffArray)[2]) << 24) + - ((uint64_t)((buffArray)[1]) << 32) + ((uint64_t)((buffArray)[0]) << 40)); + uint64_t macAddr = + (((uint64_t)((buffArray)[5]) << 0) + ((uint64_t)((buffArray)[4]) << 8) + + ((uint64_t)((buffArray)[3]) << 16) + ((uint64_t)((buffArray)[2]) << 24) + + ((uint64_t)((buffArray)[1]) << 32) + ((uint64_t)((buffArray)[0]) << 40)); - auto itr = vendorMap.find(macAddr >> 24); - if (itr == vendorMap.end()) - return "Unknown"; + auto itr = vendorMap.find(macAddr >> 24); + if (itr == vendorMap.end()) + return "Unknown"; - for (const auto &entry : itr->second.maskedFilter) - { - uint64_t maskValue = ~((1 << (48 - entry.mask)) - 1); - uint64_t bufferAddr = macAddr & maskValue; + for (const auto& entry : itr->second.maskedFilter) { + uint64_t maskValue = ~((1 << (48 - entry.mask)) - 1); + uint64_t bufferAddr = macAddr & maskValue; - auto subItr = entry.vendorMap.find(bufferAddr); - if (subItr != entry.vendorMap.end()) - return subItr->second; - } + auto subItr = entry.vendorMap.find(bufferAddr); + if (subItr != entry.vendorMap.end()) + return subItr->second; + } - return itr->second.vendorName; + return itr->second.vendorName; } } // namespace pcpp diff --git a/Common++/src/PcapPlusPlusVersion.cpp b/Common++/src/PcapPlusPlusVersion.cpp index 4038147395..4fc739ef8b 100644 --- a/Common++/src/PcapPlusPlusVersion.cpp +++ b/Common++/src/PcapPlusPlusVersion.cpp @@ -1,27 +1,23 @@ #include "PcapPlusPlusVersion.h" -namespace pcpp -{ +namespace pcpp { -std::string getGitCommit() -{ - #ifdef GIT_COMMIT - return GIT_COMMIT; - #endif - return "unavailable"; +std::string getGitCommit() { +#ifdef GIT_COMMIT + return GIT_COMMIT; +#endif + return "unavailable"; } -std::string getGitBranch() -{ - #ifdef GIT_BRANCH - return GIT_BRANCH; - #endif - return "unavailable"; +std::string getGitBranch() { +#ifdef GIT_BRANCH + return GIT_BRANCH; +#endif + return "unavailable"; } -std::string getGitInfo() -{ - return "Git branch '" + getGitBranch() + "', commit '" + getGitCommit() + "'"; +std::string getGitInfo() { + return "Git branch '" + getGitBranch() + "', commit '" + getGitCommit() + "'"; } -} +} // namespace pcpp diff --git a/Common++/src/SystemUtils.cpp b/Common++/src/SystemUtils.cpp index 664b15a7b7..5ca61f2b5f 100644 --- a/Common++/src/SystemUtils.cpp +++ b/Common++/src/SystemUtils.cpp @@ -28,358 +28,304 @@ #endif #ifdef _MSC_VER -int gettimeofday(struct timeval * tp, struct timezone * tzp) -{ - // Note: some broken versions only have 8 trailing zero's, the correct epoch has 9 trailing zero's - static const uint64_t EPOCH = ((uint64_t)116444736000000000ULL); - - SYSTEMTIME system_time; - FILETIME file_time; - uint64_t time; - - GetSystemTime(&system_time); - SystemTimeToFileTime(&system_time, &file_time); - time = ((uint64_t)file_time.dwLowDateTime); - time += ((uint64_t)file_time.dwHighDateTime) << 32; - - tp->tv_sec = (long)((time - EPOCH) / 10000000L); - tp->tv_usec = (long)(system_time.wMilliseconds * 1000); - return 0; +int gettimeofday(struct timeval* tp, struct timezone* tzp) { + // Note: some broken versions only have 8 trailing zero's, the correct epoch + // has 9 trailing zero's + static const uint64_t EPOCH = ((uint64_t)116444736000000000ULL); + + SYSTEMTIME system_time; + FILETIME file_time; + uint64_t time; + + GetSystemTime(&system_time); + SystemTimeToFileTime(&system_time, &file_time); + time = ((uint64_t)file_time.dwLowDateTime); + time += ((uint64_t)file_time.dwHighDateTime) << 32; + + tp->tv_sec = (long)((time - EPOCH) / 10000000L); + tp->tv_usec = (long)(system_time.wMilliseconds * 1000); + return 0; } #endif -namespace pcpp -{ - -const SystemCore SystemCores::Core0 = { 0x01, 0 }; -const SystemCore SystemCores::Core1 = { 0x02, 1 }; -const SystemCore SystemCores::Core2 = { 0x04, 2 }; -const SystemCore SystemCores::Core3 = { 0x08, 3 }; -const SystemCore SystemCores::Core4 = { 0x10, 4 }; -const SystemCore SystemCores::Core5 = { 0x20, 5 }; -const SystemCore SystemCores::Core6 = { 0x40, 6 }; -const SystemCore SystemCores::Core7 = { 0x80, 7 }; -const SystemCore SystemCores::Core8 = { 0x100, 8 }; -const SystemCore SystemCores::Core9 = { 0x200, 9 }; -const SystemCore SystemCores::Core10 = { 0x400, 10 }; -const SystemCore SystemCores::Core11 = { 0x800, 11 }; -const SystemCore SystemCores::Core12 = { 0x1000, 12 }; -const SystemCore SystemCores::Core13 = { 0x2000, 13 }; -const SystemCore SystemCores::Core14 = { 0x4000, 14 }; -const SystemCore SystemCores::Core15 = { 0x8000, 15 }; -const SystemCore SystemCores::Core16 = { 0x10000, 16 }; -const SystemCore SystemCores::Core17 = { 0x20000, 17 }; -const SystemCore SystemCores::Core18 = { 0x40000, 18 }; -const SystemCore SystemCores::Core19 = { 0x80000, 19 }; -const SystemCore SystemCores::Core20 = { 0x100000, 20 }; -const SystemCore SystemCores::Core21 = { 0x200000, 21 }; -const SystemCore SystemCores::Core22 = { 0x400000, 22 }; -const SystemCore SystemCores::Core23 = { 0x800000, 23 }; -const SystemCore SystemCores::Core24 = { 0x1000000, 24 }; -const SystemCore SystemCores::Core25 = { 0x2000000, 25 }; -const SystemCore SystemCores::Core26 = { 0x4000000, 26 }; -const SystemCore SystemCores::Core27 = { 0x8000000, 27 }; -const SystemCore SystemCores::Core28 = { 0x10000000, 28 }; -const SystemCore SystemCores::Core29 = { 0x20000000, 29 }; -const SystemCore SystemCores::Core30 = { 0x40000000, 30 }; -const SystemCore SystemCores::Core31 = { 0x80000000, 31 }; - -const SystemCore SystemCores::IdToSystemCore[MAX_NUM_OF_CORES] = -{ - SystemCores::Core0, - SystemCores::Core1, - SystemCores::Core2, - SystemCores::Core3, - SystemCores::Core4, - SystemCores::Core5, - SystemCores::Core6, - SystemCores::Core7, - SystemCores::Core8, - SystemCores::Core9, - SystemCores::Core10, - SystemCores::Core11, - SystemCores::Core12, - SystemCores::Core13, - SystemCores::Core14, - SystemCores::Core15, - SystemCores::Core16, - SystemCores::Core17, - SystemCores::Core18, - SystemCores::Core19, - SystemCores::Core20, - SystemCores::Core21, - SystemCores::Core22, - SystemCores::Core23, - SystemCores::Core24, - SystemCores::Core25, - SystemCores::Core26, - SystemCores::Core27, - SystemCores::Core28, - SystemCores::Core29, - SystemCores::Core30, - SystemCores::Core31 -}; - - -int getNumOfCores() -{ +namespace pcpp { + +const SystemCore SystemCores::Core0 = {0x01, 0}; +const SystemCore SystemCores::Core1 = {0x02, 1}; +const SystemCore SystemCores::Core2 = {0x04, 2}; +const SystemCore SystemCores::Core3 = {0x08, 3}; +const SystemCore SystemCores::Core4 = {0x10, 4}; +const SystemCore SystemCores::Core5 = {0x20, 5}; +const SystemCore SystemCores::Core6 = {0x40, 6}; +const SystemCore SystemCores::Core7 = {0x80, 7}; +const SystemCore SystemCores::Core8 = {0x100, 8}; +const SystemCore SystemCores::Core9 = {0x200, 9}; +const SystemCore SystemCores::Core10 = {0x400, 10}; +const SystemCore SystemCores::Core11 = {0x800, 11}; +const SystemCore SystemCores::Core12 = {0x1000, 12}; +const SystemCore SystemCores::Core13 = {0x2000, 13}; +const SystemCore SystemCores::Core14 = {0x4000, 14}; +const SystemCore SystemCores::Core15 = {0x8000, 15}; +const SystemCore SystemCores::Core16 = {0x10000, 16}; +const SystemCore SystemCores::Core17 = {0x20000, 17}; +const SystemCore SystemCores::Core18 = {0x40000, 18}; +const SystemCore SystemCores::Core19 = {0x80000, 19}; +const SystemCore SystemCores::Core20 = {0x100000, 20}; +const SystemCore SystemCores::Core21 = {0x200000, 21}; +const SystemCore SystemCores::Core22 = {0x400000, 22}; +const SystemCore SystemCores::Core23 = {0x800000, 23}; +const SystemCore SystemCores::Core24 = {0x1000000, 24}; +const SystemCore SystemCores::Core25 = {0x2000000, 25}; +const SystemCore SystemCores::Core26 = {0x4000000, 26}; +const SystemCore SystemCores::Core27 = {0x8000000, 27}; +const SystemCore SystemCores::Core28 = {0x10000000, 28}; +const SystemCore SystemCores::Core29 = {0x20000000, 29}; +const SystemCore SystemCores::Core30 = {0x40000000, 30}; +const SystemCore SystemCores::Core31 = {0x80000000, 31}; + +const SystemCore SystemCores::IdToSystemCore[MAX_NUM_OF_CORES] = { + SystemCores::Core0, SystemCores::Core1, SystemCores::Core2, + SystemCores::Core3, SystemCores::Core4, SystemCores::Core5, + SystemCores::Core6, SystemCores::Core7, SystemCores::Core8, + SystemCores::Core9, SystemCores::Core10, SystemCores::Core11, + SystemCores::Core12, SystemCores::Core13, SystemCores::Core14, + SystemCores::Core15, SystemCores::Core16, SystemCores::Core17, + SystemCores::Core18, SystemCores::Core19, SystemCores::Core20, + SystemCores::Core21, SystemCores::Core22, SystemCores::Core23, + SystemCores::Core24, SystemCores::Core25, SystemCores::Core26, + SystemCores::Core27, SystemCores::Core28, SystemCores::Core29, + SystemCores::Core30, SystemCores::Core31}; + +int getNumOfCores() { #if defined(_WIN32) - SYSTEM_INFO sysinfo; - GetSystemInfo( &sysinfo ); - return sysinfo.dwNumberOfProcessors; + SYSTEM_INFO sysinfo; + GetSystemInfo(&sysinfo); + return sysinfo.dwNumberOfProcessors; #else - return sysconf(_SC_NPROCESSORS_ONLN); + return sysconf(_SC_NPROCESSORS_ONLN); #endif } -CoreMask getCoreMaskForAllMachineCores() -{ - int numOfCores = getNumOfCores() < 32 ? getNumOfCores() : 32; - CoreMask result = 0; - for (int i = 0; i < numOfCores; i++) - { - result = result | SystemCores::IdToSystemCore[i].Mask; - } +CoreMask getCoreMaskForAllMachineCores() { + int numOfCores = getNumOfCores() < 32 ? getNumOfCores() : 32; + CoreMask result = 0; + for (int i = 0; i < numOfCores; i++) { + result = result | SystemCores::IdToSystemCore[i].Mask; + } - return result; + return result; } -CoreMask createCoreMaskFromCoreVector(std::vector cores) -{ - CoreMask result = 0; - for (std::vector::iterator iter = cores.begin(); iter != cores.end(); iter++) - { - result |= iter->Mask; - } +CoreMask createCoreMaskFromCoreVector(std::vector cores) { + CoreMask result = 0; + for (std::vector::iterator iter = cores.begin(); + iter != cores.end(); iter++) { + result |= iter->Mask; + } - return result; + return result; } -CoreMask createCoreMaskFromCoreIds(std::vector coreIds) -{ - CoreMask result = 0; - for (std::vector::iterator iter = coreIds.begin(); iter != coreIds.end(); iter++) - { - result |= SystemCores::IdToSystemCore[*iter].Mask; - } +CoreMask createCoreMaskFromCoreIds(std::vector coreIds) { + CoreMask result = 0; + for (std::vector::iterator iter = coreIds.begin(); iter != coreIds.end(); + iter++) { + result |= SystemCores::IdToSystemCore[*iter].Mask; + } - return result; + return result; } -void createCoreVectorFromCoreMask(CoreMask coreMask, std::vector& resultVec) -{ - int i = 0; - while (coreMask != 0) - { - if (1 & coreMask) - { - resultVec.push_back(SystemCores::IdToSystemCore[i]); - } - - coreMask = coreMask >> 1; - i++; - } +void createCoreVectorFromCoreMask(CoreMask coreMask, + std::vector& resultVec) { + int i = 0; + while (coreMask != 0) { + if (1 & coreMask) { + resultVec.push_back(SystemCores::IdToSystemCore[i]); + } + + coreMask = coreMask >> 1; + i++; + } } -std::string executeShellCommand(const std::string &command) -{ - FILE* pipe = POPEN(command.c_str(), "r"); - if (!pipe) return "ERROR"; - char buffer[128]; - std::string result = ""; - while(!feof(pipe)) - { - if(fgets(buffer, 128, pipe) != nullptr) - result += buffer; - } - PCLOSE(pipe); - return result; +std::string executeShellCommand(const std::string& command) { + FILE* pipe = POPEN(command.c_str(), "r"); + if (!pipe) + return "ERROR"; + char buffer[128]; + std::string result = ""; + while (!feof(pipe)) { + if (fgets(buffer, 128, pipe) != nullptr) + result += buffer; + } + PCLOSE(pipe); + return result; } +bool directoryExists(const std::string& dirPath) { + struct stat info; -bool directoryExists(const std::string &dirPath) -{ - struct stat info; - - if (stat(dirPath.c_str(), &info) != 0) - return false; - else if(info.st_mode & S_IFDIR) - return true; - else - return false; + if (stat(dirPath.c_str(), &info) != 0) + return false; + else if (info.st_mode & S_IFDIR) + return true; + else + return false; } - -int clockGetTime(long& sec, long& nsec) -{ - sec = 0; - nsec = 0; +int clockGetTime(long& sec, long& nsec) { + sec = 0; + nsec = 0; #if defined(_WIN32) #define CLOCK_GETTIME_BILLION (1E9) - static BOOL clock_gettime_first_time = 1; - static LARGE_INTEGER clock_gettime_counts_per_sec; + static BOOL clock_gettime_first_time = 1; + static LARGE_INTEGER clock_gettime_counts_per_sec; - LARGE_INTEGER count ; + LARGE_INTEGER count; - if (clock_gettime_first_time) - { - clock_gettime_first_time = 0; + if (clock_gettime_first_time) { + clock_gettime_first_time = 0; - if (0 == QueryPerformanceFrequency(&clock_gettime_counts_per_sec)) - { - clock_gettime_counts_per_sec.QuadPart = 0; - } - } + if (0 == QueryPerformanceFrequency(&clock_gettime_counts_per_sec)) { + clock_gettime_counts_per_sec.QuadPart = 0; + } + } - if ((clock_gettime_counts_per_sec.QuadPart <= 0) || (0 == QueryPerformanceCounter(&count))) - { - return -1; - } + if ((clock_gettime_counts_per_sec.QuadPart <= 0) || + (0 == QueryPerformanceCounter(&count))) { + return -1; + } - sec = count.QuadPart / clock_gettime_counts_per_sec.QuadPart; - nsec = ((count.QuadPart % clock_gettime_counts_per_sec.QuadPart) * CLOCK_GETTIME_BILLION) / clock_gettime_counts_per_sec.QuadPart; + sec = count.QuadPart / clock_gettime_counts_per_sec.QuadPart; + nsec = ((count.QuadPart % clock_gettime_counts_per_sec.QuadPart) * + CLOCK_GETTIME_BILLION) / + clock_gettime_counts_per_sec.QuadPart; - return 0; + return 0; #elif defined(__APPLE__) - clock_serv_t cclock; - mach_timespec_t mts; - host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); - clock_get_time(cclock, &mts); - mach_port_deallocate(mach_task_self(), cclock); - sec = mts.tv_sec; - nsec = mts.tv_nsec; + clock_serv_t cclock; + mach_timespec_t mts; + host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); + clock_get_time(cclock, &mts); + mach_port_deallocate(mach_task_self(), cclock); + sec = mts.tv_sec; + nsec = mts.tv_nsec; - return 0; + return 0; #else // Linux - #include +#include - timespec ts; - int res = clock_gettime(CLOCK_REALTIME, &ts); - if (res == 0) - { - sec = ts.tv_sec; - nsec = ts.tv_nsec; - } - return res; + timespec ts; + int res = clock_gettime(CLOCK_REALTIME, &ts); + if (res == 0) { + sec = ts.tv_sec; + nsec = ts.tv_nsec; + } + return res; #endif } -void multiPlatformSleep(uint32_t seconds) -{ +void multiPlatformSleep(uint32_t seconds) { #if defined(_WIN32) - Sleep(seconds*1000); + Sleep(seconds * 1000); #else - sleep(seconds); + sleep(seconds); #endif } -void multiPlatformMSleep(uint32_t milliseconds) -{ +void multiPlatformMSleep(uint32_t milliseconds) { #if defined(_WIN32) - Sleep(milliseconds); + Sleep(milliseconds); #else - usleep(milliseconds*1000); + usleep(milliseconds * 1000); #endif } -uint16_t hostToNet16(uint16_t host) -{ - return htobe16(host); -} - -uint16_t netToHost16(uint16_t net) -{ - return be16toh(net); -} +uint16_t hostToNet16(uint16_t host) { return htobe16(host); } -uint32_t hostToNet32(uint32_t host) -{ - return htobe32(host); -} +uint16_t netToHost16(uint16_t net) { return be16toh(net); } -uint32_t netToHost32(uint32_t net) -{ - return be32toh(net); -} +uint32_t hostToNet32(uint32_t host) { return htobe32(host); } +uint32_t netToHost32(uint32_t net) { return be32toh(net); } std::string AppName::m_AppName; #if defined(_WIN32) -int ApplicationEventHandler::handlerRoutine(unsigned long fdwCtrlType) -{ - switch (fdwCtrlType) - { - case CTRL_C_EVENT: - case CTRL_BREAK_EVENT: - { - if (ApplicationEventHandler::getInstance().m_ApplicationInterruptedHandler != NULL) - ApplicationEventHandler::getInstance().m_ApplicationInterruptedHandler(ApplicationEventHandler::getInstance().m_ApplicationInterruptedCookie); - return TRUE; - } - - default: - return FALSE; - } - +int ApplicationEventHandler::handlerRoutine(unsigned long fdwCtrlType) { + switch (fdwCtrlType) { + case CTRL_C_EVENT: + case CTRL_BREAK_EVENT: { + if (ApplicationEventHandler::getInstance() + .m_ApplicationInterruptedHandler != NULL) + ApplicationEventHandler::getInstance().m_ApplicationInterruptedHandler( + ApplicationEventHandler::getInstance() + .m_ApplicationInterruptedCookie); + return TRUE; + } + + default: + return FALSE; + } } #else static std::mutex UnixLinuxHandlerRoutineMutex; -void ApplicationEventHandler::handlerRoutine(int signum) -{ - switch (signum) - { - case SIGINT: - { - // Most calls are unsafe in a signal handler, and this includes printf(). In particular, - // if the signal is caught while inside printf() it may be called twice at the same time which might not be a good idea - // The way to make sure the signal is called only once is using this lock and putting NULL in m_ApplicationInterruptedHandler - const std::lock_guard lock(UnixLinuxHandlerRoutineMutex); - - if (ApplicationEventHandler::getInstance().m_ApplicationInterruptedHandler != nullptr) - ApplicationEventHandler::getInstance().m_ApplicationInterruptedHandler(ApplicationEventHandler::getInstance().m_ApplicationInterruptedCookie); - - ApplicationEventHandler::getInstance().m_ApplicationInterruptedHandler = nullptr; - - return; - } - default: - { - return; - } - } +void ApplicationEventHandler::handlerRoutine(int signum) { + switch (signum) { + case SIGINT: { + // Most calls are unsafe in a signal handler, and this includes printf(). In + // particular, if the signal is caught while inside printf() it may be + // called twice at the same time which might not be a good idea The way to + // make sure the signal is called only once is using this lock and putting + // NULL in m_ApplicationInterruptedHandler + const std::lock_guard lock(UnixLinuxHandlerRoutineMutex); + + if (ApplicationEventHandler::getInstance() + .m_ApplicationInterruptedHandler != nullptr) + ApplicationEventHandler::getInstance().m_ApplicationInterruptedHandler( + ApplicationEventHandler::getInstance() + .m_ApplicationInterruptedCookie); + + ApplicationEventHandler::getInstance().m_ApplicationInterruptedHandler = + nullptr; + + return; + } + default: { + return; + } + } } #endif +ApplicationEventHandler::ApplicationEventHandler() + : m_ApplicationInterruptedHandler(nullptr), + m_ApplicationInterruptedCookie(nullptr) {} -ApplicationEventHandler::ApplicationEventHandler() : - m_ApplicationInterruptedHandler(nullptr), m_ApplicationInterruptedCookie(nullptr) -{ -} - -void ApplicationEventHandler::onApplicationInterrupted(EventHandlerCallback handler, void* cookie) -{ - m_ApplicationInterruptedHandler = handler; - m_ApplicationInterruptedCookie = cookie; +void ApplicationEventHandler::onApplicationInterrupted( + EventHandlerCallback handler, void* cookie) { + m_ApplicationInterruptedHandler = handler; + m_ApplicationInterruptedCookie = cookie; #if defined(_WIN32) - SetConsoleCtrlHandler((PHANDLER_ROUTINE)handlerRoutine, TRUE); + SetConsoleCtrlHandler((PHANDLER_ROUTINE)handlerRoutine, TRUE); #else - struct sigaction action; - memset(&action, 0, sizeof(struct sigaction)); - action.sa_handler = handlerRoutine; - sigemptyset(&action.sa_mask); - sigaction(SIGINT, &action, nullptr); + struct sigaction action; + memset(&action, 0, sizeof(struct sigaction)); + action.sa_handler = handlerRoutine; + sigemptyset(&action.sa_mask); + sigaction(SIGINT, &action, nullptr); #endif } diff --git a/Common++/src/TablePrinter.cpp b/Common++/src/TablePrinter.cpp index 19812b6e4c..00521af69b 100644 --- a/Common++/src/TablePrinter.cpp +++ b/Common++/src/TablePrinter.cpp @@ -1,141 +1,127 @@ #define LOG_MODULE CommonLogModuleTablePrinter +#include "TablePrinter.h" +#include "Logger.h" #include -#include #include #include +#include #include -#include "TablePrinter.h" -#include "Logger.h" -namespace pcpp -{ - -TablePrinter::TablePrinter(std::vector columnNames, std::vector columnWidths) : - m_ColumnNames(std::move(columnNames)), m_ColumnWidths(std::move(columnWidths)), - m_FirstRow(true), m_TableClosed(false) -{ - if (m_ColumnWidths.size() != m_ColumnNames.size()) - { - PCPP_LOG_ERROR("Cannot create table: number of column names provided is different than number of column widths provided"); - m_TableClosed = true; - } +namespace pcpp { + +TablePrinter::TablePrinter(std::vector columnNames, + std::vector columnWidths) + : m_ColumnNames(std::move(columnNames)), + m_ColumnWidths(std::move(columnWidths)), m_FirstRow(true), + m_TableClosed(false) { + if (m_ColumnWidths.size() != m_ColumnNames.size()) { + PCPP_LOG_ERROR("Cannot create table: number of column names provided is " + "different than number of column widths provided"); + m_TableClosed = true; + } } -TablePrinter::~TablePrinter() -{ - closeTable(); +TablePrinter::~TablePrinter() { closeTable(); } + +bool TablePrinter::printRow(std::vector values) { + // if table is already closed return false + if (m_TableClosed) { + PCPP_LOG_ERROR("Table is closed"); + return false; + } + + if (values.size() != m_ColumnWidths.size()) { + PCPP_LOG_ERROR( + "Number of values in input doesn't equal to number of columns"); + return false; + } + + // if this is the first row printed - print the headline first + if (m_FirstRow) { + printHeadline(); + m_FirstRow = false; + } + + for (int i = 0; i < (int)m_ColumnWidths.size(); i++) { + std::string val = values.at(i); + if (val.length() > (size_t)m_ColumnWidths.at(i)) { + val.erase(m_ColumnWidths.at(i) - 3, std::string::npos); + val += "..."; + } + + std::cout << std::left << "| " << std::setw(m_ColumnWidths.at(i)) << val + << " "; + } + + std::cout << "|" << std::endl; + + return true; } -bool TablePrinter::printRow(std::vector values) -{ - // if table is already closed return false - if (m_TableClosed) - { - PCPP_LOG_ERROR("Table is closed"); - return false; - } - - if (values.size() != m_ColumnWidths.size()) - { - PCPP_LOG_ERROR("Number of values in input doesn't equal to number of columns"); - return false; - } - - // if this is the first row printed - print the headline first - if (m_FirstRow) - { - printHeadline(); - m_FirstRow = false; - } - - for (int i = 0; i < (int)m_ColumnWidths.size(); i++) - { - std::string val = values.at(i); - if (val.length() > (size_t)m_ColumnWidths.at(i)) - { - val.erase(m_ColumnWidths.at(i)-3, std::string::npos); - val += "..."; - } - - std::cout << std::left << "| " << std::setw(m_ColumnWidths.at(i)) << val << " "; - } - - std::cout << "|" << std::endl; - - return true; -} +bool TablePrinter::printRow(const std::string& values, char delimiter) { + std::string singleValue; + std::istringstream valueStream(values); + std::vector valuesAsVec; + while (std::getline(valueStream, singleValue, delimiter)) { + valuesAsVec.push_back(singleValue); + } -bool TablePrinter::printRow(const std::string& values, char delimiter) -{ - std::string singleValue; - std::istringstream valueStream(values); - std::vector valuesAsVec; - while (std::getline(valueStream, singleValue, delimiter)) - { - valuesAsVec.push_back(singleValue); - } - - return printRow(valuesAsVec); + return printRow(valuesAsVec); } -void TablePrinter::printSeparator() -{ - // if table is already closed return - if (m_TableClosed) - { - PCPP_LOG_ERROR("Table is closed"); - return; - } +void TablePrinter::printSeparator() { + // if table is already closed return + if (m_TableClosed) { + PCPP_LOG_ERROR("Table is closed"); + return; + } - int totalLen = 0; - for (std::vector::iterator iter = m_ColumnWidths.begin(); iter != m_ColumnWidths.end(); iter++) - { - totalLen += 2 + (*iter) + 1; - } + int totalLen = 0; + for (std::vector::iterator iter = m_ColumnWidths.begin(); + iter != m_ColumnWidths.end(); iter++) { + totalLen += 2 + (*iter) + 1; + } - totalLen++; + totalLen++; - for (int index = 0; index < totalLen; index++) - std::cout << "-"; + for (int index = 0; index < totalLen; index++) + std::cout << "-"; - std::cout << std::endl; + std::cout << std::endl; } -void TablePrinter::closeTable() -{ - // if this method was already called - do nothing - if (m_TableClosed) - return; +void TablePrinter::closeTable() { + // if this method was already called - do nothing + if (m_TableClosed) + return; - // if no rows were printed - do nothing - if (m_FirstRow) - return; + // if no rows were printed - do nothing + if (m_FirstRow) + return; - printSeparator(); + printSeparator(); - m_TableClosed = true; + m_TableClosed = true; } -void TablePrinter::printHeadline() -{ - // if table is already closed return - if (m_TableClosed) - { - PCPP_LOG_ERROR("Table is closed"); - return; - } +void TablePrinter::printHeadline() { + // if table is already closed return + if (m_TableClosed) { + PCPP_LOG_ERROR("Table is closed"); + return; + } - printSeparator(); + printSeparator(); - for (int i = 0; i < (int)m_ColumnWidths.size(); i++) - { - std::cout << std::left << "| " << std::setw(m_ColumnWidths.at(i)) << m_ColumnNames.at(i) << " "; - } + for (int i = 0; i < (int)m_ColumnWidths.size(); i++) { + std::cout << std::left << "| " << std::setw(m_ColumnWidths.at(i)) + << m_ColumnNames.at(i) << " "; + } - std::cout << "|" << std::endl; + std::cout << "|" << std::endl; - printSeparator(); + printSeparator(); } -} +} // namespace pcpp diff --git a/Examples/ArpSpoofing/main.cpp b/Examples/ArpSpoofing/main.cpp index e852075e8a..63bb9d2f24 100644 --- a/Examples/ArpSpoofing/main.cpp +++ b/Examples/ArpSpoofing/main.cpp @@ -1,251 +1,242 @@ -#include -#include -#include -#include -#include -#include +#include +#include #include +#include +#include +#include +#include #include #include -#include -#include -#include -#include -#include +#include #include +#include +#include +#include +#include - -#define EXIT_WITH_ERROR(reason) do { \ - printUsage(); \ - std::cout << std::endl << "ERROR: " << reason << std::endl << std::endl; \ - exit(1); \ - } while(0) - - -static struct option L3FwdOptions[] = -{ - {"interface", required_argument, nullptr, 'i'}, - {"victim", required_argument, nullptr, 'c'}, - {"gateway", required_argument, nullptr, 'g'}, - {"help", no_argument, nullptr, 'h'}, - {"version", no_argument, nullptr, 'v'}, - {nullptr, 0, nullptr, 0} -}; - +#define EXIT_WITH_ERROR(reason) \ + do { \ + printUsage(); \ + std::cout << std::endl \ + << "ERROR: " << reason << std::endl \ + << std::endl; \ + exit(1); \ + } while (0) + +static struct option L3FwdOptions[] = { + {"interface", required_argument, nullptr, 'i'}, + {"victim", required_argument, nullptr, 'c'}, + {"gateway", required_argument, nullptr, 'g'}, + {"help", no_argument, nullptr, 'h'}, + {"version", no_argument, nullptr, 'v'}, + {nullptr, 0, nullptr, 0}}; /** * Print application usage */ -void printUsage() -{ - std::cout << std::endl - << "Usage:" << std::endl - << "------" << std::endl - << pcpp::AppName::get() << " [-hv] -i interface_ip -c victim_ip -g gateway_ip" << std::endl - << std::endl - << "Options:" << std::endl - << std::endl - << " -i interface_ip : The IPv4 address of interface to use" << std::endl - << " -c victim_ip : The IPv4 address of the victim" << std::endl - << " -g gateway_ip : The IPv4 address of the gateway" << std::endl - << " -h : Displays this help message and exits" << std::endl - << " -v : Displays the current version and exists" << std::endl - << std::endl; +void printUsage() { + std::cout << std::endl + << "Usage:" << std::endl + << "------" << std::endl + << pcpp::AppName::get() + << " [-hv] -i interface_ip -c victim_ip -g gateway_ip" << std::endl + << std::endl + << "Options:" << std::endl + << std::endl + << " -i interface_ip : The IPv4 address of interface to use" + << std::endl + << " -c victim_ip : The IPv4 address of the victim" + << std::endl + << " -g gateway_ip : The IPv4 address of the gateway" + << std::endl + << " -h : Displays this help message and exits" + << std::endl + << " -v : Displays the current version and exists" + << std::endl + << std::endl; } - /** * Print application version */ -void printAppVersion() -{ - std::cout - << pcpp::AppName::get() << " " << pcpp::getPcapPlusPlusVersionFull() << std::endl - << "Built: " << pcpp::getBuildDateTime() << std::endl - << "Built from: " << pcpp::getGitInfo() << std::endl; - exit(0); +void printAppVersion() { + std::cout << pcpp::AppName::get() << " " << pcpp::getPcapPlusPlusVersionFull() + << std::endl + << "Built: " << pcpp::getBuildDateTime() << std::endl + << "Built from: " << pcpp::getGitInfo() << std::endl; + exit(0); } - -pcpp::MacAddress getMacAddress(const pcpp::IPv4Address& ipAddr, pcpp::PcapLiveDevice* pDevice) -{ - // Create an ARP packet and change its fields - pcpp::Packet arpRequest(500); - - pcpp::MacAddress macSrc = pDevice->getMacAddress(); - pcpp::MacAddress macDst(0xff, 0xff, 0xff, 0xff, 0xff, 0xff); - pcpp::EthLayer ethLayer(macSrc, macDst, (uint16_t)PCPP_ETHERTYPE_ARP); - pcpp::ArpLayer arpLayer(pcpp::ARP_REQUEST, - pDevice->getMacAddress(), - pDevice->getMacAddress(), - pDevice->getIPv4Address(), - ipAddr); - - - arpRequest.addLayer(ðLayer); - arpRequest.addLayer(&arpLayer); - arpRequest.computeCalculateFields(); - - //setup arp reply filter - pcpp::ArpFilter arpFilter(pcpp::ARP_REPLY); - if (!pDevice->setFilter(arpFilter)) - { - std::cerr << "Could not set ARP filter on device" << std::endl; - return pcpp::MacAddress(""); - } - - //send the arp request and wait for arp reply - pDevice->sendPacket(&arpRequest); - pcpp::RawPacketVector capturedPackets; - pDevice->startCapture(capturedPackets); - pcpp::multiPlatformSleep(2); - pDevice->stopCapture(); - - if (capturedPackets.size() < 1) - { - std::cerr << "No arp reply was captured. Couldn't retrieve MAC address for IP " << ipAddr << std::endl; - return pcpp::MacAddress(""); - } - - //parse arp reply and extract the MAC address - pcpp::Packet arpReply(capturedPackets.front()); - if (arpReply.isPacketOfType(pcpp::ARP)) - { - return arpReply.getLayerOfType()->getSenderMacAddress(); - } - std::cerr << "No arp reply was captured. Couldn't retrieve MAC address for IP " << ipAddr << std::endl; - return pcpp::MacAddress(""); +pcpp::MacAddress getMacAddress(const pcpp::IPv4Address& ipAddr, + pcpp::PcapLiveDevice* pDevice) { + // Create an ARP packet and change its fields + pcpp::Packet arpRequest(500); + + pcpp::MacAddress macSrc = pDevice->getMacAddress(); + pcpp::MacAddress macDst(0xff, 0xff, 0xff, 0xff, 0xff, 0xff); + pcpp::EthLayer ethLayer(macSrc, macDst, (uint16_t)PCPP_ETHERTYPE_ARP); + pcpp::ArpLayer arpLayer(pcpp::ARP_REQUEST, pDevice->getMacAddress(), + pDevice->getMacAddress(), pDevice->getIPv4Address(), + ipAddr); + + arpRequest.addLayer(ðLayer); + arpRequest.addLayer(&arpLayer); + arpRequest.computeCalculateFields(); + + // setup arp reply filter + pcpp::ArpFilter arpFilter(pcpp::ARP_REPLY); + if (!pDevice->setFilter(arpFilter)) { + std::cerr << "Could not set ARP filter on device" << std::endl; + return pcpp::MacAddress(""); + } + + // send the arp request and wait for arp reply + pDevice->sendPacket(&arpRequest); + pcpp::RawPacketVector capturedPackets; + pDevice->startCapture(capturedPackets); + pcpp::multiPlatformSleep(2); + pDevice->stopCapture(); + + if (capturedPackets.size() < 1) { + std::cerr + << "No arp reply was captured. Couldn't retrieve MAC address for IP " + << ipAddr << std::endl; + return pcpp::MacAddress(""); + } + + // parse arp reply and extract the MAC address + pcpp::Packet arpReply(capturedPackets.front()); + if (arpReply.isPacketOfType(pcpp::ARP)) { + return arpReply.getLayerOfType()->getSenderMacAddress(); + } + std::cerr + << "No arp reply was captured. Couldn't retrieve MAC address for IP " + << ipAddr << std::endl; + return pcpp::MacAddress(""); } - -void doArpSpoofing(pcpp::PcapLiveDevice* pDevice, const pcpp::IPv4Address& gatewayAddr, const pcpp::IPv4Address& victimAddr) -{ - // Get the gateway MAC address - pcpp::MacAddress gatewayMacAddr = getMacAddress(gatewayAddr, pDevice); - if (!gatewayMacAddr.isValid()) - { - EXIT_WITH_ERROR("Failed to find gateway MAC address"); - } - std::cout << "Got gateway MAC address: " << gatewayMacAddr << std::endl; - - // Get the victim MAC address - pcpp::MacAddress victimMacAddr = getMacAddress(victimAddr, pDevice); - if (!victimMacAddr.isValid()) - { - EXIT_WITH_ERROR("Failed to find victim MAC address"); - } - std::cout << "Got victim MAC address: " << victimMacAddr << std::endl; - - pcpp::MacAddress deviceMacAddress = pDevice->getMacAddress(); - - // Create ARP reply for the gateway - pcpp::Packet gwArpReply(500); - pcpp::EthLayer gwEthLayer(deviceMacAddress, gatewayMacAddr, (uint16_t)PCPP_ETHERTYPE_ARP); - pcpp::ArpLayer gwArpLayer(pcpp::ARP_REPLY, - pDevice->getMacAddress(), - gatewayMacAddr, - victimAddr, - gatewayAddr); - gwArpReply.addLayer(&gwEthLayer); - gwArpReply.addLayer(&gwArpLayer); - gwArpReply.computeCalculateFields(); - - // Create ARP reply for the victim - pcpp::Packet victimArpReply(500); - pcpp::EthLayer victimEthLayer(deviceMacAddress, victimMacAddr, (uint16_t)PCPP_ETHERTYPE_ARP); - pcpp::ArpLayer victimArpLayer(pcpp::ARP_REPLY, - pDevice->getMacAddress(), - victimMacAddr, - gatewayAddr, - victimAddr); - victimArpReply.addLayer(&victimEthLayer); - victimArpReply.addLayer(&victimArpLayer); - victimArpReply.computeCalculateFields(); - - // Send ARP replies to gateway and to victim every 5 seconds - std::cout << "Sending ARP replies to victim and to gateway every 5 seconds..." << std::endl << std::endl; - while(1) - { - pDevice->sendPacket(&gwArpReply); - std::cout << "Sent ARP reply: " << gatewayAddr << " [gateway] is at MAC address " << deviceMacAddress << " [me]" << std::endl; - pDevice->sendPacket(&victimArpReply); - std::cout << "Sent ARP reply: " << victimAddr << " [victim] is at MAC address " << deviceMacAddress << " [me]" << std::endl; - pcpp::multiPlatformSleep(5); - } +void doArpSpoofing(pcpp::PcapLiveDevice* pDevice, + const pcpp::IPv4Address& gatewayAddr, + const pcpp::IPv4Address& victimAddr) { + // Get the gateway MAC address + pcpp::MacAddress gatewayMacAddr = getMacAddress(gatewayAddr, pDevice); + if (!gatewayMacAddr.isValid()) { + EXIT_WITH_ERROR("Failed to find gateway MAC address"); + } + std::cout << "Got gateway MAC address: " << gatewayMacAddr << std::endl; + + // Get the victim MAC address + pcpp::MacAddress victimMacAddr = getMacAddress(victimAddr, pDevice); + if (!victimMacAddr.isValid()) { + EXIT_WITH_ERROR("Failed to find victim MAC address"); + } + std::cout << "Got victim MAC address: " << victimMacAddr << std::endl; + + pcpp::MacAddress deviceMacAddress = pDevice->getMacAddress(); + + // Create ARP reply for the gateway + pcpp::Packet gwArpReply(500); + pcpp::EthLayer gwEthLayer(deviceMacAddress, gatewayMacAddr, + (uint16_t)PCPP_ETHERTYPE_ARP); + pcpp::ArpLayer gwArpLayer(pcpp::ARP_REPLY, pDevice->getMacAddress(), + gatewayMacAddr, victimAddr, gatewayAddr); + gwArpReply.addLayer(&gwEthLayer); + gwArpReply.addLayer(&gwArpLayer); + gwArpReply.computeCalculateFields(); + + // Create ARP reply for the victim + pcpp::Packet victimArpReply(500); + pcpp::EthLayer victimEthLayer(deviceMacAddress, victimMacAddr, + (uint16_t)PCPP_ETHERTYPE_ARP); + pcpp::ArpLayer victimArpLayer(pcpp::ARP_REPLY, pDevice->getMacAddress(), + victimMacAddr, gatewayAddr, victimAddr); + victimArpReply.addLayer(&victimEthLayer); + victimArpReply.addLayer(&victimArpLayer); + victimArpReply.computeCalculateFields(); + + // Send ARP replies to gateway and to victim every 5 seconds + std::cout << "Sending ARP replies to victim and to gateway every 5 seconds..." + << std::endl + << std::endl; + while (1) { + pDevice->sendPacket(&gwArpReply); + std::cout << "Sent ARP reply: " << gatewayAddr + << " [gateway] is at MAC address " << deviceMacAddress << " [me]" + << std::endl; + pDevice->sendPacket(&victimArpReply); + std::cout << "Sent ARP reply: " << victimAddr + << " [victim] is at MAC address " << deviceMacAddress << " [me]" + << std::endl; + pcpp::multiPlatformSleep(5); + } } - -int main(int argc, char* argv[]) -{ - pcpp::AppName::init(argc, argv); - - //Get arguments from user for incoming interface and outgoing interface - - std::string iface = "", victim = "", gateway = ""; - int optionIndex = 0; - int opt = 0; - while((opt = getopt_long(argc, argv, "i:c:g:hv", L3FwdOptions, &optionIndex)) != -1) - { - switch (opt) - { - case 0: - break; - case 'i': - iface = optarg; - break; - case 'c': - victim = optarg; - break; - case 'g': - gateway = optarg; - break; - case 'h': - printUsage(); - exit(0); - break; - case 'v': - printAppVersion(); - break; - default: - printUsage(); - exit(-1); - } - } - - //Both incoming and outgoing interfaces must be provided by user - if(iface == "" || victim == "" || gateway == "") - { - EXIT_WITH_ERROR("Please specify both interface IP, victim IP and gateway IP"); - } - - //Currently supports only IPv4 addresses - pcpp::IPv4Address ifaceAddr(iface); - pcpp::IPv4Address victimAddr(victim); - pcpp::IPv4Address gatewayAddr(gateway); - - pcpp::PcapLiveDevice* pIfaceDevice = pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDeviceByIp(ifaceAddr); - - //Verifying interface is valid - if (pIfaceDevice == nullptr) - { - EXIT_WITH_ERROR("Cannot find interface"); - } - - if (!victimAddr.isValid()) - { - EXIT_WITH_ERROR("Victim address is not valid"); - } - - if (!gatewayAddr.isValid()) - { - EXIT_WITH_ERROR("Gateway address is not valid"); - } - - //Opening interface device - if (!pIfaceDevice->open()) - { - EXIT_WITH_ERROR("Cannot open interface"); - } - - doArpSpoofing(pIfaceDevice, gatewayAddr, victimAddr); +int main(int argc, char* argv[]) { + pcpp::AppName::init(argc, argv); + + // Get arguments from user for incoming interface and outgoing interface + + std::string iface = "", victim = "", gateway = ""; + int optionIndex = 0; + int opt = 0; + while ((opt = getopt_long(argc, argv, "i:c:g:hv", L3FwdOptions, + &optionIndex)) != -1) { + switch (opt) { + case 0: + break; + case 'i': + iface = optarg; + break; + case 'c': + victim = optarg; + break; + case 'g': + gateway = optarg; + break; + case 'h': + printUsage(); + exit(0); + break; + case 'v': + printAppVersion(); + break; + default: + printUsage(); + exit(-1); + } + } + + // Both incoming and outgoing interfaces must be provided by user + if (iface == "" || victim == "" || gateway == "") { + EXIT_WITH_ERROR( + "Please specify both interface IP, victim IP and gateway IP"); + } + + // Currently supports only IPv4 addresses + pcpp::IPv4Address ifaceAddr(iface); + pcpp::IPv4Address victimAddr(victim); + pcpp::IPv4Address gatewayAddr(gateway); + + pcpp::PcapLiveDevice* pIfaceDevice = + pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDeviceByIp(ifaceAddr); + + // Verifying interface is valid + if (pIfaceDevice == nullptr) { + EXIT_WITH_ERROR("Cannot find interface"); + } + + if (!victimAddr.isValid()) { + EXIT_WITH_ERROR("Victim address is not valid"); + } + + if (!gatewayAddr.isValid()) { + EXIT_WITH_ERROR("Gateway address is not valid"); + } + + // Opening interface device + if (!pIfaceDevice->open()) { + EXIT_WITH_ERROR("Cannot open interface"); + } + + doArpSpoofing(pIfaceDevice, gatewayAddr, victimAddr); } diff --git a/Examples/Arping/main.cpp b/Examples/Arping/main.cpp index d4b719e897..7f6bce2b60 100644 --- a/Examples/Arping/main.cpp +++ b/Examples/Arping/main.cpp @@ -1,254 +1,258 @@ /** * Arping example application * ================================ - * This application resolves a target MAC address by its IPv4 address by sending an ARP request and translating the ARP response. - * Its basic input is the target IP address and the interface name/IP to send the ARP request from + * This application resolves a target MAC address by its IPv4 address by sending + * an ARP request and translating the ARP response. Its basic input is the + * target IP address and the interface name/IP to send the ARP request from */ -#include -#include -#include #include #include -#include -#include -#include +#include #include -#include +#include +#include +#include #include +#include +#include +#include - -#define EXIT_WITH_ERROR(reason) do { \ - printUsage(); \ - std::cout << std::endl << "ERROR: " << reason << std::endl << std::endl; \ - exit(1); \ - } while(0) - - -#define DEFAULT_MAX_TRIES 1000000 - - -static struct option ArpingOptions[] = -{ - {"interface", optional_argument, nullptr, 'i'}, - {"source-mac", optional_argument, nullptr, 's'}, - {"source-ip", optional_argument, nullptr, 'S'}, - {"target-ip", required_argument, nullptr, 'T'}, - {"count", optional_argument, nullptr, 'c'}, - {"help", optional_argument, nullptr, 'h'}, - {"version", no_argument, nullptr, 'v'}, - {"list", optional_argument, nullptr, 'l'}, - {"timeout", optional_argument, nullptr, 'w'}, - {nullptr, 0, nullptr, 0} -}; - +#define EXIT_WITH_ERROR(reason) \ + do { \ + printUsage(); \ + std::cout << std::endl \ + << "ERROR: " << reason << std::endl \ + << std::endl; \ + exit(1); \ + } while (0) + +#define DEFAULT_MAX_TRIES 1000000 + +static struct option ArpingOptions[] = { + {"interface", optional_argument, nullptr, 'i'}, + {"source-mac", optional_argument, nullptr, 's'}, + {"source-ip", optional_argument, nullptr, 'S'}, + {"target-ip", required_argument, nullptr, 'T'}, + {"count", optional_argument, nullptr, 'c'}, + {"help", optional_argument, nullptr, 'h'}, + {"version", no_argument, nullptr, 'v'}, + {"list", optional_argument, nullptr, 'l'}, + {"timeout", optional_argument, nullptr, 'w'}, + {nullptr, 0, nullptr, 0}}; /** * Print application usage */ -void printUsage() -{ - std::cout << std::endl - << "Usage:" << std::endl - << "------" << std::endl - << pcpp::AppName::get() << " [-hvl] [-c count] [-w timeout] [-s mac_addr] [-S ip_addr] -i interface -T ip_addr" << std::endl - << std::endl - << "Options:" << std::endl - << std::endl - << " -h : Displays this help message and exits" << std::endl - << " -v : Displays the current version and exists" << std::endl - << " -l : Print the list of interfaces and exists" << std::endl - << " -c count : Send 'count' requests" << std::endl - << " -i interface : Use the specified interface. Can be interface name (e.g eth0) or interface IPv4 address" << std::endl - << " -s mac_addr : Set source MAC address" << std::endl - << " -S ip_addr : Set source IP address" << std::endl - << " -T ip_addr : Set target IP address" << std::endl - << " -w timeout : How long to wait for a reply (in seconds)" << std::endl - << std::endl; +void printUsage() { + std::cout << std::endl + << "Usage:" << std::endl + << "------" << std::endl + << pcpp::AppName::get() + << " [-hvl] [-c count] [-w timeout] [-s mac_addr] [-S ip_addr] -i " + "interface -T ip_addr" + << std::endl + << std::endl + << "Options:" << std::endl + << std::endl + << " -h : Displays this help message and exits" + << std::endl + << " -v : Displays the current version and exists" + << std::endl + << " -l : Print the list of interfaces and exists" + << std::endl + << " -c count : Send 'count' requests" << std::endl + << " -i interface : Use the specified interface. Can be " + "interface name (e.g eth0) or interface IPv4 address" + << std::endl + << " -s mac_addr : Set source MAC address" << std::endl + << " -S ip_addr : Set source IP address" << std::endl + << " -T ip_addr : Set target IP address" << std::endl + << " -w timeout : How long to wait for a reply (in seconds)" + << std::endl + << std::endl; } - /** * Print application version */ -void printAppVersion() -{ - std::cout - << pcpp::AppName::get() << " " << pcpp::getPcapPlusPlusVersionFull() << std::endl - << "Built: " << pcpp::getBuildDateTime() << std::endl - << "Built from: " << pcpp::getGitInfo() << std::endl; - exit(0); +void printAppVersion() { + std::cout << pcpp::AppName::get() << " " << pcpp::getPcapPlusPlusVersionFull() + << std::endl + << "Built: " << pcpp::getBuildDateTime() << std::endl + << "Built from: " << pcpp::getGitInfo() << std::endl; + exit(0); } - /** * Go over all interfaces and output their names */ -void listInterfaces() -{ - const std::vector& devList = pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDevicesList(); - - std::cout << std::endl << "Network interfaces:" << std::endl; - for (std::vector::const_iterator iter = devList.begin(); iter != devList.end(); iter++) - { - std::cout << " -> Name: '" << (*iter)->getName() << "' IP address: " << (*iter)->getIPv4Address().toString() << std::endl; - } - exit(0); +void listInterfaces() { + const std::vector& devList = + pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDevicesList(); + + std::cout << std::endl + << "Network interfaces:" << std::endl; + for (std::vector::const_iterator iter = + devList.begin(); + iter != devList.end(); iter++) { + std::cout << " -> Name: '" << (*iter)->getName() + << "' IP address: " << (*iter)->getIPv4Address().toString() + << std::endl; + } + exit(0); } - /** - * The callback to be called when application is terminated by ctrl-c. Stops the endless while loop + * The callback to be called when application is terminated by ctrl-c. Stops the + * endless while loop */ -void onApplicationInterrupted(void* cookie) -{ - auto shouldStop = (bool*)cookie; - *shouldStop = true; +void onApplicationInterrupted(void* cookie) { + auto shouldStop = (bool*)cookie; + *shouldStop = true; } - /** * main method of the application */ -int main(int argc, char* argv[]) -{ - pcpp::AppName::init(argc, argv); - - int maxTries = DEFAULT_MAX_TRIES; - pcpp::MacAddress sourceMac; - pcpp::IPv4Address sourceIP; - pcpp::IPv4Address targetIP; - bool targetIpProvided = false; - std::string ifaceNameOrIP; - bool ifaceNameOrIpProvided = false; - int timeoutSec = pcpp::NetworkUtils::DefaultTimeout; - int optionIndex = 0; - int opt = 0; - - while((opt = getopt_long(argc, argv, "i:s:S:T:c:hvlw:", ArpingOptions, &optionIndex)) != -1) - { - switch (opt) - { - case 0: - break; - case 'i': - ifaceNameOrIP = optarg; - ifaceNameOrIpProvided = true; - break; - case 's': - sourceMac = pcpp::MacAddress(optarg); - break; - case 'S': - sourceIP = pcpp::IPv4Address(static_cast(optarg)); - break; - case 'T': - targetIP = pcpp::IPv4Address(static_cast(optarg)); - targetIpProvided = true; - break; - case 'c': - maxTries = atoi(optarg); - break; - case 'h': - printUsage(); - exit(0); - case 'v': - printAppVersion(); - break; - case 'l': - listInterfaces(); - break; - case 'w': - timeoutSec = atoi(optarg); - break; - default: - printUsage(); - exit(-1); - } - } - - // verify that interface name or IP were provided - if (!ifaceNameOrIpProvided) - EXIT_WITH_ERROR("You must provide at least interface name or interface IP (-i switch)"); - - // verify target IP was provided - if (!targetIpProvided) - EXIT_WITH_ERROR("You must provide target IP (-T switch)"); - - // verify target IP is value - if (!targetIP.isValid()) - EXIT_WITH_ERROR("Target IP is not valid"); - - - pcpp::PcapLiveDevice* dev = nullptr; - - // Search interface by name or IP - if (!ifaceNameOrIP.empty()) - { - dev = pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDeviceByIpOrName(ifaceNameOrIP); - if (dev == nullptr) - EXIT_WITH_ERROR("Couldn't find interface by provided IP address or name"); - } - else - EXIT_WITH_ERROR("Interface name or IP empty"); - - // open device in promiscuous mode - if (!dev->open()) - EXIT_WITH_ERROR("Couldn't open interface device '" << dev->getName() << "'"); - - // verify source MAC is valid - if (!sourceMac.isValid()) - EXIT_WITH_ERROR("Source MAC address is invalid"); - - // if source MAC not provided - use the interface MAC address - if (sourceMac == pcpp::MacAddress::Zero) - sourceMac = dev->getMacAddress(); - - // if source MAC is still invalid, it means it couldn't be extracted from interface - if (!sourceMac.isValid() || sourceMac == pcpp::MacAddress::Zero) - EXIT_WITH_ERROR("MAC address couldn't be extracted from interface"); - - if (!sourceIP.isValid() || sourceIP == pcpp::IPv4Address::Zero) - sourceIP = dev->getIPv4Address(); - - if (!sourceIP.isValid() || sourceIP == pcpp::IPv4Address::Zero) - EXIT_WITH_ERROR("Source IPv4 address wasn't supplied and couldn't be retrieved from interface"); - - // let's go - double arpResponseTimeMS = 0; - int i = 1; - - // suppressing errors to avoid cluttering stdout - pcpp::Logger::getInstance().suppressLogs(); - - // make sure the app closes the device upon termination - bool shouldStop = false; - pcpp::ApplicationEventHandler::getInstance().onApplicationInterrupted(onApplicationInterrupted, &shouldStop); - - while (i <= maxTries && !shouldStop) - { - // use the getMacAddress utility to send an ARP request and resolve the MAC address - pcpp::MacAddress result = pcpp::NetworkUtils::getInstance().getMacAddress(targetIP, dev, arpResponseTimeMS, sourceMac, sourceIP, timeoutSec); - - // failed fetching MAC address - if (result == pcpp::MacAddress::Zero) - { - // PcapPlusPlus logger saves the last internal error message - std::cout << "Arping index=" << i << " : " << pcpp::Logger::getInstance().getLastError() << std::endl; - } - else // Succeeded fetching MAC address - { - // output ARP ping data - std::cout.precision(3); - std::cout - << "Reply from " << targetIP << " " - << "[" << result << "] " - << std::fixed << arpResponseTimeMS << "ms " - << "index=" << i - << std::endl; - } - - i++; - } - - dev->close(); +int main(int argc, char* argv[]) { + pcpp::AppName::init(argc, argv); + + int maxTries = DEFAULT_MAX_TRIES; + pcpp::MacAddress sourceMac; + pcpp::IPv4Address sourceIP; + pcpp::IPv4Address targetIP; + bool targetIpProvided = false; + std::string ifaceNameOrIP; + bool ifaceNameOrIpProvided = false; + int timeoutSec = pcpp::NetworkUtils::DefaultTimeout; + int optionIndex = 0; + int opt = 0; + + while ((opt = getopt_long(argc, argv, "i:s:S:T:c:hvlw:", ArpingOptions, + &optionIndex)) != -1) { + switch (opt) { + case 0: + break; + case 'i': + ifaceNameOrIP = optarg; + ifaceNameOrIpProvided = true; + break; + case 's': + sourceMac = pcpp::MacAddress(optarg); + break; + case 'S': + sourceIP = pcpp::IPv4Address(static_cast(optarg)); + break; + case 'T': + targetIP = pcpp::IPv4Address(static_cast(optarg)); + targetIpProvided = true; + break; + case 'c': + maxTries = atoi(optarg); + break; + case 'h': + printUsage(); + exit(0); + case 'v': + printAppVersion(); + break; + case 'l': + listInterfaces(); + break; + case 'w': + timeoutSec = atoi(optarg); + break; + default: + printUsage(); + exit(-1); + } + } + + // verify that interface name or IP were provided + if (!ifaceNameOrIpProvided) + EXIT_WITH_ERROR( + "You must provide at least interface name or interface IP (-i switch)"); + + // verify target IP was provided + if (!targetIpProvided) + EXIT_WITH_ERROR("You must provide target IP (-T switch)"); + + // verify target IP is value + if (!targetIP.isValid()) + EXIT_WITH_ERROR("Target IP is not valid"); + + pcpp::PcapLiveDevice* dev = nullptr; + + // Search interface by name or IP + if (!ifaceNameOrIP.empty()) { + dev = pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDeviceByIpOrName( + ifaceNameOrIP); + if (dev == nullptr) + EXIT_WITH_ERROR("Couldn't find interface by provided IP address or name"); + } else + EXIT_WITH_ERROR("Interface name or IP empty"); + + // open device in promiscuous mode + if (!dev->open()) + EXIT_WITH_ERROR("Couldn't open interface device '" << dev->getName() + << "'"); + + // verify source MAC is valid + if (!sourceMac.isValid()) + EXIT_WITH_ERROR("Source MAC address is invalid"); + + // if source MAC not provided - use the interface MAC address + if (sourceMac == pcpp::MacAddress::Zero) + sourceMac = dev->getMacAddress(); + + // if source MAC is still invalid, it means it couldn't be extracted from + // interface + if (!sourceMac.isValid() || sourceMac == pcpp::MacAddress::Zero) + EXIT_WITH_ERROR("MAC address couldn't be extracted from interface"); + + if (!sourceIP.isValid() || sourceIP == pcpp::IPv4Address::Zero) + sourceIP = dev->getIPv4Address(); + + if (!sourceIP.isValid() || sourceIP == pcpp::IPv4Address::Zero) + EXIT_WITH_ERROR("Source IPv4 address wasn't supplied and couldn't be " + "retrieved from interface"); + + // let's go + double arpResponseTimeMS = 0; + int i = 1; + + // suppressing errors to avoid cluttering stdout + pcpp::Logger::getInstance().suppressLogs(); + + // make sure the app closes the device upon termination + bool shouldStop = false; + pcpp::ApplicationEventHandler::getInstance().onApplicationInterrupted( + onApplicationInterrupted, &shouldStop); + + while (i <= maxTries && !shouldStop) { + // use the getMacAddress utility to send an ARP request and resolve the MAC + // address + pcpp::MacAddress result = pcpp::NetworkUtils::getInstance().getMacAddress( + targetIP, dev, arpResponseTimeMS, sourceMac, sourceIP, timeoutSec); + + // failed fetching MAC address + if (result == pcpp::MacAddress::Zero) { + // PcapPlusPlus logger saves the last internal error message + std::cout << "Arping index=" << i << " : " + << pcpp::Logger::getInstance().getLastError() << std::endl; + } else // Succeeded fetching MAC address + { + // output ARP ping data + std::cout.precision(3); + std::cout << "Reply from " << targetIP << " " + << "[" << result << "] " << std::fixed << arpResponseTimeMS + << "ms " + << "index=" << i << std::endl; + } + + i++; + } + + dev->close(); } diff --git a/Examples/DNSResolver/main.cpp b/Examples/DNSResolver/main.cpp index cf95e684cc..678190d8ea 100644 --- a/Examples/DNSResolver/main.cpp +++ b/Examples/DNSResolver/main.cpp @@ -1,224 +1,230 @@ -#include -#include -#include "PcapPlusPlusVersion.h" +#include "Logger.h" +#include "NetworkUtils.h" #include "PcapLiveDevice.h" #include "PcapLiveDeviceList.h" -#include "NetworkUtils.h" +#include "PcapPlusPlusVersion.h" #include "SystemUtils.h" -#include "Logger.h" #include +#include +#include - -#define EXIT_WITH_ERROR(reason) do { \ - printUsage(); \ - std::cout << std::endl << "ERROR: " << reason << std::endl << std::endl; \ - exit(1); \ - } while(0) - - -static struct option DNSResolverOptions[] = -{ - {"interface", required_argument, nullptr, 'i'}, - {"hostname", required_argument, nullptr, 's'}, - {"dns-server", required_argument, nullptr, 'd'}, - {"gateway", required_argument, nullptr, 'g'}, - {"timeout", optional_argument, nullptr, 't'}, - {"help", no_argument, nullptr, 'h'}, - {"version", no_argument, nullptr, 'v'}, - {"list", no_argument, nullptr, 'l'}, - {nullptr, 0, nullptr, 0} -}; - +#define EXIT_WITH_ERROR(reason) \ + do { \ + printUsage(); \ + std::cout << std::endl \ + << "ERROR: " << reason << std::endl \ + << std::endl; \ + exit(1); \ + } while (0) + +static struct option DNSResolverOptions[] = { + {"interface", required_argument, nullptr, 'i'}, + {"hostname", required_argument, nullptr, 's'}, + {"dns-server", required_argument, nullptr, 'd'}, + {"gateway", required_argument, nullptr, 'g'}, + {"timeout", optional_argument, nullptr, 't'}, + {"help", no_argument, nullptr, 'h'}, + {"version", no_argument, nullptr, 'v'}, + {"list", no_argument, nullptr, 'l'}, + {nullptr, 0, nullptr, 0}}; /** * Print application usage */ -void printUsage() -{ - std::cout << std::endl - << "Usage:" << std::endl - << "------" << std::endl - << pcpp::AppName::get() << " [-hvl] [-t timeout] [-d dns_server] [-g gateway] [-i interface] -s hostname" << std::endl - << std::endl - << "Options:" << std::endl - << std::endl - << " -h : Displays this help message and exits" << std::endl - << " -v : Displays the current version and exists" << std::endl - << " -l : Print the list of interfaces and exists" << std::endl - << " -s hostname : Hostname to resolve" << std::endl - << " -i interface : Use the specified interface. Can be interface name (e.g eth0) or interface IPv4 address. If not set" << std::endl - << " one of the interfaces that has a default gateway will be used" << std::endl - << " -d dns_server: IPv4 address of DNS server to send the DNS request to. If not set the DNS request will be sent to the gateway" << std::endl - << " -g gateway : IPv4 address of the gateway to send the DNS request to. If not set the default gateway will be chosen" << std::endl - << " -t timeout : How long to wait for a reply (in seconds). Default timeout is 5 seconds" << std::endl - << std::endl; +void printUsage() { + std::cout + << std::endl + << "Usage:" << std::endl + << "------" << std::endl + << pcpp::AppName::get() + << " [-hvl] [-t timeout] [-d dns_server] [-g gateway] [-i interface] -s " + "hostname" + << std::endl + << std::endl + << "Options:" << std::endl + << std::endl + << " -h : Displays this help message and exits" << std::endl + << " -v : Displays the current version and exists" + << std::endl + << " -l : Print the list of interfaces and exists" + << std::endl + << " -s hostname : Hostname to resolve" << std::endl + << " -i interface : Use the specified interface. Can be interface " + "name (e.g eth0) or interface IPv4 address. If not set" + << std::endl + << " one of the interfaces that has a default gateway " + "will be used" + << std::endl + << " -d dns_server: IPv4 address of DNS server to send the DNS " + "request to. If not set the DNS request will be sent to the gateway" + << std::endl + << " -g gateway : IPv4 address of the gateway to send the DNS " + "request to. If not set the default gateway will be chosen" + << std::endl + << " -t timeout : How long to wait for a reply (in seconds). " + "Default timeout is 5 seconds" + << std::endl + << std::endl; } - /** * Print application version */ -void printAppVersion() -{ - std::cout - << pcpp::AppName::get() << " " << pcpp::getPcapPlusPlusVersionFull() << std::endl - << "Built: " << pcpp::getBuildDateTime() << std::endl - << "Built from: " << pcpp::getGitInfo() << std::endl; - exit(0); +void printAppVersion() { + std::cout << pcpp::AppName::get() << " " << pcpp::getPcapPlusPlusVersionFull() + << std::endl + << "Built: " << pcpp::getBuildDateTime() << std::endl + << "Built from: " << pcpp::getGitInfo() << std::endl; + exit(0); } - /** * Go over all interfaces and output their names */ -void listInterfaces() -{ - const std::vector& devList = pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDevicesList(); - - std::cout << std::endl << "Network interfaces:" << std::endl; - for (std::vector::const_iterator iter = devList.begin(); iter != devList.end(); iter++) - { - std::cout << " -> Name: '" << (*iter)->getName() << "' IP address: " << (*iter)->getIPv4Address().toString() << std::endl; - } - exit(0); +void listInterfaces() { + const std::vector& devList = + pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDevicesList(); + + std::cout << std::endl + << "Network interfaces:" << std::endl; + for (std::vector::const_iterator iter = + devList.begin(); + iter != devList.end(); iter++) { + std::cout << " -> Name: '" << (*iter)->getName() + << "' IP address: " << (*iter)->getIPv4Address().toString() + << std::endl; + } + exit(0); } - /** - * The callback to be called when application is terminated by ctrl-c. Stops the endless while loop + * The callback to be called when application is terminated by ctrl-c. Stops the + * endless while loop */ -void onApplicationInterrupted(void* cookie) -{ - auto device = (pcpp::PcapLiveDevice*)cookie; - device->close(); +void onApplicationInterrupted(void* cookie) { + auto device = (pcpp::PcapLiveDevice*)cookie; + device->close(); } - /** * main method of the application */ -int main(int argc, char* argv[]) -{ - pcpp::AppName::init(argc, argv); - - std::string hostname; - bool hostnameProvided = false; - std::string interfaceNameOrIP; - bool interfaceNameOrIPProvided = false; - pcpp::IPv4Address dnsServerIP; - pcpp::IPv4Address gatewayIP; - int timeoutSec = -1; - - int optionIndex = 0; - int opt = 0; - - while((opt = getopt_long(argc, argv, "i:d:g:s:t:hvl", DNSResolverOptions, &optionIndex)) != -1) - { - switch (opt) - { - case 0: - { - break; - } - case 'h': - { - printUsage(); - exit(0); - } - case 'v': - { - printAppVersion(); - break; - } - case 'l': - { - listInterfaces(); - exit(0); - } - case 'i': - { - interfaceNameOrIP = optarg; - interfaceNameOrIPProvided = true; - break; - } - case 'd': - { - dnsServerIP = pcpp::IPv4Address(static_cast(optarg)); - break; - } - case 'g': - { - gatewayIP = pcpp::IPv4Address(static_cast(optarg)); - break; - } - case 's': - { - hostname = optarg; - hostnameProvided = true; - break; - } - case 't': - { - timeoutSec = atoi(optarg); - break; - } - default: - { - printUsage(); - exit(1); - } - } - } - - // make sure that hostname is provided - if (!hostnameProvided) - EXIT_WITH_ERROR("Hostname not provided"); - - // find the interface to send the DNS request from - pcpp::PcapLiveDevice* dev = nullptr; - - // if interface name or IP was provided - find the device accordingly - if (interfaceNameOrIPProvided) - { - dev = pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDeviceByIpOrName(interfaceNameOrIP); - if (dev == nullptr) - EXIT_WITH_ERROR("Couldn't find interface by provided IP address or name"); - } - // if interface name or IP was not provided - find a device that has a default gateway - else - { - const std::vector& devList = pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDevicesList(); - - for (std::vector::const_iterator iter = devList.begin(); iter != devList.end(); iter++) - { - if ((*iter)->getDefaultGateway().isValid()) - { - dev = *iter; - break; - } - } - - if (dev == nullptr) - EXIT_WITH_ERROR("Couldn't find an interface with a default gateway"); - } - - std::cout << "Using interface '" << dev->getIPv4Address() << "'" << std::endl; - - // suppressing errors to avoid cluttering stdout - pcpp::Logger::getInstance().suppressLogs(); - - // make sure the app closes the device upon termination - pcpp::ApplicationEventHandler::getInstance().onApplicationInterrupted(onApplicationInterrupted, dev); - - // find the IPv4 address for provided hostname - double responseTime = 0; - uint32_t dnsTTL = 0; - pcpp::IPv4Address resultIP = pcpp::NetworkUtils::getInstance().getIPv4Address(hostname, dev, responseTime, dnsTTL, timeoutSec, dnsServerIP, gatewayIP); - - // print resolved IPv4 address if found - if (!resultIP.isValid()) - std::cout << std::endl << "Could not resolve hostname [" << hostname << "]" << std::endl; - else - std::cout << std::endl << "IP address of [" << hostname << "] is: " << resultIP << " DNS-TTL=" << dnsTTL << " time=" << (int)responseTime << "ms" << std::endl; - +int main(int argc, char* argv[]) { + pcpp::AppName::init(argc, argv); + + std::string hostname; + bool hostnameProvided = false; + std::string interfaceNameOrIP; + bool interfaceNameOrIPProvided = false; + pcpp::IPv4Address dnsServerIP; + pcpp::IPv4Address gatewayIP; + int timeoutSec = -1; + + int optionIndex = 0; + int opt = 0; + + while ((opt = getopt_long(argc, argv, "i:d:g:s:t:hvl", DNSResolverOptions, + &optionIndex)) != -1) { + switch (opt) { + case 0: { + break; + } + case 'h': { + printUsage(); + exit(0); + } + case 'v': { + printAppVersion(); + break; + } + case 'l': { + listInterfaces(); + exit(0); + } + case 'i': { + interfaceNameOrIP = optarg; + interfaceNameOrIPProvided = true; + break; + } + case 'd': { + dnsServerIP = pcpp::IPv4Address(static_cast(optarg)); + break; + } + case 'g': { + gatewayIP = pcpp::IPv4Address(static_cast(optarg)); + break; + } + case 's': { + hostname = optarg; + hostnameProvided = true; + break; + } + case 't': { + timeoutSec = atoi(optarg); + break; + } + default: { + printUsage(); + exit(1); + } + } + } + + // make sure that hostname is provided + if (!hostnameProvided) + EXIT_WITH_ERROR("Hostname not provided"); + + // find the interface to send the DNS request from + pcpp::PcapLiveDevice* dev = nullptr; + + // if interface name or IP was provided - find the device accordingly + if (interfaceNameOrIPProvided) { + dev = pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDeviceByIpOrName( + interfaceNameOrIP); + if (dev == nullptr) + EXIT_WITH_ERROR("Couldn't find interface by provided IP address or name"); + } + // if interface name or IP was not provided - find a device that has a default + // gateway + else { + const std::vector& devList = + pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDevicesList(); + + for (std::vector::const_iterator iter = + devList.begin(); + iter != devList.end(); iter++) { + if ((*iter)->getDefaultGateway().isValid()) { + dev = *iter; + break; + } + } + + if (dev == nullptr) + EXIT_WITH_ERROR("Couldn't find an interface with a default gateway"); + } + + std::cout << "Using interface '" << dev->getIPv4Address() << "'" << std::endl; + + // suppressing errors to avoid cluttering stdout + pcpp::Logger::getInstance().suppressLogs(); + + // make sure the app closes the device upon termination + pcpp::ApplicationEventHandler::getInstance().onApplicationInterrupted( + onApplicationInterrupted, dev); + + // find the IPv4 address for provided hostname + double responseTime = 0; + uint32_t dnsTTL = 0; + pcpp::IPv4Address resultIP = pcpp::NetworkUtils::getInstance().getIPv4Address( + hostname, dev, responseTime, dnsTTL, timeoutSec, dnsServerIP, gatewayIP); + + // print resolved IPv4 address if found + if (!resultIP.isValid()) + std::cout << std::endl + << "Could not resolve hostname [" << hostname << "]" << std::endl; + else + std::cout << std::endl + << "IP address of [" << hostname << "] is: " << resultIP + << " DNS-TTL=" << dnsTTL << " time=" << (int)responseTime + << "ms" << std::endl; } diff --git a/Examples/DnsSpoofing/main.cpp b/Examples/DnsSpoofing/main.cpp index 268231a57e..6d95b268dc 100644 --- a/Examples/DnsSpoofing/main.cpp +++ b/Examples/DnsSpoofing/main.cpp @@ -1,452 +1,464 @@ /** * DNS spoofing example application * ================================ - * This application does simple DNS spoofing. It's provided with interface name or IP and starts capturing DNS requests on that - * interface. Each DNS request that matches is edited and turned into a DNS response with a user-provided IP address as the resolved IP. - * Then it's sent back on the network on the same interface + * This application does simple DNS spoofing. It's provided with interface name + * or IP and starts capturing DNS requests on that interface. Each DNS request + * that matches is edited and turned into a DNS response with a user-provided IP + * address as the resolved IP. Then it's sent back on the network on the same + * interface */ -#include -#include #include +#include +#include #include #include -#include +#include #if !defined(_WIN32) #include #endif -#include "IpAddress.h" -#include "RawPacket.h" -#include "ProtocolType.h" -#include "Packet.h" +#include "DnsLayer.h" #include "EthLayer.h" #include "IPv4Layer.h" #include "IPv6Layer.h" -#include "UdpLayer.h" -#include "DnsLayer.h" +#include "IpAddress.h" +#include "Packet.h" #include "PcapFilter.h" #include "PcapLiveDevice.h" #include "PcapLiveDeviceList.h" -#include "TablePrinter.h" -#include "SystemUtils.h" #include "PcapPlusPlusVersion.h" +#include "ProtocolType.h" +#include "RawPacket.h" +#include "SystemUtils.h" +#include "TablePrinter.h" +#include "UdpLayer.h" #include - -#define EXIT_WITH_ERROR(reason) do { \ - printUsage(); \ - std::cout << std::endl << "ERROR: " << reason << std::endl << std::endl; \ - exit(1); \ - } while(0) - - -static struct option DnsSpoofingOptions[] = -{ - {"interface", required_argument, nullptr, 'i'}, - {"spoof-dns-server", required_argument, nullptr, 'd'}, - {"client-ip", required_argument, nullptr, 'c'}, - {"host-list", required_argument, nullptr, 'o'}, - {"help", no_argument, nullptr, 'h'}, - {"version", no_argument, nullptr, 'v'}, - {"list", no_argument, nullptr, 'l'}, - {nullptr, 0, nullptr, 0} -}; - +#define EXIT_WITH_ERROR(reason) \ + do { \ + printUsage(); \ + std::cout << std::endl \ + << "ERROR: " << reason << std::endl \ + << std::endl; \ + exit(1); \ + } while (0) + +static struct option DnsSpoofingOptions[] = { + {"interface", required_argument, nullptr, 'i'}, + {"spoof-dns-server", required_argument, nullptr, 'd'}, + {"client-ip", required_argument, nullptr, 'c'}, + {"host-list", required_argument, nullptr, 'o'}, + {"help", no_argument, nullptr, 'h'}, + {"version", no_argument, nullptr, 'v'}, + {"list", no_argument, nullptr, 'l'}, + {nullptr, 0, nullptr, 0}}; /** - * A struct that holds all counters that are collected during application runtime + * A struct that holds all counters that are collected during application + * runtime */ -struct DnsSpoofStats -{ - int numOfSpoofedDnsRequests; - std::map spoofedHosts; +struct DnsSpoofStats { + int numOfSpoofedDnsRequests; + std::map spoofedHosts; - DnsSpoofStats() : numOfSpoofedDnsRequests(0) {} + DnsSpoofStats() : numOfSpoofedDnsRequests(0) {} }; - /** * A struct that holds all arguments passed to handleDnsRequest() */ -struct DnsSpoofingArgs -{ - pcpp::IPAddress dnsServer; - std::vector dnsHostsToSpoof; - DnsSpoofStats stats; - bool shouldStop; - - DnsSpoofingArgs() : shouldStop(false) {} -}; +struct DnsSpoofingArgs { + pcpp::IPAddress dnsServer; + std::vector dnsHostsToSpoof; + DnsSpoofStats stats; + bool shouldStop; + DnsSpoofingArgs() : shouldStop(false) {} +}; /** * Print application usage */ -void printUsage() -{ - std::cout << std::endl - << "Usage:" << std::endl - << "------" << std::endl - << pcpp::AppName::get() << " [-hvl] [-o host1,host2,...,host_n] [-c ip_address] -i interface -d ip_address" << std::endl - << std::endl - << "Options:" << std::endl - << std::endl - << " -h : Displays this help message and exits" << std::endl - << " -v : Displays the current version and exists" << std::endl - << " -l : Print the list of available interfaces" << std::endl - << " -i interface : The interface name or interface IP address to use." << std::endl - << " Use the -l switch to see all interfaces" << std::endl - << " -d ip_address : The IP address of the spoofed DNS server. Supports both IPv4 and IPv6" << std::endl - << " (all responses will be sent with this IP address)" << std::endl - << " -c ip_address : Spoof only DNS requests coming from a specific IP address" << std::endl - << " -o host1,host2,...,host_n : A comma-separated list of hosts to spoof. If list is not given," << std::endl - << " all hosts will be spoofed. If an host contains '*' all sub-domains" << std::endl - << " will be spoofed, for example: if '*.google.com' is given" << std::endl - << " then 'mail.google.com', 'tools.google.com', etc. will be spoofed" << std::endl - << std::endl; +void printUsage() { + std::cout << std::endl + << "Usage:" << std::endl + << "------" << std::endl + << pcpp::AppName::get() + << " [-hvl] [-o host1,host2,...,host_n] [-c ip_address] -i " + "interface -d ip_address" + << std::endl + << std::endl + << "Options:" << std::endl + << std::endl + << " -h : Displays this help message " + "and exits" + << std::endl + << " -v : Displays the current version " + "and exists" + << std::endl + << " -l : Print the list of available " + "interfaces" + << std::endl + << " -i interface : The interface name or " + "interface IP address to use." + << std::endl + << " Use the -l switch to see all " + "interfaces" + << std::endl + << " -d ip_address : The IP address of the " + "spoofed DNS server. Supports both IPv4 and IPv6" + << std::endl + << " (all responses will be sent " + "with this IP address)" + << std::endl + << " -c ip_address : Spoof only DNS requests " + "coming from a specific IP address" + << std::endl + << " -o host1,host2,...,host_n : A comma-separated list of " + "hosts to spoof. If list is not given," + << std::endl + << " all hosts will be spoofed. " + "If an host contains '*' all sub-domains" + << std::endl + << " will be spoofed, for " + "example: if '*.google.com' is given" + << std::endl + << " then 'mail.google.com', " + "'tools.google.com', etc. will be spoofed" + << std::endl + << std::endl; } - /** * Print application version */ -void printAppVersion() -{ - std::cout - << pcpp::AppName::get() << " " << pcpp::getPcapPlusPlusVersionFull() << std::endl - << "Built: " << pcpp::getBuildDateTime() << std::endl - << "Built from: " << pcpp::getGitInfo() << std::endl; - exit(0); +void printAppVersion() { + std::cout << pcpp::AppName::get() << " " << pcpp::getPcapPlusPlusVersionFull() + << std::endl + << "Built: " << pcpp::getBuildDateTime() << std::endl + << "Built from: " << pcpp::getGitInfo() << std::endl; + exit(0); } - /** * Go over all interfaces and output their names */ -void listInterfaces() -{ - const std::vector& devList = pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDevicesList(); - - std::cout << std::endl << "Network interfaces:" << std::endl; - for (std::vector::const_iterator iter = devList.begin(); iter != devList.end(); iter++) - { - std::cout << " -> Name: '" << (*iter)->getName() << "' IP address: " << (*iter)->getIPv4Address().toString() << std::endl; - } - exit(0); +void listInterfaces() { + const std::vector& devList = + pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDevicesList(); + + std::cout << std::endl + << "Network interfaces:" << std::endl; + for (std::vector::const_iterator iter = + devList.begin(); + iter != devList.end(); iter++) { + std::cout << " -> Name: '" << (*iter)->getName() + << "' IP address: " << (*iter)->getIPv4Address().toString() + << std::endl; + } + exit(0); } - /** - * The method that is called each time a DNS request is received. This methods turns the DNS request into a DNS response with the - * spoofed information and sends it back to the network + * The method that is called each time a DNS request is received. This methods + * turns the DNS request into a DNS response with the spoofed information and + * sends it back to the network */ -void handleDnsRequest(pcpp::RawPacket* packet, pcpp::PcapLiveDevice* dev, void* cookie) -{ - DnsSpoofingArgs* args = (DnsSpoofingArgs*)cookie; - - // create a parsed packet from the raw packet - pcpp::Packet dnsRequest(packet); - - if (!dnsRequest.isPacketOfType(pcpp::DNS) || !dnsRequest.isPacketOfType(pcpp::IP) || !dnsRequest.isPacketOfType(pcpp::UDP) || !dnsRequest.isPacketOfType(pcpp::Ethernet)) - return; - - // extract all packet layers - pcpp::EthLayer* ethLayer = dnsRequest.getLayerOfType(); - pcpp::IPLayer* ipLayer = dnsRequest.getLayerOfType(); - pcpp::UdpLayer* udpLayer = dnsRequest.getLayerOfType(); - pcpp::DnsLayer* dnsLayer = dnsRequest.getLayerOfType(); - - // skip DNS requests with more than 1 request or with 0 requests - if (dnsLayer->getDnsHeader()->numberOfQuestions != pcpp::hostToNet16(1) || dnsLayer->getFirstQuery() == nullptr) - return; - - // skip DNS requests which are not of class IN and type A (IPv4) or AAAA (IPv6) - pcpp::DnsType dnsType = (args->dnsServer.isIPv4()? pcpp::DNS_TYPE_A : pcpp::DNS_TYPE_AAAA); - pcpp::DnsQuery* dnsQuery = dnsLayer->getFirstQuery(); - if (dnsQuery->getDnsType() != dnsType || dnsQuery->getDnsClass() != pcpp::DNS_CLASS_IN) - return; - - // empty dnsHostsToSpoof means spoofing all hosts - if (!args->dnsHostsToSpoof.empty()) - { - bool hostMatch = false; - - // go over all hosts in dnsHostsToSpoof list and see if current query matches one of them - for (std::vector::iterator iter = args->dnsHostsToSpoof.begin(); iter != args->dnsHostsToSpoof.end(); iter++) - { - if (dnsLayer->getQuery(*iter, false) != nullptr) - { - hostMatch = true; - break; - } - } - - if (!hostMatch) - return; - } - - - // create a response out of the request packet - - // reverse src and dst MAC addresses - pcpp::MacAddress srcMac = ethLayer->getSourceMac(); - ethLayer->setSourceMac(ethLayer->getDestMac()); - ethLayer->setDestMac(srcMac); - - // reverse src and dst IP addresses - pcpp::IPAddress srcIP = ipLayer->getSrcIPAddress(); - pcpp::IPv4Layer* ip4Layer = dynamic_cast(ipLayer); - pcpp::IPv6Layer* ip6Layer = dynamic_cast(ipLayer); - if (ip4Layer != nullptr) - { - ip4Layer->setSrcIPv4Address(ip4Layer->getDstIPv4Address()); - ip4Layer->setDstIPv4Address(srcIP.getIPv4()); - ip4Layer->getIPv4Header()->ipId = 0; - } - else - { - ip6Layer->setSrcIPv6Address(ip6Layer->getDstIPv6Address()); - ip6Layer->setDstIPv6Address(srcIP.getIPv6()); - } - - // reverse src and dst UDP ports - uint16_t srcPort = udpLayer->getUdpHeader()->portSrc; - udpLayer->getUdpHeader()->portSrc = udpLayer->getUdpHeader()->portDst; - udpLayer->getUdpHeader()->portDst = srcPort; - - // add DNS response - dnsLayer->getDnsHeader()->queryOrResponse = 1; - if (args->dnsServer.isIPv4()) - { - pcpp::IPv4DnsResourceData dnsServer(args->dnsServer.getIPv4()); - if (!dnsLayer->addAnswer(dnsQuery->getName(), pcpp::DNS_TYPE_A, pcpp::DNS_CLASS_IN, 1, &dnsServer)) - return; - } - else - { - pcpp::IPv6DnsResourceData dnsServer(args->dnsServer.getIPv6()); - if (!dnsLayer->addAnswer(dnsQuery->getName(), pcpp::DNS_TYPE_AAAA, pcpp::DNS_CLASS_IN, 1, &dnsServer)) - return; - } - - dnsRequest.computeCalculateFields(); - - // send DNS response back to the network - if (!dev->sendPacket(&dnsRequest)) - return; - - args->stats.numOfSpoofedDnsRequests++; - args->stats.spoofedHosts[dnsQuery->getName()]++; +void handleDnsRequest(pcpp::RawPacket* packet, pcpp::PcapLiveDevice* dev, + void* cookie) { + DnsSpoofingArgs* args = (DnsSpoofingArgs*)cookie; + + // create a parsed packet from the raw packet + pcpp::Packet dnsRequest(packet); + + if (!dnsRequest.isPacketOfType(pcpp::DNS) || + !dnsRequest.isPacketOfType(pcpp::IP) || + !dnsRequest.isPacketOfType(pcpp::UDP) || + !dnsRequest.isPacketOfType(pcpp::Ethernet)) + return; + + // extract all packet layers + pcpp::EthLayer* ethLayer = dnsRequest.getLayerOfType(); + pcpp::IPLayer* ipLayer = dnsRequest.getLayerOfType(); + pcpp::UdpLayer* udpLayer = dnsRequest.getLayerOfType(); + pcpp::DnsLayer* dnsLayer = dnsRequest.getLayerOfType(); + + // skip DNS requests with more than 1 request or with 0 requests + if (dnsLayer->getDnsHeader()->numberOfQuestions != pcpp::hostToNet16(1) || + dnsLayer->getFirstQuery() == nullptr) + return; + + // skip DNS requests which are not of class IN and type A (IPv4) or AAAA + // (IPv6) + pcpp::DnsType dnsType = + (args->dnsServer.isIPv4() ? pcpp::DNS_TYPE_A : pcpp::DNS_TYPE_AAAA); + pcpp::DnsQuery* dnsQuery = dnsLayer->getFirstQuery(); + if (dnsQuery->getDnsType() != dnsType || + dnsQuery->getDnsClass() != pcpp::DNS_CLASS_IN) + return; + + // empty dnsHostsToSpoof means spoofing all hosts + if (!args->dnsHostsToSpoof.empty()) { + bool hostMatch = false; + + // go over all hosts in dnsHostsToSpoof list and see if current query + // matches one of them + for (std::vector::iterator iter = + args->dnsHostsToSpoof.begin(); + iter != args->dnsHostsToSpoof.end(); iter++) { + if (dnsLayer->getQuery(*iter, false) != nullptr) { + hostMatch = true; + break; + } + } + + if (!hostMatch) + return; + } + + // create a response out of the request packet + + // reverse src and dst MAC addresses + pcpp::MacAddress srcMac = ethLayer->getSourceMac(); + ethLayer->setSourceMac(ethLayer->getDestMac()); + ethLayer->setDestMac(srcMac); + + // reverse src and dst IP addresses + pcpp::IPAddress srcIP = ipLayer->getSrcIPAddress(); + pcpp::IPv4Layer* ip4Layer = dynamic_cast(ipLayer); + pcpp::IPv6Layer* ip6Layer = dynamic_cast(ipLayer); + if (ip4Layer != nullptr) { + ip4Layer->setSrcIPv4Address(ip4Layer->getDstIPv4Address()); + ip4Layer->setDstIPv4Address(srcIP.getIPv4()); + ip4Layer->getIPv4Header()->ipId = 0; + } else { + ip6Layer->setSrcIPv6Address(ip6Layer->getDstIPv6Address()); + ip6Layer->setDstIPv6Address(srcIP.getIPv6()); + } + + // reverse src and dst UDP ports + uint16_t srcPort = udpLayer->getUdpHeader()->portSrc; + udpLayer->getUdpHeader()->portSrc = udpLayer->getUdpHeader()->portDst; + udpLayer->getUdpHeader()->portDst = srcPort; + + // add DNS response + dnsLayer->getDnsHeader()->queryOrResponse = 1; + if (args->dnsServer.isIPv4()) { + pcpp::IPv4DnsResourceData dnsServer(args->dnsServer.getIPv4()); + if (!dnsLayer->addAnswer(dnsQuery->getName(), pcpp::DNS_TYPE_A, + pcpp::DNS_CLASS_IN, 1, &dnsServer)) + return; + } else { + pcpp::IPv6DnsResourceData dnsServer(args->dnsServer.getIPv6()); + if (!dnsLayer->addAnswer(dnsQuery->getName(), pcpp::DNS_TYPE_AAAA, + pcpp::DNS_CLASS_IN, 1, &dnsServer)) + return; + } + + dnsRequest.computeCalculateFields(); + + // send DNS response back to the network + if (!dev->sendPacket(&dnsRequest)) + return; + + args->stats.numOfSpoofedDnsRequests++; + args->stats.spoofedHosts[dnsQuery->getName()]++; } - /** - * An auxiliary method for sorting the string count map. Used for printing the summary of spoofed hosts + * An auxiliary method for sorting the string count map. Used for printing the + * summary of spoofed hosts */ -bool stringCountComparer(const std::pair& first, const std::pair& second) -{ - if (first.second == second.second) - { - return first.first > second.first; - } - return first.second > second.second; +bool stringCountComparer(const std::pair& first, + const std::pair& second) { + if (first.second == second.second) { + return first.first > second.first; + } + return first.second > second.second; } - /** - * A callback for application interrupted event (ctrl+c): print DNS spoofing summary + * A callback for application interrupted event (ctrl+c): print DNS spoofing + * summary */ -void onApplicationInterrupted(void* cookie) -{ - DnsSpoofingArgs* args = (DnsSpoofingArgs*)cookie; - if (args->stats.spoofedHosts.size() == 0) - { - std::cout << std::endl << "Application closing. No hosts were spoofed." << std::endl; - } - else - { - std::cout << std::endl - << "Summary of spoofed hosts:" << std::endl - << "-------------------------" << std::endl - << std::endl - << "Total spoofed: " << args->stats.numOfSpoofedDnsRequests << std::endl - << "Number of host spoofed: " << args->stats.spoofedHosts.size() << std::endl - << std::endl; - - // create a table - std::vector columnNames; - columnNames.push_back("Host"); - columnNames.push_back("# of times spoofed"); - std::vector columnsWidths; - columnsWidths.push_back(40); - columnsWidths.push_back(18); - pcpp::TablePrinter printer(columnNames, columnsWidths); - - // sort the spoofed hosts map so the most spoofed hosts will be first - // since it's not possible to sort a std::map you must copy it to a std::vector and sort it then - std::vector > map2vec(args->stats.spoofedHosts.begin(), args->stats.spoofedHosts.end()); - std::sort(map2vec.begin(),map2vec.end(), &stringCountComparer); - - // go over all items (hosts + count) in the sorted vector and print them - for(std::vector >::iterator iter = map2vec.begin(); - iter != map2vec.end(); - iter++) - { - std::stringstream values; - values << iter->first << "|" << iter->second; - printer.printRow(values.str(), '|'); - } - } - - args->shouldStop = true; +void onApplicationInterrupted(void* cookie) { + DnsSpoofingArgs* args = (DnsSpoofingArgs*)cookie; + if (args->stats.spoofedHosts.size() == 0) { + std::cout << std::endl + << "Application closing. No hosts were spoofed." << std::endl; + } else { + std::cout << std::endl + << "Summary of spoofed hosts:" << std::endl + << "-------------------------" << std::endl + << std::endl + << "Total spoofed: " + << args->stats.numOfSpoofedDnsRequests << std::endl + << "Number of host spoofed: " << args->stats.spoofedHosts.size() + << std::endl + << std::endl; + + // create a table + std::vector columnNames; + columnNames.push_back("Host"); + columnNames.push_back("# of times spoofed"); + std::vector columnsWidths; + columnsWidths.push_back(40); + columnsWidths.push_back(18); + pcpp::TablePrinter printer(columnNames, columnsWidths); + + // sort the spoofed hosts map so the most spoofed hosts will be first + // since it's not possible to sort a std::map you must copy it to a + // std::vector and sort it then + std::vector> map2vec( + args->stats.spoofedHosts.begin(), args->stats.spoofedHosts.end()); + std::sort(map2vec.begin(), map2vec.end(), &stringCountComparer); + + // go over all items (hosts + count) in the sorted vector and print them + for (std::vector>::iterator iter = + map2vec.begin(); + iter != map2vec.end(); iter++) { + std::stringstream values; + values << iter->first << "|" << iter->second; + printer.printRow(values.str(), '|'); + } + } + + args->shouldStop = true; } - /** * Activate DNS spoofing: prepare the device and start capturing DNS requests */ -void doDnsSpoofing(pcpp::PcapLiveDevice* dev, const pcpp::IPAddress& dnsServer, const pcpp::IPAddress& clientIP, const std::vector &dnsHostsToSpoof) -{ - // open device - if (!dev->open()) - EXIT_WITH_ERROR("Cannot open capture device"); - - // set a filter to capture only DNS requests and client IP if provided - pcpp::PortFilter dnsPortFilter(53, pcpp::DST); - if (!clientIP.isValid()) - { - if (!dev->setFilter(dnsPortFilter)) - EXIT_WITH_ERROR("Cannot set DNS filter for device"); - } - else - { - pcpp::IPFilter clientIpFilter(clientIP.toString(), pcpp::SRC); - std::vector filterForAnd; - filterForAnd.push_back(&dnsPortFilter); - filterForAnd.push_back(&clientIpFilter); - pcpp::AndFilter andFilter(filterForAnd); - - if (!dev->setFilter(andFilter)) - EXIT_WITH_ERROR("Cannot set DNS and client IP filter for device"); - } - - // make args for callback - DnsSpoofingArgs args; - args.dnsServer = dnsServer; - args.dnsHostsToSpoof = dnsHostsToSpoof; - - // start capturing DNS requests - if (!dev->startCapture(handleDnsRequest, &args)) - EXIT_WITH_ERROR("Cannot start packet capture"); - - - // register the on app close event to print summary stats on app termination - pcpp::ApplicationEventHandler::getInstance().onApplicationInterrupted(onApplicationInterrupted, &args); - - // run an endless loop until ctrl+c is pressed - while (!args.shouldStop) - { - std::cout << "Spoofed " << args.stats.numOfSpoofedDnsRequests << " DNS requests so far" << std::endl; - pcpp::multiPlatformSleep(5); - } +void doDnsSpoofing(pcpp::PcapLiveDevice* dev, const pcpp::IPAddress& dnsServer, + const pcpp::IPAddress& clientIP, + const std::vector& dnsHostsToSpoof) { + // open device + if (!dev->open()) + EXIT_WITH_ERROR("Cannot open capture device"); + + // set a filter to capture only DNS requests and client IP if provided + pcpp::PortFilter dnsPortFilter(53, pcpp::DST); + if (!clientIP.isValid()) { + if (!dev->setFilter(dnsPortFilter)) + EXIT_WITH_ERROR("Cannot set DNS filter for device"); + } else { + pcpp::IPFilter clientIpFilter(clientIP.toString(), pcpp::SRC); + std::vector filterForAnd; + filterForAnd.push_back(&dnsPortFilter); + filterForAnd.push_back(&clientIpFilter); + pcpp::AndFilter andFilter(filterForAnd); + + if (!dev->setFilter(andFilter)) + EXIT_WITH_ERROR("Cannot set DNS and client IP filter for device"); + } + + // make args for callback + DnsSpoofingArgs args; + args.dnsServer = dnsServer; + args.dnsHostsToSpoof = dnsHostsToSpoof; + + // start capturing DNS requests + if (!dev->startCapture(handleDnsRequest, &args)) + EXIT_WITH_ERROR("Cannot start packet capture"); + + // register the on app close event to print summary stats on app termination + pcpp::ApplicationEventHandler::getInstance().onApplicationInterrupted( + onApplicationInterrupted, &args); + + // run an endless loop until ctrl+c is pressed + while (!args.shouldStop) { + std::cout << "Spoofed " << args.stats.numOfSpoofedDnsRequests + << " DNS requests so far" << std::endl; + pcpp::multiPlatformSleep(5); + } } - /** * main method of the application */ -int main(int argc, char* argv[]) -{ - pcpp::AppName::init(argc, argv); - - int optionIndex = 0; - int opt = 0; - - std::string interfaceNameOrIP; - - pcpp::IPAddress dnsServer; - - pcpp::IPAddress clientIP; - bool clientIpSet = false; - - std::vector hostList; - - while((opt = getopt_long(argc, argv, "i:d:c:o:hvl", DnsSpoofingOptions, &optionIndex)) != -1) - { - switch (opt) - { - case 0: - { - break; - } - case 'h': - { - printUsage(); - exit(0); - } - case 'v': - { - printAppVersion(); - break; - } - case 'l': - { - listInterfaces(); - exit(0); - } - case 'i': - { - interfaceNameOrIP = optarg; - break; - } - case 'd': - { - dnsServer = pcpp::IPAddress(static_cast(optarg)); - break; - } - case 'c': - { - clientIP = pcpp::IPAddress(static_cast(optarg)); - clientIpSet = true; - break; - } - case 'o': - { - std::string input = optarg; - std::istringstream stream(input); - std::string token; - - while(std::getline(stream, token, ',')) - hostList.push_back(token); - break; - } - default: - { - printUsage(); - exit(1); - } - } - } - - pcpp::PcapLiveDevice* dev = nullptr; - - // check if interface argument is IP or name and extract the device - if (interfaceNameOrIP.empty()) - { - EXIT_WITH_ERROR("Interface name or IP weren't provided. Please use the -i switch or -h for help"); - } - - dev = pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDeviceByIpOrName(interfaceNameOrIP); - if (dev == nullptr) - EXIT_WITH_ERROR("Couldn't find interface by provided IP address or name"); - - // verify DNS server IP is a valid IPv4 address - if (dnsServer == pcpp::IPv4Address::Zero || !dnsServer.isValid()) - EXIT_WITH_ERROR("Spoof DNS server IP provided is empty or not a valid IPv4 address"); - - // verify client IP is valid if set - if (clientIpSet && !clientIP.isValid()) - EXIT_WITH_ERROR("Client IP to spoof is invalid"); - - doDnsSpoofing(dev, dnsServer, clientIP, hostList); +int main(int argc, char* argv[]) { + pcpp::AppName::init(argc, argv); + + int optionIndex = 0; + int opt = 0; + + std::string interfaceNameOrIP; + + pcpp::IPAddress dnsServer; + + pcpp::IPAddress clientIP; + bool clientIpSet = false; + + std::vector hostList; + + while ((opt = getopt_long(argc, argv, "i:d:c:o:hvl", DnsSpoofingOptions, + &optionIndex)) != -1) { + switch (opt) { + case 0: { + break; + } + case 'h': { + printUsage(); + exit(0); + } + case 'v': { + printAppVersion(); + break; + } + case 'l': { + listInterfaces(); + exit(0); + } + case 'i': { + interfaceNameOrIP = optarg; + break; + } + case 'd': { + dnsServer = pcpp::IPAddress(static_cast(optarg)); + break; + } + case 'c': { + clientIP = pcpp::IPAddress(static_cast(optarg)); + clientIpSet = true; + break; + } + case 'o': { + std::string input = optarg; + std::istringstream stream(input); + std::string token; + + while (std::getline(stream, token, ',')) + hostList.push_back(token); + break; + } + default: { + printUsage(); + exit(1); + } + } + } + + pcpp::PcapLiveDevice* dev = nullptr; + + // check if interface argument is IP or name and extract the device + if (interfaceNameOrIP.empty()) { + EXIT_WITH_ERROR("Interface name or IP weren't provided. Please use the -i " + "switch or -h for help"); + } + + dev = pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDeviceByIpOrName( + interfaceNameOrIP); + if (dev == nullptr) + EXIT_WITH_ERROR("Couldn't find interface by provided IP address or name"); + + // verify DNS server IP is a valid IPv4 address + if (dnsServer == pcpp::IPv4Address::Zero || !dnsServer.isValid()) + EXIT_WITH_ERROR( + "Spoof DNS server IP provided is empty or not a valid IPv4 address"); + + // verify client IP is valid if set + if (clientIpSet && !clientIP.isValid()) + EXIT_WITH_ERROR("Client IP to spoof is invalid"); + + doDnsSpoofing(dev, dnsServer, clientIP, hostList); } diff --git a/Examples/DpdkBridge/AppWorkerThread.h b/Examples/DpdkBridge/AppWorkerThread.h index 1b8713fcd8..4217aaca50 100644 --- a/Examples/DpdkBridge/AppWorkerThread.h +++ b/Examples/DpdkBridge/AppWorkerThread.h @@ -2,88 +2,77 @@ #include "Common.h" -#include "PacketUtils.h" #include "DpdkDevice.h" #include "DpdkDeviceList.h" +#include "PacketUtils.h" #include "PcapFileDevice.h" - /** - * The worker thread class which does all the work. It's initialized with pointers to the RX and TX devices, then it runs in - * an endless loop which reads packets from the RX device and sends them to the TX device. - * The endless loop is interrupted only when the thread is asked to stop (calling its stop() method) + * The worker thread class which does all the work. It's initialized with + * pointers to the RX and TX devices, then it runs in an endless loop which + * reads packets from the RX device and sends them to the TX device. The endless + * loop is interrupted only when the thread is asked to stop (calling its stop() + * method) */ -class AppWorkerThread : public pcpp::DpdkWorkerThread -{ -private: - AppWorkerConfig& m_WorkerConfig; - bool m_Stop; - uint32_t m_CoreId; - -public: - explicit AppWorkerThread(AppWorkerConfig& workerConfig) : - m_WorkerConfig(workerConfig), m_Stop(true), m_CoreId(MAX_NUM_OF_CORES+1) - { - } - - virtual ~AppWorkerThread() - { - // do nothing - } - - // implement abstract methods - - bool run(uint32_t coreId) - { - m_CoreId = coreId; - m_Stop = false; - pcpp::DpdkDevice* rxDevice = m_WorkerConfig.RxDevice; - pcpp::DpdkDevice* txDevice = m_WorkerConfig.TxDevice; - - // if no DPDK devices were assigned to this worker/core don't enter the main loop and exit - if (!rxDevice || !txDevice) - { - return true; - } - - #define MAX_RECEIVE_BURST 64 - pcpp::MBufRawPacket* packetArr[MAX_RECEIVE_BURST] = {}; - - // main loop, runs until be told to stop - while (!m_Stop) - { - for(uint16_t i = 0; i < m_WorkerConfig.RxQueues; i++) - { - // receive packets from network on the specified DPDK device - uint16_t packetsReceived = rxDevice->receivePackets(packetArr, MAX_RECEIVE_BURST, i); - - if (packetsReceived > 0) - { - // send packets to TX port - txDevice->sendPackets(packetArr, packetsReceived, 0); - } - } - } - - // free packet array (frees all mbufs as well) - for (int i = 0; i < MAX_RECEIVE_BURST; i++) - { - if (packetArr[i] != NULL) - delete packetArr[i]; - } - - return true; - } - - void stop() - { - // assign the stop flag which will cause the main loop to end - m_Stop = true; - } - - uint32_t getCoreId() const - { - return m_CoreId; - } - +class AppWorkerThread : public pcpp::DpdkWorkerThread { + private: + AppWorkerConfig& m_WorkerConfig; + bool m_Stop; + uint32_t m_CoreId; + + public: + explicit AppWorkerThread(AppWorkerConfig& workerConfig) + : m_WorkerConfig(workerConfig), m_Stop(true), + m_CoreId(MAX_NUM_OF_CORES + 1) {} + + virtual ~AppWorkerThread() { + // do nothing + } + + // implement abstract methods + + bool run(uint32_t coreId) { + m_CoreId = coreId; + m_Stop = false; + pcpp::DpdkDevice* rxDevice = m_WorkerConfig.RxDevice; + pcpp::DpdkDevice* txDevice = m_WorkerConfig.TxDevice; + + // if no DPDK devices were assigned to this worker/core don't enter the main + // loop and exit + if (!rxDevice || !txDevice) { + return true; + } + +#define MAX_RECEIVE_BURST 64 + pcpp::MBufRawPacket* packetArr[MAX_RECEIVE_BURST] = {}; + + // main loop, runs until be told to stop + while (!m_Stop) { + for (uint16_t i = 0; i < m_WorkerConfig.RxQueues; i++) { + // receive packets from network on the specified DPDK device + uint16_t packetsReceived = + rxDevice->receivePackets(packetArr, MAX_RECEIVE_BURST, i); + + if (packetsReceived > 0) { + // send packets to TX port + txDevice->sendPackets(packetArr, packetsReceived, 0); + } + } + } + + // free packet array (frees all mbufs as well) + for (int i = 0; i < MAX_RECEIVE_BURST; i++) { + if (packetArr[i] != NULL) + delete packetArr[i]; + } + + return true; + } + + void stop() { + // assign the stop flag which will cause the main loop to end + m_Stop = true; + } + + uint32_t getCoreId() const { return m_CoreId; } }; diff --git a/Examples/DpdkBridge/Common.h b/Examples/DpdkBridge/Common.h index c7c8a09387..c04be8fb63 100644 --- a/Examples/DpdkBridge/Common.h +++ b/Examples/DpdkBridge/Common.h @@ -1,49 +1,51 @@ #pragma once -#include "Packet.h" #include "DpdkDevice.h" +#include "Packet.h" #include -#include -#include -#include #include #include +#include #include #include - +#include +#include /** * Macros for exiting the application with error */ -#define EXIT_WITH_ERROR(reason) do { \ - std::cout << std::endl << "ERROR: " << reason << std::endl << std::endl; \ - exit(1); \ - } while(0) - - -#define EXIT_WITH_ERROR_AND_PRINT_USAGE(reason) do { \ - printUsage(); \ - std::cout << std::endl << "ERROR: " << reason << std::endl << std::endl; \ - exit(1); \ - } while(0) - +#define EXIT_WITH_ERROR(reason) \ + do { \ + std::cout << std::endl \ + << "ERROR: " << reason << std::endl \ + << std::endl; \ + exit(1); \ + } while (0) + +#define EXIT_WITH_ERROR_AND_PRINT_USAGE(reason) \ + do { \ + printUsage(); \ + std::cout << std::endl \ + << "ERROR: " << reason << std::endl \ + << std::endl; \ + exit(1); \ + } while (0) /** * Contains all the configuration needed for the worker thread including: * - Which DPDK port to receive packets from * - Which DPDK port to send packets to */ -struct AppWorkerConfig -{ - uint32_t CoreId; - pcpp::DpdkDevice* RxDevice; - uint16_t RxQueues; - pcpp::DpdkDevice* TxDevice; - - AppWorkerConfig() : CoreId(MAX_NUM_OF_CORES+1), RxDevice(NULL), RxQueues(1), TxDevice(NULL) - { - } +struct AppWorkerConfig { + uint32_t CoreId; + pcpp::DpdkDevice* RxDevice; + uint16_t RxQueues; + pcpp::DpdkDevice* TxDevice; + + AppWorkerConfig() + : CoreId(MAX_NUM_OF_CORES + 1), RxDevice(NULL), RxQueues(1), + TxDevice(NULL) {} }; diff --git a/Examples/DpdkBridge/main.cpp b/Examples/DpdkBridge/main.cpp index dae8604880..60988bbabd 100644 --- a/Examples/DpdkBridge/main.cpp +++ b/Examples/DpdkBridge/main.cpp @@ -1,386 +1,397 @@ /** * DPDK bridge example application * ======================================= - * This application demonstrates how to create a bridge between two network devices using PcapPlusPlus DPDK APIs. - * It listens to two DPDK ports (a.k.a DPDK devices), and forwards all the traffic received on one port to the other, acting like a L2 bridge. + * This application demonstrates how to create a bridge between two network + * devices using PcapPlusPlus DPDK APIs. It listens to two DPDK ports (a.k.a + * DPDK devices), and forwards all the traffic received on one port to the + * other, acting like a L2 bridge. * - * The application is very similar to [DPDK's L2 forwarding example](https://doc.dpdk.org/guides/sample_app_ug/l2_forward_real_virtual.html) - * and demonstrates how to achieve the same functionality with PcapPlusPlus using less and easier to understand C++ code. + * The application is very similar to [DPDK's L2 forwarding + * example](https://doc.dpdk.org/guides/sample_app_ug/l2_forward_real_virtual.html) + * and demonstrates how to achieve the same functionality with PcapPlusPlus + * using less and easier to understand C++ code. * - * The application uses the concept of worker threads. It creates 2 worker threads running in an endless loop (as long as the app is running): - * one for receiving packets on NIC#1 and sending them to NIC#2, and another for receiving packets on NIC#2 and sending them to NIC#1. + * The application uses the concept of worker threads. It creates 2 worker + * threads running in an endless loop (as long as the app is running): one for + * receiving packets on NIC#1 and sending them to NIC#2, and another for + * receiving packets on NIC#2 and sending them to NIC#1. * * __Important__: - * - This application runs only on Linux (DPDK is not supported on Windows and Mac OS X) + * - This application runs only on Linux (DPDK is not supported on Windows and + * Mac OS X) * - This application (like all applications using DPDK) should be run as 'sudo' - * - In order to test this application you need an envorinment where the bridge is connected directly (back-to-back) to the two machines the - * bridge wants to connect -*/ + * - In order to test this application you need an envorinment where the bridge + * is connected directly (back-to-back) to the two machines the bridge wants to + * connect + */ -#include "Common.h" #include "AppWorkerThread.h" +#include "Common.h" #include "DpdkDeviceList.h" #include "IPv4Layer.h" -#include "TcpLayer.h" -#include "UdpLayer.h" -#include "SystemUtils.h" #include "PcapPlusPlusVersion.h" +#include "SystemUtils.h" #include "TablePrinter.h" +#include "TcpLayer.h" +#include "UdpLayer.h" -#include -#include +#include #include -#include +#include #include -#include -#include #include +#include +#include #include - +#include #define COLLECT_STATS_EVERY_SEC 1 #define DEFAULT_MBUF_POOL_SIZE 4095 #define DEFAULT_QUEUE_QUANTITY 1 - -static struct option DpdkBridgeOptions[] = -{ - {"dpdk-ports", required_argument, 0, 'd'}, - {"core-mask", optional_argument, 0, 'c'}, - {"mbuf-pool-size", optional_argument, 0, 'm'}, - {"queue-quantity", optional_argument, 0, 'q'}, - {"help", optional_argument, 0, 'h'}, - {"list", optional_argument, 0, 'l'}, - {"version", optional_argument, 0, 'v'}, - {0, 0, 0, 0} -}; - +static struct option DpdkBridgeOptions[] = { + {"dpdk-ports", required_argument, 0, 'd'}, + {"core-mask", optional_argument, 0, 'c'}, + {"mbuf-pool-size", optional_argument, 0, 'm'}, + {"queue-quantity", optional_argument, 0, 'q'}, + {"help", optional_argument, 0, 'h'}, + {"list", optional_argument, 0, 'l'}, + {"version", optional_argument, 0, 'v'}, + {0, 0, 0, 0}}; /** * Print application usage */ -void printUsage() -{ - std::cout << std::endl - << "Usage:" << std::endl - << "------" << std::endl - << pcpp::AppName::get() << " [-hlv] [-c CORE_MASK] [-m POOL_SIZE] [-q QUEUE_QTY] -d PORT_1,PORT_2" << std::endl - << std::endl - << "Options:" << std::endl - << std::endl - << " -h|--help : Displays this help message and exits" << std::endl - << " -l|--list : Print the list of DPDK ports and exits" << std::endl - << " -v|--version : Displays the current version and exits" << std::endl - << " -c|--core-mask CORE_MASK : Core mask of cores to use. For example: use 7 (binary 0111) to use cores 0,1,2." << std::endl - << " Default is using all cores except management core" << std::endl - << " -m|--mbuf-pool-size POOL_SIZE : DPDK mBuf pool size to initialize DPDK with. Default value is 4095\n" << std::endl - << " -d|--dpdk-ports PORT_1,PORT_2 : A comma-separated list of two DPDK port numbers to be bridged." << std::endl - << " To see all available DPDK ports use the -l switch" << std::endl - << " -q|--queue-quantity QUEUE_QTY : Quantity of RX queues to be opened for each DPDK device. Default value is 1" << std::endl - << std::endl; +void printUsage() { + std::cout + << std::endl + << "Usage:" << std::endl + << "------" << std::endl + << pcpp::AppName::get() + << " [-hlv] [-c CORE_MASK] [-m POOL_SIZE] [-q QUEUE_QTY] -d PORT_1,PORT_2" + << std::endl + << std::endl + << "Options:" << std::endl + << std::endl + << " -h|--help : Displays this help " + "message and exits" + << std::endl + << " -l|--list : Print the list of " + "DPDK ports and exits" + << std::endl + << " -v|--version : Displays the " + "current version and exits" + << std::endl + << " -c|--core-mask CORE_MASK : Core mask of cores " + "to use. For example: use 7 (binary 0111) to use cores 0,1,2." + << std::endl + << " Default is using " + "all cores except management core" + << std::endl + << " -m|--mbuf-pool-size POOL_SIZE : DPDK mBuf pool size " + "to initialize DPDK with. Default value is 4095\n" + << std::endl + << " -d|--dpdk-ports PORT_1,PORT_2 : A comma-separated " + "list of two DPDK port numbers to be bridged." + << std::endl + << " To see all " + "available DPDK ports use the -l switch" + << std::endl + << " -q|--queue-quantity QUEUE_QTY : Quantity of RX " + "queues to be opened for each DPDK device. Default value is 1" + << std::endl + << std::endl; } - /** * Print application version */ -void printAppVersion() -{ - std::cout - << pcpp::AppName::get() << " " << pcpp::getPcapPlusPlusVersionFull() << std::endl - << "Built: " << pcpp::getBuildDateTime() << std::endl - << "Built from: " << pcpp::getGitInfo() << std::endl; - exit(0); +void printAppVersion() { + std::cout << pcpp::AppName::get() << " " << pcpp::getPcapPlusPlusVersionFull() + << std::endl + << "Built: " << pcpp::getBuildDateTime() << std::endl + << "Built from: " << pcpp::getGitInfo() << std::endl; + exit(0); } - /** * Print to console all available DPDK ports. Used by the -l switch */ -void listDpdkPorts() -{ - pcpp::CoreMask coreMaskToUse = pcpp::getCoreMaskForAllMachineCores(); - - // initialize DPDK - if (!pcpp::DpdkDeviceList::initDpdk(coreMaskToUse, DEFAULT_MBUF_POOL_SIZE)) - { - EXIT_WITH_ERROR("couldn't initialize DPDK"); - } - - std::cout << "DPDK port list:" << std::endl; - - // go over all available DPDK devices and print info for each one - std::vector deviceList = pcpp::DpdkDeviceList::getInstance().getDpdkDeviceList(); - for (std::vector::iterator iter = deviceList.begin(); iter != deviceList.end(); iter++) - { - pcpp::DpdkDevice* dev = *iter; - std::cout << " " - << " Port #" << dev->getDeviceId() << ":" - << " MAC address='" << dev->getMacAddress() << "';" - << " PCI address='" << dev->getPciAddress() << "';" - << " PMD='" << dev->getPMDName() << "'" - << std::endl; - } +void listDpdkPorts() { + pcpp::CoreMask coreMaskToUse = pcpp::getCoreMaskForAllMachineCores(); + + // initialize DPDK + if (!pcpp::DpdkDeviceList::initDpdk(coreMaskToUse, DEFAULT_MBUF_POOL_SIZE)) { + EXIT_WITH_ERROR("couldn't initialize DPDK"); + } + + std::cout << "DPDK port list:" << std::endl; + + // go over all available DPDK devices and print info for each one + std::vector deviceList = + pcpp::DpdkDeviceList::getInstance().getDpdkDeviceList(); + for (std::vector::iterator iter = deviceList.begin(); + iter != deviceList.end(); iter++) { + pcpp::DpdkDevice* dev = *iter; + std::cout << " " + << " Port #" << dev->getDeviceId() << ":" + << " MAC address='" << dev->getMacAddress() << "';" + << " PCI address='" << dev->getPciAddress() << "';" + << " PMD='" << dev->getPMDName() << "'" << std::endl; + } } +struct DpdkBridgeArgs { + bool shouldStop; + std::vector* workerThreadsVector; -struct DpdkBridgeArgs -{ - bool shouldStop; - std::vector* workerThreadsVector; - - DpdkBridgeArgs() : shouldStop(false), workerThreadsVector(NULL) {} + DpdkBridgeArgs() : shouldStop(false), workerThreadsVector(NULL) {} }; - /** - * The callback to be called when application is terminated by ctrl-c. Do cleanup and print summary stats + * The callback to be called when application is terminated by ctrl-c. Do + * cleanup and print summary stats */ -void onApplicationInterrupted(void* cookie) -{ - DpdkBridgeArgs* args = (DpdkBridgeArgs*)cookie; +void onApplicationInterrupted(void* cookie) { + DpdkBridgeArgs* args = (DpdkBridgeArgs*)cookie; - std::cout - << std::endl << std::endl - << "Application stopped" - << std::endl; + std::cout << std::endl + << std::endl + << "Application stopped" << std::endl; - // stop worker threads - pcpp::DpdkDeviceList::getInstance().stopDpdkWorkerThreads(); + // stop worker threads + pcpp::DpdkDeviceList::getInstance().stopDpdkWorkerThreads(); - args->shouldStop = true; + args->shouldStop = true; } - /** * Extract and print traffic stats from a device */ -void printStats(pcpp::DpdkDevice* device) -{ - pcpp::DpdkDevice::DpdkDeviceStats stats; - device->getStatistics(stats); - - std::cout << std::endl <<"Statistics for port " << device->getDeviceId() << ":" << std::endl; - - std::vector columnNames; - columnNames.push_back(" "); - columnNames.push_back("Total Packets"); - columnNames.push_back("Packets/sec"); - columnNames.push_back("Total Bytes"); - columnNames.push_back("Bytes/sec"); - - std::vector columnLengths; - columnLengths.push_back(10); - columnLengths.push_back(15); - columnLengths.push_back(15); - columnLengths.push_back(15); - columnLengths.push_back(15); - - pcpp::TablePrinter printer(columnNames, columnLengths); - - std::stringstream totalRx; - totalRx << "rx" << "|" << stats.aggregatedRxStats.packets << "|" << stats.aggregatedRxStats.packetsPerSec << "|" << stats.aggregatedRxStats.bytes << "|" << stats.aggregatedRxStats.bytesPerSec; - printer.printRow(totalRx.str(), '|'); - - std::stringstream totalTx; - totalTx << "tx" << "|" << stats.aggregatedTxStats.packets << "|" << stats.aggregatedTxStats.packetsPerSec << "|" << stats.aggregatedTxStats.bytes << "|" << stats.aggregatedTxStats.bytesPerSec; - printer.printRow(totalTx.str(), '|'); +void printStats(pcpp::DpdkDevice* device) { + pcpp::DpdkDevice::DpdkDeviceStats stats; + device->getStatistics(stats); + + std::cout << std::endl + << "Statistics for port " << device->getDeviceId() << ":" + << std::endl; + + std::vector columnNames; + columnNames.push_back(" "); + columnNames.push_back("Total Packets"); + columnNames.push_back("Packets/sec"); + columnNames.push_back("Total Bytes"); + columnNames.push_back("Bytes/sec"); + + std::vector columnLengths; + columnLengths.push_back(10); + columnLengths.push_back(15); + columnLengths.push_back(15); + columnLengths.push_back(15); + columnLengths.push_back(15); + + pcpp::TablePrinter printer(columnNames, columnLengths); + + std::stringstream totalRx; + totalRx << "rx" + << "|" << stats.aggregatedRxStats.packets << "|" + << stats.aggregatedRxStats.packetsPerSec << "|" + << stats.aggregatedRxStats.bytes << "|" + << stats.aggregatedRxStats.bytesPerSec; + printer.printRow(totalRx.str(), '|'); + + std::stringstream totalTx; + totalTx << "tx" + << "|" << stats.aggregatedTxStats.packets << "|" + << stats.aggregatedTxStats.packetsPerSec << "|" + << stats.aggregatedTxStats.bytes << "|" + << stats.aggregatedTxStats.bytesPerSec; + printer.printRow(totalTx.str(), '|'); } - /** - * main method of the application. Responsible for parsing user args, preparing worker thread configuration, creating the worker threads and activate them. - * At program termination worker threads are stopped, statistics are collected from them and printed to console + * main method of the application. Responsible for parsing user args, preparing + * worker thread configuration, creating the worker threads and activate them. + * At program termination worker threads are stopped, statistics are collected + * from them and printed to console */ -int main(int argc, char* argv[]) -{ - pcpp::AppName::init(argc, argv); - - std::vector dpdkPortVec; - - // if core mask is not provided, use the 3 first cores - pcpp::CoreMask coreMaskToUse = (pcpp::getCoreMaskForAllMachineCores() & 7); - - int optionIndex = 0; - int opt = 0; - - uint32_t mBufPoolSize = DEFAULT_MBUF_POOL_SIZE; - uint16_t queueQuantity = DEFAULT_QUEUE_QUANTITY; - - while((opt = getopt_long(argc, argv, "d:c:m:q:hvl", DpdkBridgeOptions, &optionIndex)) != -1) - { - switch (opt) - { - case 0: - { - break; - } - case 'c': - { - coreMaskToUse = atoi(optarg); - break; - } - case 'd': - { - std::string portListAsString = std::string(optarg); - std::stringstream stream(portListAsString); - std::string portAsString; - int port; - // break comma-separated string into string list - while(getline(stream, portAsString, ',')) - { - char c; - std::stringstream stream2(portAsString); - stream2 >> port; - if (stream2.fail() || stream2.get(c)) - { - // not an integer - EXIT_WITH_ERROR_AND_PRINT_USAGE("DPDK ports list is invalid"); - } - dpdkPortVec.push_back(port); - } - // verify list contains two ports - if(dpdkPortVec.size()!=2) - { - EXIT_WITH_ERROR_AND_PRINT_USAGE("DPDK list must contain two values"); - } - break; - } - case 'm': - { - mBufPoolSize = atoi(optarg); - break; - } - case 'q': - { - queueQuantity = atoi(optarg); - break; - } - case 'h': - { - printUsage(); - exit(0); - } - case 'l': - { - listDpdkPorts(); - exit(0); - } - case 'v': - { - printAppVersion(); - break; - } - default: - { - printUsage(); - exit(0); - } - } - } - - // verify list is not empty - if (dpdkPortVec.empty()) - { - EXIT_WITH_ERROR_AND_PRINT_USAGE("DPDK ports list is empty. Please use the -d switch"); - } - - // extract core vector from core mask - std::vector coresToUse; - createCoreVectorFromCoreMask(coreMaskToUse, coresToUse); - - // need minimum of 3 cores to start - 1 management core + 1 (or more) worker thread(s) - if (coresToUse.size() < 3) - { - EXIT_WITH_ERROR("Needed minimum of 3 cores to start the application"); - } - - // initialize DPDK - if (!pcpp::DpdkDeviceList::initDpdk(coreMaskToUse, mBufPoolSize)) - { - EXIT_WITH_ERROR("Couldn't initialize DPDK"); - } - - // removing DPDK master core from core mask because DPDK worker threads cannot run on master core - coreMaskToUse = coreMaskToUse & ~(pcpp::DpdkDeviceList::getInstance().getDpdkMasterCore().Mask); - - // re-calculate cores to use after removing master core - coresToUse.clear(); - createCoreVectorFromCoreMask(coreMaskToUse, coresToUse); - - // collect the list of DPDK devices - std::vector dpdkDevicesToUse; - for (std::vector::iterator iter = dpdkPortVec.begin(); iter != dpdkPortVec.end(); iter++) - { - pcpp::DpdkDevice* dev = pcpp::DpdkDeviceList::getInstance().getDeviceByPort(*iter); - if (dev == NULL) - { - EXIT_WITH_ERROR("DPDK device for port " << *iter << " doesn't exist"); - } - dpdkDevicesToUse.push_back(dev); - } - - // go over all devices and open them - for (std::vector::iterator iter = dpdkDevicesToUse.begin(); iter != dpdkDevicesToUse.end(); iter++) - { - if (!(*iter)->openMultiQueues(queueQuantity, 1)) - { - EXIT_WITH_ERROR("Couldn't open DPDK device #" << (*iter)->getDeviceId() << ", PMD '" << (*iter)->getPMDName() << "'"); - } - } - - // prepare configuration for every core - AppWorkerConfig workerConfigArr[2]; - workerConfigArr[0].CoreId = coresToUse.at(0).Id; - workerConfigArr[0].RxDevice = dpdkDevicesToUse.at(0); - workerConfigArr[0].RxQueues = queueQuantity; - workerConfigArr[0].TxDevice = dpdkDevicesToUse.at(1); - workerConfigArr[1].CoreId = coresToUse.at(1).Id; - workerConfigArr[1].RxDevice = dpdkDevicesToUse.at(1); - workerConfigArr[1].RxQueues = queueQuantity; - workerConfigArr[1].TxDevice = dpdkDevicesToUse.at(0); - - // create worker thread for every core - std::vector workerThreadVec; - workerThreadVec.push_back(new AppWorkerThread(workerConfigArr[0])); - workerThreadVec.push_back(new AppWorkerThread(workerConfigArr[1])); - - // start all worker threads - if (!pcpp::DpdkDeviceList::getInstance().startDpdkWorkerThreads(coreMaskToUse, workerThreadVec)) - { - EXIT_WITH_ERROR("Couldn't start worker threads"); - } - - // register the on app close event to print summary stats on app termination - DpdkBridgeArgs args; - args.workerThreadsVector = &workerThreadVec; - pcpp::ApplicationEventHandler::getInstance().onApplicationInterrupted(onApplicationInterrupted, &args); - - // infinite loop (until program is terminated) - uint64_t counter = 0; - int statsCounter = 1; - - // Keep running while flag is on - while (!args.shouldStop) - { - // Sleep for 1 second - sleep(1); - - // Print stats every COLLECT_STATS_EVERY_SEC seconds - // cppcheck-suppress moduloofone - if (counter % COLLECT_STATS_EVERY_SEC == 0) - { - // Clear screen and move to top left - std::cout << "\033[2J\033[1;1H"; - - // Print devices traffic stats - std::cout - << "Stats #" << statsCounter++ << std::endl - << "==========" << std::endl; - printStats(dpdkDevicesToUse.at(0)); - printStats(dpdkDevicesToUse.at(1)); - } - counter++; - } +int main(int argc, char* argv[]) { + pcpp::AppName::init(argc, argv); + + std::vector dpdkPortVec; + + // if core mask is not provided, use the 3 first cores + pcpp::CoreMask coreMaskToUse = (pcpp::getCoreMaskForAllMachineCores() & 7); + + int optionIndex = 0; + int opt = 0; + + uint32_t mBufPoolSize = DEFAULT_MBUF_POOL_SIZE; + uint16_t queueQuantity = DEFAULT_QUEUE_QUANTITY; + + while ((opt = getopt_long(argc, argv, "d:c:m:q:hvl", DpdkBridgeOptions, + &optionIndex)) != -1) { + switch (opt) { + case 0: { + break; + } + case 'c': { + coreMaskToUse = atoi(optarg); + break; + } + case 'd': { + std::string portListAsString = std::string(optarg); + std::stringstream stream(portListAsString); + std::string portAsString; + int port; + // break comma-separated string into string list + while (getline(stream, portAsString, ',')) { + char c; + std::stringstream stream2(portAsString); + stream2 >> port; + if (stream2.fail() || stream2.get(c)) { + // not an integer + EXIT_WITH_ERROR_AND_PRINT_USAGE("DPDK ports list is invalid"); + } + dpdkPortVec.push_back(port); + } + // verify list contains two ports + if (dpdkPortVec.size() != 2) { + EXIT_WITH_ERROR_AND_PRINT_USAGE("DPDK list must contain two values"); + } + break; + } + case 'm': { + mBufPoolSize = atoi(optarg); + break; + } + case 'q': { + queueQuantity = atoi(optarg); + break; + } + case 'h': { + printUsage(); + exit(0); + } + case 'l': { + listDpdkPorts(); + exit(0); + } + case 'v': { + printAppVersion(); + break; + } + default: { + printUsage(); + exit(0); + } + } + } + + // verify list is not empty + if (dpdkPortVec.empty()) { + EXIT_WITH_ERROR_AND_PRINT_USAGE( + "DPDK ports list is empty. Please use the -d switch"); + } + + // extract core vector from core mask + std::vector coresToUse; + createCoreVectorFromCoreMask(coreMaskToUse, coresToUse); + + // need minimum of 3 cores to start - 1 management core + 1 (or more) worker + // thread(s) + if (coresToUse.size() < 3) { + EXIT_WITH_ERROR("Needed minimum of 3 cores to start the application"); + } + + // initialize DPDK + if (!pcpp::DpdkDeviceList::initDpdk(coreMaskToUse, mBufPoolSize)) { + EXIT_WITH_ERROR("Couldn't initialize DPDK"); + } + + // removing DPDK master core from core mask because DPDK worker threads cannot + // run on master core + coreMaskToUse = + coreMaskToUse & + ~(pcpp::DpdkDeviceList::getInstance().getDpdkMasterCore().Mask); + + // re-calculate cores to use after removing master core + coresToUse.clear(); + createCoreVectorFromCoreMask(coreMaskToUse, coresToUse); + + // collect the list of DPDK devices + std::vector dpdkDevicesToUse; + for (std::vector::iterator iter = dpdkPortVec.begin(); + iter != dpdkPortVec.end(); iter++) { + pcpp::DpdkDevice* dev = + pcpp::DpdkDeviceList::getInstance().getDeviceByPort(*iter); + if (dev == NULL) { + EXIT_WITH_ERROR("DPDK device for port " << *iter << " doesn't exist"); + } + dpdkDevicesToUse.push_back(dev); + } + + // go over all devices and open them + for (std::vector::iterator iter = + dpdkDevicesToUse.begin(); + iter != dpdkDevicesToUse.end(); iter++) { + if (!(*iter)->openMultiQueues(queueQuantity, 1)) { + EXIT_WITH_ERROR("Couldn't open DPDK device #" + << (*iter)->getDeviceId() << ", PMD '" + << (*iter)->getPMDName() << "'"); + } + } + + // prepare configuration for every core + AppWorkerConfig workerConfigArr[2]; + workerConfigArr[0].CoreId = coresToUse.at(0).Id; + workerConfigArr[0].RxDevice = dpdkDevicesToUse.at(0); + workerConfigArr[0].RxQueues = queueQuantity; + workerConfigArr[0].TxDevice = dpdkDevicesToUse.at(1); + workerConfigArr[1].CoreId = coresToUse.at(1).Id; + workerConfigArr[1].RxDevice = dpdkDevicesToUse.at(1); + workerConfigArr[1].RxQueues = queueQuantity; + workerConfigArr[1].TxDevice = dpdkDevicesToUse.at(0); + + // create worker thread for every core + std::vector workerThreadVec; + workerThreadVec.push_back(new AppWorkerThread(workerConfigArr[0])); + workerThreadVec.push_back(new AppWorkerThread(workerConfigArr[1])); + + // start all worker threads + if (!pcpp::DpdkDeviceList::getInstance().startDpdkWorkerThreads( + coreMaskToUse, workerThreadVec)) { + EXIT_WITH_ERROR("Couldn't start worker threads"); + } + + // register the on app close event to print summary stats on app termination + DpdkBridgeArgs args; + args.workerThreadsVector = &workerThreadVec; + pcpp::ApplicationEventHandler::getInstance().onApplicationInterrupted( + onApplicationInterrupted, &args); + + // infinite loop (until program is terminated) + uint64_t counter = 0; + int statsCounter = 1; + + // Keep running while flag is on + while (!args.shouldStop) { + // Sleep for 1 second + sleep(1); + + // Print stats every COLLECT_STATS_EVERY_SEC seconds + // cppcheck-suppress moduloofone + if (counter % COLLECT_STATS_EVERY_SEC == 0) { + // Clear screen and move to top left + std::cout << "\033[2J\033[1;1H"; + + // Print devices traffic stats + std::cout << "Stats #" << statsCounter++ << std::endl + << "==========" << std::endl; + printStats(dpdkDevicesToUse.at(0)); + printStats(dpdkDevicesToUse.at(1)); + } + counter++; + } } diff --git a/Examples/DpdkExample-FilterTraffic/AppWorkerThread.h b/Examples/DpdkExample-FilterTraffic/AppWorkerThread.h index f074cf0af0..bc6ecd81ee 100644 --- a/Examples/DpdkExample-FilterTraffic/AppWorkerThread.h +++ b/Examples/DpdkExample-FilterTraffic/AppWorkerThread.h @@ -3,173 +3,156 @@ #include "Common.h" #include "PacketMatchingEngine.h" -#include "PacketUtils.h" #include "DpdkDevice.h" #include "DpdkDeviceList.h" +#include "PacketUtils.h" #include "PcapFileDevice.h" /** - * The worker thread class which does all the work: receive packets from relevant DPDK port(s), matched them with the packet matching engine and send them to - * TX port and/or save them to a file. In addition it collects packets statistics. - * Each core is assigned with one such worker thread, and all of them are activated using DpdkDeviceList::startDpdkWorkerThreads (see main.cpp) + * The worker thread class which does all the work: receive packets from + * relevant DPDK port(s), matched them with the packet matching engine and send + * them to TX port and/or save them to a file. In addition it collects packets + * statistics. Each core is assigned with one such worker thread, and all of + * them are activated using DpdkDeviceList::startDpdkWorkerThreads (see + * main.cpp) */ -class AppWorkerThread : public pcpp::DpdkWorkerThread -{ -private: - AppWorkerConfig& m_WorkerConfig; - bool m_Stop; - uint32_t m_CoreId; - PacketStats m_Stats; - PacketMatchingEngine& m_PacketMatchingEngine; - std::map m_FlowTable; - -public: - AppWorkerThread(AppWorkerConfig& workerConfig, PacketMatchingEngine& matchingEngine) : - m_WorkerConfig(workerConfig), m_Stop(true), m_CoreId(MAX_NUM_OF_CORES+1), - m_PacketMatchingEngine(matchingEngine) - { - } - - virtual ~AppWorkerThread() - { - // do nothing - } - - PacketStats& getStats() - { - return m_Stats; - } - - // implement abstract methods - - bool run(uint32_t coreId) - { - m_CoreId = coreId; - m_Stop = false; - m_Stats.WorkerId = coreId; - pcpp::DpdkDevice* sendPacketsTo = m_WorkerConfig.SendPacketsTo; - pcpp::PcapFileWriterDevice* pcapWriter = NULL; - - // if needed, create the pcap file writer which all matched packets will be written into - if (m_WorkerConfig.WriteMatchedPacketsToFile) - { - pcapWriter = new pcpp::PcapFileWriterDevice(m_WorkerConfig.PathToWritePackets.c_str()); - if (!pcapWriter->open()) - { - EXIT_WITH_ERROR("Couldn't open pcap writer device"); - } - } - - // if no DPDK devices were assigned to this worker/core don't enter the main loop and exit - if (m_WorkerConfig.InDataCfg.size() == 0) - { - return true; - } - - #define MAX_RECEIVE_BURST 64 - pcpp::MBufRawPacket* packetArr[MAX_RECEIVE_BURST] = {}; - - // main loop, runs until be told to stop - // cppcheck-suppress knownConditionTrueFalse - while (!m_Stop) - { - // go over all DPDK devices configured for this worker/core - for (InputDataConfig::iterator iter = m_WorkerConfig.InDataCfg.begin(); iter != m_WorkerConfig.InDataCfg.end(); iter++) - { - // for each DPDK device go over all RX queues configured for this worker/core - for (std::vector::iterator iter2 = iter->second.begin(); iter2 != iter->second.end(); iter2++) - { - pcpp::DpdkDevice* dev = iter->first; - - // receive packets from network on the specified DPDK device and RX queue - uint16_t packetsReceived = dev->receivePackets(packetArr, MAX_RECEIVE_BURST, *iter2); - - for (int i = 0; i < packetsReceived; i++) - { - // parse packet - pcpp::Packet parsedPacket(packetArr[i]); - - // collect packet statistics - m_Stats.collectStats(parsedPacket); - - bool packetMatched = false; - - // hash the packet by 5-tuple and look in the flow table to see whether this packet belongs to an existing or new flow - uint32_t hash = pcpp::hash5Tuple(&parsedPacket); - std::map::const_iterator iter3 = m_FlowTable.find(hash); - - // if packet belongs to an already existing flow - if (iter3 != m_FlowTable.end() && iter3->second) - { - packetMatched = true; - } - else // packet belongs to a new flow - { - packetMatched = m_PacketMatchingEngine.isMatched(parsedPacket); - if (packetMatched) - { - // put new flow in flow table - m_FlowTable[hash] = true; - - //collect stats - if (parsedPacket.isPacketOfType(pcpp::TCP)) - { - m_Stats.MatchedTcpFlows++; - } - else if (parsedPacket.isPacketOfType(pcpp::UDP)) - { - m_Stats.MatchedUdpFlows++; - } - - } - } - - if (packetMatched) - { - // send packet to TX port if needed - if (sendPacketsTo != NULL) - { - sendPacketsTo->sendPacket(*packetArr[i], 0); - } - - // save packet to file if needed - if (pcapWriter != NULL) - { - pcapWriter->writePacket(*packetArr[i]); - } - - m_Stats.MatchedPackets++; - } - } - } - } - } - - // free packet array (frees all mbufs as well) - for (int i = 0; i < MAX_RECEIVE_BURST; i++) - { - if (packetArr[i] != NULL) - delete packetArr[i]; - } - - // close and delete pcap file writer - if (pcapWriter != NULL) - { - delete pcapWriter; - } - - return true; - } - - void stop() - { - // assign the stop flag which will cause the main loop to end - m_Stop = true; - } - - uint32_t getCoreId() const - { - return m_CoreId; - } - +class AppWorkerThread : public pcpp::DpdkWorkerThread { + private: + AppWorkerConfig& m_WorkerConfig; + bool m_Stop; + uint32_t m_CoreId; + PacketStats m_Stats; + PacketMatchingEngine& m_PacketMatchingEngine; + std::map m_FlowTable; + + public: + AppWorkerThread(AppWorkerConfig& workerConfig, + PacketMatchingEngine& matchingEngine) + : m_WorkerConfig(workerConfig), m_Stop(true), + m_CoreId(MAX_NUM_OF_CORES + 1), m_PacketMatchingEngine(matchingEngine) { + } + + virtual ~AppWorkerThread() { + // do nothing + } + + PacketStats& getStats() { return m_Stats; } + + // implement abstract methods + + bool run(uint32_t coreId) { + m_CoreId = coreId; + m_Stop = false; + m_Stats.WorkerId = coreId; + pcpp::DpdkDevice* sendPacketsTo = m_WorkerConfig.SendPacketsTo; + pcpp::PcapFileWriterDevice* pcapWriter = NULL; + + // if needed, create the pcap file writer which all matched packets will be + // written into + if (m_WorkerConfig.WriteMatchedPacketsToFile) { + pcapWriter = new pcpp::PcapFileWriterDevice( + m_WorkerConfig.PathToWritePackets.c_str()); + if (!pcapWriter->open()) { + EXIT_WITH_ERROR("Couldn't open pcap writer device"); + } + } + + // if no DPDK devices were assigned to this worker/core don't enter the main + // loop and exit + if (m_WorkerConfig.InDataCfg.size() == 0) { + return true; + } + +#define MAX_RECEIVE_BURST 64 + pcpp::MBufRawPacket* packetArr[MAX_RECEIVE_BURST] = {}; + + // main loop, runs until be told to stop + // cppcheck-suppress knownConditionTrueFalse + while (!m_Stop) { + // go over all DPDK devices configured for this worker/core + for (InputDataConfig::iterator iter = m_WorkerConfig.InDataCfg.begin(); + iter != m_WorkerConfig.InDataCfg.end(); iter++) { + // for each DPDK device go over all RX queues configured for this + // worker/core + for (std::vector::iterator iter2 = iter->second.begin(); + iter2 != iter->second.end(); iter2++) { + pcpp::DpdkDevice* dev = iter->first; + + // receive packets from network on the specified DPDK device and RX + // queue + uint16_t packetsReceived = + dev->receivePackets(packetArr, MAX_RECEIVE_BURST, *iter2); + + for (int i = 0; i < packetsReceived; i++) { + // parse packet + pcpp::Packet parsedPacket(packetArr[i]); + + // collect packet statistics + m_Stats.collectStats(parsedPacket); + + bool packetMatched = false; + + // hash the packet by 5-tuple and look in the flow table to see + // whether this packet belongs to an existing or new flow + uint32_t hash = pcpp::hash5Tuple(&parsedPacket); + std::map::const_iterator iter3 = + m_FlowTable.find(hash); + + // if packet belongs to an already existing flow + if (iter3 != m_FlowTable.end() && iter3->second) { + packetMatched = true; + } else // packet belongs to a new flow + { + packetMatched = m_PacketMatchingEngine.isMatched(parsedPacket); + if (packetMatched) { + // put new flow in flow table + m_FlowTable[hash] = true; + + // collect stats + if (parsedPacket.isPacketOfType(pcpp::TCP)) { + m_Stats.MatchedTcpFlows++; + } else if (parsedPacket.isPacketOfType(pcpp::UDP)) { + m_Stats.MatchedUdpFlows++; + } + } + } + + if (packetMatched) { + // send packet to TX port if needed + if (sendPacketsTo != NULL) { + sendPacketsTo->sendPacket(*packetArr[i], 0); + } + + // save packet to file if needed + if (pcapWriter != NULL) { + pcapWriter->writePacket(*packetArr[i]); + } + + m_Stats.MatchedPackets++; + } + } + } + } + } + + // free packet array (frees all mbufs as well) + for (int i = 0; i < MAX_RECEIVE_BURST; i++) { + if (packetArr[i] != NULL) + delete packetArr[i]; + } + + // close and delete pcap file writer + if (pcapWriter != NULL) { + delete pcapWriter; + } + + return true; + } + + void stop() { + // assign the stop flag which will cause the main loop to end + m_Stop = true; + } + + uint32_t getCoreId() const { return m_CoreId; } }; diff --git a/Examples/DpdkExample-FilterTraffic/Common.h b/Examples/DpdkExample-FilterTraffic/Common.h index 113d73aeb6..36a130db02 100644 --- a/Examples/DpdkExample-FilterTraffic/Common.h +++ b/Examples/DpdkExample-FilterTraffic/Common.h @@ -1,172 +1,186 @@ #pragma once -#include "Packet.h" #include "DpdkDevice.h" +#include "Packet.h" #include -#include -#include -#include #include #include +#include #include #include - +#include +#include /** * Macros for exiting the application with error */ -#define EXIT_WITH_ERROR(reason) do { \ - std::cout << std::endl << "ERROR: " << reason << std::endl << std::endl; \ - exit(1); \ - } while(0) - - -#define EXIT_WITH_ERROR_AND_PRINT_USAGE(reason) do { \ - printUsage(); \ - std::cout << std::endl << "ERROR: " << reason << std::endl << std::endl; \ - exit(1); \ - } while(0) - -typedef std::map > InputDataConfig; - +#define EXIT_WITH_ERROR(reason) \ + do { \ + std::cout << std::endl \ + << "ERROR: " << reason << std::endl \ + << std::endl; \ + exit(1); \ + } while (0) + +#define EXIT_WITH_ERROR_AND_PRINT_USAGE(reason) \ + do { \ + printUsage(); \ + std::cout << std::endl \ + << "ERROR: " << reason << std::endl \ + << std::endl; \ + exit(1); \ + } while (0) + +typedef std::map> InputDataConfig; /** * Contains all the configuration needed for the worker thread including: * - Which DPDK ports and which RX queues to receive packet from - * - Whether to send matched packets to TX DPDK port and/or save them to a pcap file + * - Whether to send matched packets to TX DPDK port and/or save them to a pcap + * file */ -struct AppWorkerConfig -{ - uint32_t CoreId; - InputDataConfig InDataCfg; - pcpp::DpdkDevice* SendPacketsTo; - bool WriteMatchedPacketsToFile; - std::string PathToWritePackets; - - AppWorkerConfig() : CoreId(MAX_NUM_OF_CORES+1), SendPacketsTo(NULL), WriteMatchedPacketsToFile(false), PathToWritePackets("") - { - } +struct AppWorkerConfig { + uint32_t CoreId; + InputDataConfig InDataCfg; + pcpp::DpdkDevice* SendPacketsTo; + bool WriteMatchedPacketsToFile; + std::string PathToWritePackets; + + AppWorkerConfig() + : CoreId(MAX_NUM_OF_CORES + 1), SendPacketsTo(NULL), + WriteMatchedPacketsToFile(false), PathToWritePackets("") {} }; - /** * Collect and analyze packet and flow statistics */ -struct PacketStats -{ -public: - uint8_t WorkerId; - - int PacketCount; - int EthCount; - int ArpCount; - int Ip4Count; - int Ip6Count; - int TcpCount; - int UdpCount; - int HttpCount; - - int MatchedTcpFlows; - int MatchedUdpFlows; - int MatchedPackets; - - PacketStats() : WorkerId(MAX_NUM_OF_CORES+1), PacketCount(0), EthCount(0), ArpCount(0), Ip4Count(0), Ip6Count(0), TcpCount(0), UdpCount(0), HttpCount(0), MatchedTcpFlows(0), MatchedUdpFlows(0), MatchedPackets(0) {} - - void collectStats(pcpp::Packet& packet) - { - PacketCount++; - if (packet.isPacketOfType(pcpp::Ethernet)) - EthCount++; - if (packet.isPacketOfType(pcpp::ARP)) - ArpCount++; - if (packet.isPacketOfType(pcpp::IPv4)) - Ip4Count++; - if (packet.isPacketOfType(pcpp::IPv6)) - Ip6Count++; - if (packet.isPacketOfType(pcpp::TCP)) - TcpCount++; - if (packet.isPacketOfType(pcpp::UDP)) - UdpCount++; - if (packet.isPacketOfType(pcpp::HTTP)) - HttpCount++; - } - - void collectStats(const PacketStats& stats) - { - PacketCount += stats.PacketCount; - EthCount += stats.EthCount; - ArpCount += stats.ArpCount; - Ip4Count += stats.Ip4Count; - Ip6Count += stats.Ip6Count; - TcpCount += stats.TcpCount; - UdpCount += stats.UdpCount; - HttpCount += stats.HttpCount; - - MatchedTcpFlows += stats.MatchedTcpFlows; - MatchedUdpFlows += stats.MatchedUdpFlows; - MatchedPackets += stats.MatchedPackets; - } - - void clear() { WorkerId = MAX_NUM_OF_CORES+1; PacketCount = 0; EthCount = 0; ArpCount = 0; Ip4Count = 0; Ip6Count = 0; TcpCount = 0; UdpCount = 0; HttpCount = 0; MatchedTcpFlows = 0; MatchedUdpFlows = 0; MatchedPackets = 0; } - - std::string getStatValuesAsString(const std::string &delimiter) - { - std::stringstream values; - if (WorkerId == MAX_NUM_OF_CORES+1) - values << "Total" << delimiter; - else - values << (int)WorkerId << delimiter; - values << PacketCount << delimiter; - values << EthCount << delimiter; - values << ArpCount << delimiter; - values << Ip4Count << delimiter; - values << Ip6Count << delimiter; - values << TcpCount << delimiter; - values << UdpCount << delimiter; - values << HttpCount << delimiter; - values << MatchedTcpFlows << delimiter; - values << MatchedUdpFlows << delimiter; - values << MatchedPackets; - - return values.str(); - } - - static void getStatsColumns(std::vector& columnNames, std::vector& columnWidths) - { - columnNames.clear(); - columnWidths.clear(); - - static const int narrowColumnWidth = 11; - static const int wideColumnWidth = 18; - - columnNames.push_back("Core ID"); - columnNames.push_back("Packet Cnt"); - columnNames.push_back("Eth Cnt"); - columnNames.push_back("ARP Cnt"); - columnNames.push_back("IPv4 Cnt"); - columnNames.push_back("IPv6 Cnt"); - columnNames.push_back("TCP Cnt"); - columnNames.push_back("UDP Cnt"); - columnNames.push_back("HTTP Cnt"); - columnNames.push_back("Matched TCP Flows"); - columnNames.push_back("Matched UDP Flows"); - columnNames.push_back("Matched Packets"); - - columnWidths.push_back(7); - columnWidths.push_back(narrowColumnWidth); - columnWidths.push_back(narrowColumnWidth); - columnWidths.push_back(narrowColumnWidth); - columnWidths.push_back(narrowColumnWidth); - columnWidths.push_back(narrowColumnWidth); - columnWidths.push_back(narrowColumnWidth); - columnWidths.push_back(narrowColumnWidth); - columnWidths.push_back(narrowColumnWidth); - columnWidths.push_back(wideColumnWidth); - columnWidths.push_back(wideColumnWidth); - columnWidths.push_back(wideColumnWidth); - - } +struct PacketStats { + public: + uint8_t WorkerId; + + int PacketCount; + int EthCount; + int ArpCount; + int Ip4Count; + int Ip6Count; + int TcpCount; + int UdpCount; + int HttpCount; + + int MatchedTcpFlows; + int MatchedUdpFlows; + int MatchedPackets; + + PacketStats() + : WorkerId(MAX_NUM_OF_CORES + 1), PacketCount(0), EthCount(0), + ArpCount(0), Ip4Count(0), Ip6Count(0), TcpCount(0), UdpCount(0), + HttpCount(0), MatchedTcpFlows(0), MatchedUdpFlows(0), + MatchedPackets(0) {} + + void collectStats(pcpp::Packet& packet) { + PacketCount++; + if (packet.isPacketOfType(pcpp::Ethernet)) + EthCount++; + if (packet.isPacketOfType(pcpp::ARP)) + ArpCount++; + if (packet.isPacketOfType(pcpp::IPv4)) + Ip4Count++; + if (packet.isPacketOfType(pcpp::IPv6)) + Ip6Count++; + if (packet.isPacketOfType(pcpp::TCP)) + TcpCount++; + if (packet.isPacketOfType(pcpp::UDP)) + UdpCount++; + if (packet.isPacketOfType(pcpp::HTTP)) + HttpCount++; + } + + void collectStats(const PacketStats& stats) { + PacketCount += stats.PacketCount; + EthCount += stats.EthCount; + ArpCount += stats.ArpCount; + Ip4Count += stats.Ip4Count; + Ip6Count += stats.Ip6Count; + TcpCount += stats.TcpCount; + UdpCount += stats.UdpCount; + HttpCount += stats.HttpCount; + + MatchedTcpFlows += stats.MatchedTcpFlows; + MatchedUdpFlows += stats.MatchedUdpFlows; + MatchedPackets += stats.MatchedPackets; + } + + void clear() { + WorkerId = MAX_NUM_OF_CORES + 1; + PacketCount = 0; + EthCount = 0; + ArpCount = 0; + Ip4Count = 0; + Ip6Count = 0; + TcpCount = 0; + UdpCount = 0; + HttpCount = 0; + MatchedTcpFlows = 0; + MatchedUdpFlows = 0; + MatchedPackets = 0; + } + + std::string getStatValuesAsString(const std::string& delimiter) { + std::stringstream values; + if (WorkerId == MAX_NUM_OF_CORES + 1) + values << "Total" << delimiter; + else + values << (int)WorkerId << delimiter; + values << PacketCount << delimiter; + values << EthCount << delimiter; + values << ArpCount << delimiter; + values << Ip4Count << delimiter; + values << Ip6Count << delimiter; + values << TcpCount << delimiter; + values << UdpCount << delimiter; + values << HttpCount << delimiter; + values << MatchedTcpFlows << delimiter; + values << MatchedUdpFlows << delimiter; + values << MatchedPackets; + + return values.str(); + } + + static void getStatsColumns(std::vector& columnNames, + std::vector& columnWidths) { + columnNames.clear(); + columnWidths.clear(); + + static const int narrowColumnWidth = 11; + static const int wideColumnWidth = 18; + + columnNames.push_back("Core ID"); + columnNames.push_back("Packet Cnt"); + columnNames.push_back("Eth Cnt"); + columnNames.push_back("ARP Cnt"); + columnNames.push_back("IPv4 Cnt"); + columnNames.push_back("IPv6 Cnt"); + columnNames.push_back("TCP Cnt"); + columnNames.push_back("UDP Cnt"); + columnNames.push_back("HTTP Cnt"); + columnNames.push_back("Matched TCP Flows"); + columnNames.push_back("Matched UDP Flows"); + columnNames.push_back("Matched Packets"); + + columnWidths.push_back(7); + columnWidths.push_back(narrowColumnWidth); + columnWidths.push_back(narrowColumnWidth); + columnWidths.push_back(narrowColumnWidth); + columnWidths.push_back(narrowColumnWidth); + columnWidths.push_back(narrowColumnWidth); + columnWidths.push_back(narrowColumnWidth); + columnWidths.push_back(narrowColumnWidth); + columnWidths.push_back(narrowColumnWidth); + columnWidths.push_back(wideColumnWidth); + columnWidths.push_back(wideColumnWidth); + columnWidths.push_back(wideColumnWidth); + } }; diff --git a/Examples/DpdkExample-FilterTraffic/PacketMatchingEngine.h b/Examples/DpdkExample-FilterTraffic/PacketMatchingEngine.h index ef249f114a..56914cc2e4 100644 --- a/Examples/DpdkExample-FilterTraffic/PacketMatchingEngine.h +++ b/Examples/DpdkExample-FilterTraffic/PacketMatchingEngine.h @@ -1,105 +1,97 @@ #pragma once -#include "Packet.h" #include "IPv4Layer.h" +#include "Packet.h" +#include "SystemUtils.h" #include "TcpLayer.h" #include "UdpLayer.h" -#include "SystemUtils.h" /** - * Responsible for matching packets by match criteria received from the user. Current match criteria are a combination of zero or more of the - * following parameters: source IP, dest IP, source TCP/UDP port, dest TCP/UDP port and TCP/UDP protocol. + * Responsible for matching packets by match criteria received from the user. + * Current match criteria are a combination of zero or more of the following + * parameters: source IP, dest IP, source TCP/UDP port, dest TCP/UDP port and + * TCP/UDP protocol. */ -class PacketMatchingEngine -{ -private: - pcpp::IPv4Address m_SrcIpToMatch, m_DstIpToMatch; - uint16_t m_SrcPortToMatch, m_DstPortToMatch; - pcpp::ProtocolType m_ProtocolToMatch; - bool m_MatchSrcIp, m_MatchDstIp; - bool m_MatchSrcPort, m_MatchDstPort; - bool m_MatchProtocol; -public: - PacketMatchingEngine(const pcpp::IPv4Address& srcIpToMatch, const pcpp::IPv4Address& dstIpToMatch, uint16_t srcPortToMatch, uint16_t dstPortToMatch, pcpp::ProtocolType protocolToMatch) - : m_SrcIpToMatch(srcIpToMatch), m_DstIpToMatch(dstIpToMatch), - m_SrcPortToMatch(srcPortToMatch), m_DstPortToMatch(dstPortToMatch), m_ProtocolToMatch(protocolToMatch), - m_MatchSrcIp(false), m_MatchDstIp(false), m_MatchSrcPort(false), m_MatchDstPort(false), m_MatchProtocol(false) - { - if (m_SrcIpToMatch != pcpp::IPv4Address::Zero) - m_MatchSrcIp = true; - if (m_DstIpToMatch != pcpp::IPv4Address::Zero) - m_MatchDstIp = true; - if (m_SrcPortToMatch != 0) - m_MatchSrcPort = true; - if (m_DstPortToMatch != 0) - m_MatchDstPort = true; - if (m_ProtocolToMatch == pcpp::TCP || m_ProtocolToMatch == pcpp::UDP) - m_MatchProtocol = true; - } +class PacketMatchingEngine { + private: + pcpp::IPv4Address m_SrcIpToMatch, m_DstIpToMatch; + uint16_t m_SrcPortToMatch, m_DstPortToMatch; + pcpp::ProtocolType m_ProtocolToMatch; + bool m_MatchSrcIp, m_MatchDstIp; + bool m_MatchSrcPort, m_MatchDstPort; + bool m_MatchProtocol; + + public: + PacketMatchingEngine(const pcpp::IPv4Address& srcIpToMatch, + const pcpp::IPv4Address& dstIpToMatch, + uint16_t srcPortToMatch, uint16_t dstPortToMatch, + pcpp::ProtocolType protocolToMatch) + : m_SrcIpToMatch(srcIpToMatch), m_DstIpToMatch(dstIpToMatch), + m_SrcPortToMatch(srcPortToMatch), m_DstPortToMatch(dstPortToMatch), + m_ProtocolToMatch(protocolToMatch), m_MatchSrcIp(false), + m_MatchDstIp(false), m_MatchSrcPort(false), m_MatchDstPort(false), + m_MatchProtocol(false) { + if (m_SrcIpToMatch != pcpp::IPv4Address::Zero) + m_MatchSrcIp = true; + if (m_DstIpToMatch != pcpp::IPv4Address::Zero) + m_MatchDstIp = true; + if (m_SrcPortToMatch != 0) + m_MatchSrcPort = true; + if (m_DstPortToMatch != 0) + m_MatchDstPort = true; + if (m_ProtocolToMatch == pcpp::TCP || m_ProtocolToMatch == pcpp::UDP) + m_MatchProtocol = true; + } - bool isMatched(pcpp::Packet& packet) - { - if (m_MatchSrcIp || m_MatchDstIp) - { - if (!packet.isPacketOfType(pcpp::IPv4)) - { - return false; - } + bool isMatched(pcpp::Packet& packet) { + if (m_MatchSrcIp || m_MatchDstIp) { + if (!packet.isPacketOfType(pcpp::IPv4)) { + return false; + } - pcpp::IPv4Layer* ip4Layer = packet.getLayerOfType(); - if (m_MatchSrcIp && (ip4Layer->getSrcIPv4Address() != m_SrcIpToMatch)) - { - return false; - } + pcpp::IPv4Layer* ip4Layer = packet.getLayerOfType(); + if (m_MatchSrcIp && (ip4Layer->getSrcIPv4Address() != m_SrcIpToMatch)) { + return false; + } - if (m_MatchDstIp && (ip4Layer->getDstIPv4Address() != m_DstIpToMatch)) - { - return false; - } - } + if (m_MatchDstIp && (ip4Layer->getDstIPv4Address() != m_DstIpToMatch)) { + return false; + } + } - if (m_MatchSrcPort || m_MatchDstPort) - { - uint16_t srcPort, dstPort; - if (packet.isPacketOfType(pcpp::TCP)) - { - srcPort = packet.getLayerOfType()->getSrcPort(); - dstPort = packet.getLayerOfType()->getDstPort(); - } - else if (packet.isPacketOfType(pcpp::UDP)) - { - srcPort = packet.getLayerOfType()->getSrcPort(); - dstPort = packet.getLayerOfType()->getDstPort(); - } - else - { - return false; - } + if (m_MatchSrcPort || m_MatchDstPort) { + uint16_t srcPort, dstPort; + if (packet.isPacketOfType(pcpp::TCP)) { + srcPort = packet.getLayerOfType()->getSrcPort(); + dstPort = packet.getLayerOfType()->getDstPort(); + } else if (packet.isPacketOfType(pcpp::UDP)) { + srcPort = packet.getLayerOfType()->getSrcPort(); + dstPort = packet.getLayerOfType()->getDstPort(); + } else { + return false; + } - if (m_MatchSrcPort && (srcPort != m_SrcPortToMatch)) - { - return false; - } + if (m_MatchSrcPort && (srcPort != m_SrcPortToMatch)) { + return false; + } - if (m_MatchDstPort && (dstPort != m_DstPortToMatch)) - { - return false; - } - } + if (m_MatchDstPort && (dstPort != m_DstPortToMatch)) { + return false; + } + } - if (m_MatchProtocol) - { - if (m_ProtocolToMatch == pcpp::TCP && (!packet.isPacketOfType(pcpp::TCP))) - { - return false; - } + if (m_MatchProtocol) { + if (m_ProtocolToMatch == pcpp::TCP && + (!packet.isPacketOfType(pcpp::TCP))) { + return false; + } - if (m_ProtocolToMatch == pcpp::UDP && (!packet.isPacketOfType(pcpp::UDP))) - { - return false; - } - } + if (m_ProtocolToMatch == pcpp::UDP && + (!packet.isPacketOfType(pcpp::UDP))) { + return false; + } + } - return true; - } + return true; + } }; diff --git a/Examples/DpdkExample-FilterTraffic/main.cpp b/Examples/DpdkExample-FilterTraffic/main.cpp index 132740048b..f30325bab8 100644 --- a/Examples/DpdkExample-FilterTraffic/main.cpp +++ b/Examples/DpdkExample-FilterTraffic/main.cpp @@ -1,567 +1,610 @@ /** * Filter Traffic DPDK example application * ======================================= - * An application that listens to one or more DPDK ports (a.k.a DPDK devices), captures all traffic - * and matches packets by user-defined matching criteria. Matching criteria is given on startup and can contain one or more of the following: - * source IP, destination IP, source TCP/UDP port, destination TCP/UDP port and TCP or UDP protocol. Matching is done per flow, meaning the first packet - * received on a flow is matched against the matching criteria and if it's matched then all packets of the same flow will be matched too. - * Packets that are matched can be send to a DPDK port and/or be save to a pcap file. - * In addition the application collect statistics on received and matched packets: number of packets per protocol, number of matched flows and number + * An application that listens to one or more DPDK ports (a.k.a DPDK devices), + * captures all traffic and matches packets by user-defined matching criteria. + * Matching criteria is given on startup and can contain one or more of the + * following: source IP, destination IP, source TCP/UDP port, destination + * TCP/UDP port and TCP or UDP protocol. Matching is done per flow, meaning the + * first packet received on a flow is matched against the matching criteria and + * if it's matched then all packets of the same flow will be matched too. + * Packets that are matched can be send to a DPDK port and/or be save to a pcap + * file. In addition the application collect statistics on received and matched + * packets: number of packets per protocol, number of matched flows and number * of matched packets. * - * The application uses the concept of worker threads. Number of cores can be set by the user or set to default (default is all machine cores minus one - * management core). Each core is assigned with one worker thread. The application divides the DPDK ports and RX queues equally between worker threads. - * For example: if there are 2 DPDK ports to listen to, each one with 6 RX queues and there are 3 worker threads, then worker #1 will get RX queues - * 1-4 of port 1, worker #2 will get RX queues 5-6 of port 1 and RX queues 1-2 of port 2, and worker #3 will get RX queues 3-6 of port 2. - * Each worker thread does exactly the same work: receiving packets, collecting packet statistics, matching flows and sending/saving matched packets + * The application uses the concept of worker threads. Number of cores can be + * set by the user or set to default (default is all machine cores minus one + * management core). Each core is assigned with one worker thread. The + * application divides the DPDK ports and RX queues equally between worker + * threads. For example: if there are 2 DPDK ports to listen to, each one with 6 + * RX queues and there are 3 worker threads, then worker #1 will get RX queues + * 1-4 of port 1, worker #2 will get RX queues 5-6 of port 1 and RX queues 1-2 + * of port 2, and worker #3 will get RX queues 3-6 of port 2. Each worker thread + * does exactly the same work: receiving packets, collecting packet statistics, + * matching flows and sending/saving matched packets * - * __Important__: this application (like all applications using DPDK) should be run as 'sudo' + * __Important__: this application (like all applications using DPDK) should be + * run as 'sudo' */ +#include "AppWorkerThread.h" #include "Common.h" #include "PacketMatchingEngine.h" -#include "AppWorkerThread.h" #include "DpdkDeviceList.h" #include "IPv4Layer.h" -#include "TcpLayer.h" -#include "UdpLayer.h" -#include "SystemUtils.h" #include "PcapPlusPlusVersion.h" +#include "SystemUtils.h" #include "TablePrinter.h" +#include "TcpLayer.h" +#include "UdpLayer.h" -#include -#include +#include +#include #include -#include +#include #include -#include -#include #include -#include +#include +#include #include - +#include #define DEFAULT_MBUF_POOL_SIZE 4095 #define MAX_QUEUES 64 - -static struct option FilterTrafficOptions[] = -{ - {"dpdk-ports", required_argument, 0, 'd'}, - {"send-matched-packets", optional_argument, 0, 's'}, - {"save-matched-packets", optional_argument, 0, 'f'}, - {"match-source-ip", optional_argument, 0, 'i'}, - {"match-dest-ip", optional_argument, 0, 'I'}, - {"match-source-port", optional_argument, 0, 'p'}, - {"match-dest-port", optional_argument, 0, 'P'}, - {"match-protocol", optional_argument, 0, 'o'}, - {"core-mask", optional_argument, 0, 'c'}, - {"mbuf-pool-size", optional_argument, 0, 'm'}, - {"rx-queues", optional_argument, 0, 'r'}, - {"tx-queues", optional_argument, 0, 't'}, - {"help", optional_argument, 0, 'h'}, - {"version", optional_argument, 0, 'v'}, - {"list", optional_argument, 0, 'l'}, - {0, 0, 0, 0} -}; - +static struct option FilterTrafficOptions[] = { + {"dpdk-ports", required_argument, 0, 'd'}, + {"send-matched-packets", optional_argument, 0, 's'}, + {"save-matched-packets", optional_argument, 0, 'f'}, + {"match-source-ip", optional_argument, 0, 'i'}, + {"match-dest-ip", optional_argument, 0, 'I'}, + {"match-source-port", optional_argument, 0, 'p'}, + {"match-dest-port", optional_argument, 0, 'P'}, + {"match-protocol", optional_argument, 0, 'o'}, + {"core-mask", optional_argument, 0, 'c'}, + {"mbuf-pool-size", optional_argument, 0, 'm'}, + {"rx-queues", optional_argument, 0, 'r'}, + {"tx-queues", optional_argument, 0, 't'}, + {"help", optional_argument, 0, 'h'}, + {"version", optional_argument, 0, 'v'}, + {"list", optional_argument, 0, 'l'}, + {0, 0, 0, 0}}; /** * Print application usage */ -void printUsage() -{ - std::cout << std::endl - << "Usage:" << std::endl - << "------" << std::endl - << pcpp::AppName::get() << " [-hvl] [-s PORT] [-f FILENAME] [-i IPV4_ADDR] [-I IPV4_ADDR] [-p PORT] [-P PORT] [-r PROTOCOL]" << std::endl - << " [-c CORE_MASK] [-m POOL_SIZE] [-r NUM_QUEUES] [-t NUM_QUEUES] -d PORT_1,PORT_3,...,PORT_N" << std::endl - << std::endl - << "Options:" << std::endl - << std::endl - << " -h|--help : Displays this help message and exits" << std::endl - << " -v|--version : Displays the current version and exits" << std::endl - << " -l|--list : Print the list of DPDK ports and exists" << std::endl - << " -d|--dpdk-ports PORT_1,PORT_3,...,PORT_N : A comma-separated list of DPDK port numbers to receive" << std::endl - << " packets from. To see all available DPDK ports use the -l switch" << std::endl - << " -s|--send-matched-packets PORT : DPDK port to send matched packets to" << std::endl - << " -f|--save-matched-packets FILEPATH : Save matched packets to pcap files under FILEPATH. Packets" << std::endl - << " matched by core X will be saved under 'FILEPATH/CoreX.pcap'" << std::endl - << " -i|--match-source-ip IPV4_ADDR : Match source IPv4 address" << std::endl - << " -I|--match-dest-ip IPV4_ADDR : Match destination IPv4 address" << std::endl - << " -p|--match-source-port PORT : Match source TCP/UDP port" << std::endl - << " -P|--match-dest-port PORT : Match destination TCP/UDP port" << std::endl - << " -o|--match-protocol PROTOCOL : Match protocol. Valid values are 'TCP' or 'UDP'" << std::endl - << " -c|--core-mask CORE_MASK : Core mask of cores to use." << std::endl - << " For example: use 7 (binary 0111) to use cores 0,1,2." << std::endl - << " Default is using all cores except management core" << std::endl - << " -m|--mbuf-pool-size POOL_SIZE : DPDK mBuf pool size to initialize DPDK with." << std::endl - << " Default value is 4095" << std::endl - << " -r|--rx-queues NUM_QUEUES : Number of RX queues to open. Cannot exceed the max allowed by the NIC or " << MAX_QUEUES << std::endl - << " The default is 1" << std::endl - << " -t|--tx-queues NUM_QUEUES : Number of TX queues to open. Cannot exceed the max allowed by the NIC or " << MAX_QUEUES << std::endl - << " The default is 1" << std::endl - << std::endl; +void printUsage() { + std::cout + << std::endl + << "Usage:" << std::endl + << "------" << std::endl + << pcpp::AppName::get() + << " [-hvl] [-s PORT] [-f FILENAME] [-i IPV4_ADDR] [-I IPV4_ADDR] [-p " + "PORT] [-P PORT] [-r PROTOCOL]" + << std::endl + << " [-c CORE_MASK] [-m POOL_SIZE] [-r NUM_QUEUES] [-t " + "NUM_QUEUES] -d PORT_1,PORT_3,...,PORT_N" + << std::endl + << std::endl + << "Options:" << std::endl + << std::endl + << " -h|--help : Displays this help " + "message and exits" + << std::endl + << " -v|--version : Displays the " + "current version and exits" + << std::endl + << " -l|--list : Print the list of " + "DPDK ports and exists" + << std::endl + << " -d|--dpdk-ports PORT_1,PORT_3,...,PORT_N : A comma-separated " + "list of DPDK port numbers to receive" + << std::endl + << " packets from. To " + "see all available DPDK ports use the -l switch" + << std::endl + << " -s|--send-matched-packets PORT : DPDK port to send " + "matched packets to" + << std::endl + << " -f|--save-matched-packets FILEPATH : Save matched " + "packets to pcap files under FILEPATH. Packets" + << std::endl + << " matched by core X " + "will be saved under 'FILEPATH/CoreX.pcap'" + << std::endl + << " -i|--match-source-ip IPV4_ADDR : Match source IPv4 " + "address" + << std::endl + << " -I|--match-dest-ip IPV4_ADDR : Match destination " + "IPv4 address" + << std::endl + << " -p|--match-source-port PORT : Match source " + "TCP/UDP port" + << std::endl + << " -P|--match-dest-port PORT : Match destination " + "TCP/UDP port" + << std::endl + << " -o|--match-protocol PROTOCOL : Match protocol. " + "Valid values are 'TCP' or 'UDP'" + << std::endl + << " -c|--core-mask CORE_MASK : Core mask of cores " + "to use." + << std::endl + << " For example: use 7 " + "(binary 0111) to use cores 0,1,2." + << std::endl + << " Default is using " + "all cores except management core" + << std::endl + << " -m|--mbuf-pool-size POOL_SIZE : DPDK mBuf pool size " + "to initialize DPDK with." + << std::endl + << " Default value is " + "4095" + << std::endl + << " -r|--rx-queues NUM_QUEUES : Number of RX queues " + "to open. Cannot exceed the max allowed by the NIC or " + << MAX_QUEUES << std::endl + << " The default is 1" + << std::endl + << " -t|--tx-queues NUM_QUEUES : Number of TX queues " + "to open. Cannot exceed the max allowed by the NIC or " + << MAX_QUEUES << std::endl + << " The default is 1" + << std::endl + << std::endl; } - /** * Print application version */ -void printAppVersion() -{ - std::cout - << pcpp::AppName::get() << " " << pcpp::getPcapPlusPlusVersionFull() << std::endl - << "Built: " << pcpp::getBuildDateTime() << std::endl - << "Built from: " << pcpp::getGitInfo() << std::endl; - exit(0); +void printAppVersion() { + std::cout << pcpp::AppName::get() << " " << pcpp::getPcapPlusPlusVersionFull() + << std::endl + << "Built: " << pcpp::getBuildDateTime() << std::endl + << "Built from: " << pcpp::getGitInfo() << std::endl; + exit(0); } - /** * Print to console all available DPDK ports. Used by the -l switch */ -void listDpdkPorts() -{ - pcpp::CoreMask coreMaskToUse = pcpp::getCoreMaskForAllMachineCores(); - - // initialize DPDK - if (!pcpp::DpdkDeviceList::initDpdk(coreMaskToUse, DEFAULT_MBUF_POOL_SIZE)) - { - EXIT_WITH_ERROR("couldn't initialize DPDK"); - } - - std::cout << "DPDK port list:" << std::endl; - - // go over all available DPDK devices and print info for each one - std::vector deviceList = pcpp::DpdkDeviceList::getInstance().getDpdkDeviceList(); - for (std::vector::iterator iter = deviceList.begin(); iter != deviceList.end(); iter++) - { - pcpp::DpdkDevice* dev = *iter; - std::cout << " " - << " Port #" << dev->getDeviceId() << ":" - << " MAC address='" << dev->getMacAddress() << "';" - << " PCI address='" << dev->getPciAddress() << "';" - << " PMD='" << dev->getPMDName() << "'" - << std::endl; - } +void listDpdkPorts() { + pcpp::CoreMask coreMaskToUse = pcpp::getCoreMaskForAllMachineCores(); + + // initialize DPDK + if (!pcpp::DpdkDeviceList::initDpdk(coreMaskToUse, DEFAULT_MBUF_POOL_SIZE)) { + EXIT_WITH_ERROR("couldn't initialize DPDK"); + } + + std::cout << "DPDK port list:" << std::endl; + + // go over all available DPDK devices and print info for each one + std::vector deviceList = + pcpp::DpdkDeviceList::getInstance().getDpdkDeviceList(); + for (std::vector::iterator iter = deviceList.begin(); + iter != deviceList.end(); iter++) { + pcpp::DpdkDevice* dev = *iter; + std::cout << " " + << " Port #" << dev->getDeviceId() << ":" + << " MAC address='" << dev->getMacAddress() << "';" + << " PCI address='" << dev->getPciAddress() << "';" + << " PMD='" << dev->getPMDName() << "'" << std::endl; + } } - /** - * Prepare the configuration for each core. Configuration includes: which DpdkDevices and which RX queues to receive packets from, where to send the matched - * packets, etc. + * Prepare the configuration for each core. Configuration includes: which + * DpdkDevices and which RX queues to receive packets from, where to send the + * matched packets, etc. */ -void prepareCoreConfiguration(std::vector& dpdkDevicesToUse, std::vector& coresToUse, - bool writePacketsToDisk, const std::string &packetFilePath, pcpp::DpdkDevice* sendPacketsTo, - AppWorkerConfig workerConfigArr[], int workerConfigArrLen, uint16_t rxQueues) -{ - // create a list of pairs of DpdkDevice and RX queues for all RX queues in all requested devices - int totalNumOfRxQueues = 0; - std::vector > deviceAndRxQVec; - for (std::vector::iterator iter = dpdkDevicesToUse.begin(); iter != dpdkDevicesToUse.end(); iter++) - { - for (int rxQueueIndex = 0; rxQueueIndex < rxQueues; rxQueueIndex++) - { - std::pair curPair(*iter, rxQueueIndex); - deviceAndRxQVec.push_back(curPair); - } - totalNumOfRxQueues += rxQueues; - } - - // calculate how many RX queues each core will read packets from. We divide the total number of RX queues with total number of core - int numOfRxQueuesPerCore = totalNumOfRxQueues / coresToUse.size(); - int rxQueuesRemainder = totalNumOfRxQueues % coresToUse.size(); - - // prepare the configuration for every core: divide the devices and RX queue for each device with the various cores - int i = 0; - std::vector >::iterator pairVecIter = deviceAndRxQVec.begin(); - for (std::vector::iterator iter = coresToUse.begin(); iter != coresToUse.end(); iter++) - { - std::cout << "Using core " << (int)iter->Id << std::endl; - workerConfigArr[i].CoreId = iter->Id; - workerConfigArr[i].WriteMatchedPacketsToFile = writePacketsToDisk; - - std::stringstream packetFileName; - packetFileName << packetFilePath << "Core" << workerConfigArr[i].CoreId << ".pcap"; - workerConfigArr[i].PathToWritePackets = packetFileName.str(); - - workerConfigArr[i].SendPacketsTo = sendPacketsTo; - for (int rxQIndex = 0; rxQIndex < numOfRxQueuesPerCore; rxQIndex++) - { - if (pairVecIter == deviceAndRxQVec.end()) - break; - workerConfigArr[i].InDataCfg[pairVecIter->first].push_back(pairVecIter->second); - pairVecIter++; - } - if (rxQueuesRemainder > 0 && (pairVecIter != deviceAndRxQVec.end())) - { - workerConfigArr[i].InDataCfg[pairVecIter->first].push_back(pairVecIter->second); - pairVecIter++; - rxQueuesRemainder--; - } - - // print configuration for core - std::cout << " Core configuration:" << std::endl; - for (InputDataConfig::iterator iter2 = workerConfigArr[i].InDataCfg.begin(); iter2 != workerConfigArr[i].InDataCfg.end(); iter2++) - { - std::cout << " DPDK device#" << iter2->first->getDeviceId() << ": "; - for (std::vector::iterator iter3 = iter2->second.begin(); iter3 != iter2->second.end(); iter3++) - { - std::cout << "RX-Queue#" << *iter3 << "; "; - } - std::cout << std::endl; - } - if (workerConfigArr[i].InDataCfg.size() == 0) - { - std::cout << " None" << std::endl; - } - i++; - } +void prepareCoreConfiguration(std::vector& dpdkDevicesToUse, + std::vector& coresToUse, + bool writePacketsToDisk, + const std::string& packetFilePath, + pcpp::DpdkDevice* sendPacketsTo, + AppWorkerConfig workerConfigArr[], + int workerConfigArrLen, uint16_t rxQueues) { + // create a list of pairs of DpdkDevice and RX queues for all RX queues in all + // requested devices + int totalNumOfRxQueues = 0; + std::vector> deviceAndRxQVec; + for (std::vector::iterator iter = + dpdkDevicesToUse.begin(); + iter != dpdkDevicesToUse.end(); iter++) { + for (int rxQueueIndex = 0; rxQueueIndex < rxQueues; rxQueueIndex++) { + std::pair curPair(*iter, rxQueueIndex); + deviceAndRxQVec.push_back(curPair); + } + totalNumOfRxQueues += rxQueues; + } + + // calculate how many RX queues each core will read packets from. We divide + // the total number of RX queues with total number of core + int numOfRxQueuesPerCore = totalNumOfRxQueues / coresToUse.size(); + int rxQueuesRemainder = totalNumOfRxQueues % coresToUse.size(); + + // prepare the configuration for every core: divide the devices and RX queue + // for each device with the various cores + int i = 0; + std::vector>::iterator pairVecIter = + deviceAndRxQVec.begin(); + for (std::vector::iterator iter = coresToUse.begin(); + iter != coresToUse.end(); iter++) { + std::cout << "Using core " << (int)iter->Id << std::endl; + workerConfigArr[i].CoreId = iter->Id; + workerConfigArr[i].WriteMatchedPacketsToFile = writePacketsToDisk; + + std::stringstream packetFileName; + packetFileName << packetFilePath << "Core" << workerConfigArr[i].CoreId + << ".pcap"; + workerConfigArr[i].PathToWritePackets = packetFileName.str(); + + workerConfigArr[i].SendPacketsTo = sendPacketsTo; + for (int rxQIndex = 0; rxQIndex < numOfRxQueuesPerCore; rxQIndex++) { + if (pairVecIter == deviceAndRxQVec.end()) + break; + workerConfigArr[i].InDataCfg[pairVecIter->first].push_back( + pairVecIter->second); + pairVecIter++; + } + if (rxQueuesRemainder > 0 && (pairVecIter != deviceAndRxQVec.end())) { + workerConfigArr[i].InDataCfg[pairVecIter->first].push_back( + pairVecIter->second); + pairVecIter++; + rxQueuesRemainder--; + } + + // print configuration for core + std::cout << " Core configuration:" << std::endl; + for (InputDataConfig::iterator iter2 = workerConfigArr[i].InDataCfg.begin(); + iter2 != workerConfigArr[i].InDataCfg.end(); iter2++) { + std::cout << " DPDK device#" << iter2->first->getDeviceId() << ": "; + for (std::vector::iterator iter3 = iter2->second.begin(); + iter3 != iter2->second.end(); iter3++) { + std::cout << "RX-Queue#" << *iter3 << "; "; + } + std::cout << std::endl; + } + if (workerConfigArr[i].InDataCfg.size() == 0) { + std::cout << " None" << std::endl; + } + i++; + } } +struct FilterTrafficArgs { + bool shouldStop; + std::vector* workerThreadsVector; -struct FilterTrafficArgs -{ - bool shouldStop; - std::vector* workerThreadsVector; - - FilterTrafficArgs() : shouldStop(false), workerThreadsVector(NULL) {} + FilterTrafficArgs() : shouldStop(false), workerThreadsVector(NULL) {} }; /** - * The callback to be called when application is terminated by ctrl-c. Do cleanup and print summary stats + * The callback to be called when application is terminated by ctrl-c. Do + * cleanup and print summary stats */ -void onApplicationInterrupted(void* cookie) -{ - FilterTrafficArgs* args = (FilterTrafficArgs*)cookie; - - std::cout << std::endl << std::endl << "Application stopped" << std::endl; - - // stop worker threads - pcpp::DpdkDeviceList::getInstance().stopDpdkWorkerThreads(); - - // create table printer - std::vector columnNames; - std::vector columnWidths; - PacketStats::getStatsColumns(columnNames, columnWidths); - pcpp::TablePrinter printer(columnNames, columnWidths); - - // print final stats for every worker thread plus sum of all threads and free worker threads memory - PacketStats aggregatedStats; - for (std::vector::iterator iter = args->workerThreadsVector->begin(); iter != args->workerThreadsVector->end(); iter++) - { - AppWorkerThread* thread = (AppWorkerThread*)(*iter); - PacketStats threadStats = thread->getStats(); - aggregatedStats.collectStats(threadStats); - printer.printRow(threadStats.getStatValuesAsString("|"), '|'); - delete thread; - } - - printer.printSeparator(); - printer.printRow(aggregatedStats.getStatValuesAsString("|"), '|'); - - args->shouldStop = true; +void onApplicationInterrupted(void* cookie) { + FilterTrafficArgs* args = (FilterTrafficArgs*)cookie; + + std::cout << std::endl + << std::endl + << "Application stopped" << std::endl; + + // stop worker threads + pcpp::DpdkDeviceList::getInstance().stopDpdkWorkerThreads(); + + // create table printer + std::vector columnNames; + std::vector columnWidths; + PacketStats::getStatsColumns(columnNames, columnWidths); + pcpp::TablePrinter printer(columnNames, columnWidths); + + // print final stats for every worker thread plus sum of all threads and free + // worker threads memory + PacketStats aggregatedStats; + for (std::vector::iterator iter = + args->workerThreadsVector->begin(); + iter != args->workerThreadsVector->end(); iter++) { + AppWorkerThread* thread = (AppWorkerThread*)(*iter); + PacketStats threadStats = thread->getStats(); + aggregatedStats.collectStats(threadStats); + printer.printRow(threadStats.getStatValuesAsString("|"), '|'); + delete thread; + } + + printer.printSeparator(); + printer.printRow(aggregatedStats.getStatValuesAsString("|"), '|'); + + args->shouldStop = true; } - /** - * main method of the application. Responsible for parsing user args, preparing worker thread configuration, creating the worker threads and activate them. - * At program termination worker threads are stopped, statistics are collected from them and printed to console + * main method of the application. Responsible for parsing user args, preparing + * worker thread configuration, creating the worker threads and activate them. + * At program termination worker threads are stopped, statistics are collected + * from them and printed to console */ -int main(int argc, char* argv[]) -{ - pcpp::AppName::init(argc, argv); - - std::vector dpdkPortVec; - - bool writePacketsToDisk = false; - - std::string packetFilePath = ""; - - pcpp::CoreMask coreMaskToUse = pcpp::getCoreMaskForAllMachineCores(); - - int sendPacketsToPort = -1; - - int optionIndex = 0; - int opt = 0; - - uint32_t mBufPoolSize = DEFAULT_MBUF_POOL_SIZE; - - pcpp::IPv4Address srcIPToMatch = pcpp::IPv4Address::Zero; - pcpp::IPv4Address dstIPToMatch = pcpp::IPv4Address::Zero; - uint16_t srcPortToMatch = 0; - uint16_t dstPortToMatch = 0; - pcpp::ProtocolType protocolToMatch = pcpp::UnknownProtocol; - - uint16_t rxQueues = 1; - uint16_t txQueues = 1; - - while((opt = getopt_long(argc, argv, "d:c:s:f:m:i:I:p:P:o:r:t:hvl", FilterTrafficOptions, &optionIndex)) != -1) - { - switch (opt) - { - case 0: - { - break; - } - case 'd': - { - std::string portListAsString = std::string(optarg); - std::stringstream stream(portListAsString); - std::string portAsString; - int port; - // break comma-separated string into string list - while(getline(stream, portAsString, ',')) - { - char c; - std::stringstream stream2(portAsString); - stream2 >> port; - if (stream2.fail() || stream2.get(c)) - { - // not an integer - EXIT_WITH_ERROR_AND_PRINT_USAGE("DPDK ports list is invalid"); - } - dpdkPortVec.push_back(port); - } - - // verify list is not empty - if (dpdkPortVec.empty()) - { - EXIT_WITH_ERROR_AND_PRINT_USAGE("DPDK ports list is empty"); - } - break; - } - case 's': - { - sendPacketsToPort = atoi(optarg); - break; - } - case 'c': - { - coreMaskToUse = atoi(optarg); - break; - } - case 'f': - { - packetFilePath = std::string(optarg); - writePacketsToDisk = true; - if (packetFilePath.empty()) - { - EXIT_WITH_ERROR_AND_PRINT_USAGE("Filename to write packets is empty"); - } - break; - } - case 'm': - { - mBufPoolSize = atoi(optarg); - break; - } - case 'i': - { - srcIPToMatch = pcpp::IPv4Address(optarg); - if (!srcIPToMatch.isValid()) - { - EXIT_WITH_ERROR_AND_PRINT_USAGE("Source IP to match isn't a valid IP address"); - } - break; - } - case 'I': - { - dstIPToMatch = pcpp::IPv4Address(optarg); - if (!dstIPToMatch.isValid()) - { - EXIT_WITH_ERROR_AND_PRINT_USAGE("Destination IP to match isn't a valid IP address"); - } - break; - } - case 'p': - { - int ret = atoi(optarg); - if (ret <= 0 || ret > 65535) - { - EXIT_WITH_ERROR_AND_PRINT_USAGE("Source port to match isn't a valid TCP/UDP port"); - } - srcPortToMatch = ret; - break; - } - case 'P': - { - int ret = atoi(optarg); - if (ret <= 0 || ret > 65535) - { - EXIT_WITH_ERROR_AND_PRINT_USAGE("Destination port to match isn't a valid TCP/UDP port"); - } - dstPortToMatch = ret; - break; - } - case 'o': - { - std::string protocol = std::string(optarg); - if (protocol == "TCP") - protocolToMatch = pcpp::TCP; - else if (protocol == "UDP") - protocolToMatch = pcpp::UDP; - else - { - EXIT_WITH_ERROR_AND_PRINT_USAGE("Protocol to match isn't TCP or UDP"); - } - break; - } - case 'r': - { - rxQueues = atoi(optarg); - if (rxQueues == 0) - { - EXIT_WITH_ERROR("Cannot open the device with 0 RX queues"); - } - if (rxQueues > MAX_QUEUES) - { - EXIT_WITH_ERROR("The number of RX queues cannot exceed " << MAX_QUEUES); - } - break; - } - case 't': - { - txQueues = atoi(optarg); - if (txQueues == 0) - { - EXIT_WITH_ERROR("Cannot open the device with 0 TX queues"); - } - if (txQueues > MAX_QUEUES) - { - EXIT_WITH_ERROR("The number of TX queues cannot exceed " << MAX_QUEUES); - } - break; - } - case 'h': - { - printUsage(); - exit(0); - } - case 'v': - { - printAppVersion(); - break; - } - case 'l': - { - listDpdkPorts(); - exit(0); - } - default: - { - printUsage(); - exit(0); - } - } - } - - // verify list is not empty - if (dpdkPortVec.empty()) - { - EXIT_WITH_ERROR_AND_PRINT_USAGE("DPDK ports list is empty. Please use the -d switch"); - } - - // extract core vector from core mask - std::vector coresToUse; - pcpp::createCoreVectorFromCoreMask(coreMaskToUse, coresToUse); - - // need minimum of 2 cores to start - 1 management core + 1 (or more) worker thread(s) - if (coresToUse.size() < 2) - { - EXIT_WITH_ERROR("Needed minimum of 2 cores to start the application"); - } - - // initialize DPDK - if (!pcpp::DpdkDeviceList::initDpdk(coreMaskToUse, mBufPoolSize)) - { - EXIT_WITH_ERROR("Couldn't initialize DPDK"); - } - - // removing DPDK master core from core mask because DPDK worker threads cannot run on master core - coreMaskToUse = coreMaskToUse & ~(pcpp::DpdkDeviceList::getInstance().getDpdkMasterCore().Mask); - - // re-calculate cores to use after removing master core - coresToUse.clear(); - createCoreVectorFromCoreMask(coreMaskToUse, coresToUse); - - // collect the list of DPDK devices - std::vector dpdkDevicesToUse; - for (std::vector::iterator iter = dpdkPortVec.begin(); iter != dpdkPortVec.end(); iter++) - { - pcpp::DpdkDevice* dev = pcpp::DpdkDeviceList::getInstance().getDeviceByPort(*iter); - if (dev == NULL) - { - EXIT_WITH_ERROR("DPDK device for port " << *iter << " doesn't exist"); - } - dpdkDevicesToUse.push_back(dev); - } - - // go over all devices and open them - for (std::vector::iterator iter = dpdkDevicesToUse.begin(); iter != dpdkDevicesToUse.end(); iter++) - { - if (rxQueues > (*iter)->getTotalNumOfRxQueues()) - { - EXIT_WITH_ERROR("Number of RX errors cannot exceed the max allowed by the device which is " << (*iter)->getTotalNumOfRxQueues()); - } - if (txQueues > (*iter)->getTotalNumOfTxQueues()) - { - EXIT_WITH_ERROR("Number of TX errors cannot exceed the max allowed by the device which is " << (*iter)->getTotalNumOfTxQueues()); - } - if (!(*iter)->openMultiQueues(rxQueues, txQueues)) - { - EXIT_WITH_ERROR("Couldn't open DPDK device #" << (*iter)->getDeviceId() << ", PMD '" << (*iter)->getPMDName() << "'"); - } - std::cout - << "Opened device #" << (*iter)->getDeviceId() - << " with " << rxQueues << " RX queues and " << txQueues << " TX queues." - << " RSS hash functions:" << std::endl; - std::vector rssHashFunctions = (*iter)->rssHashFunctionMaskToString((*iter)->getConfiguredRssHashFunction()); - for(std::vector::iterator it = rssHashFunctions.begin(); it != rssHashFunctions.end(); ++it) - { - std::cout << " " << (*it) << std::endl; - } - } - - // get DPDK device to send packets to (or NULL if doesn't exist) - pcpp::DpdkDevice* sendPacketsTo = pcpp::DpdkDeviceList::getInstance().getDeviceByPort(sendPacketsToPort); - if (sendPacketsTo != NULL && !sendPacketsTo->isOpened() && !sendPacketsTo->open()) - { - EXIT_WITH_ERROR("Could not open port#" << sendPacketsToPort << " for sending matched packets"); - } - - // prepare configuration for every core - AppWorkerConfig workerConfigArr[coresToUse.size()]; - prepareCoreConfiguration(dpdkDevicesToUse, coresToUse, writePacketsToDisk, packetFilePath, sendPacketsTo, workerConfigArr, coresToUse.size(), rxQueues); - - PacketMatchingEngine matchingEngine(srcIPToMatch, dstIPToMatch, srcPortToMatch, dstPortToMatch, protocolToMatch); - - // create worker thread for every core - std::vector workerThreadVec; - int i = 0; - for (std::vector::iterator iter = coresToUse.begin(); iter != coresToUse.end(); iter++) - { - AppWorkerThread* newWorker = new AppWorkerThread(workerConfigArr[i], matchingEngine); - workerThreadVec.push_back(newWorker); - i++; - } - - // start all worker threads - if (!pcpp::DpdkDeviceList::getInstance().startDpdkWorkerThreads(coreMaskToUse, workerThreadVec)) - { - EXIT_WITH_ERROR("Couldn't start worker threads"); - } - - // register the on app close event to print summary stats on app termination - FilterTrafficArgs args; - args.workerThreadsVector = &workerThreadVec; - pcpp::ApplicationEventHandler::getInstance().onApplicationInterrupted(onApplicationInterrupted, &args); - - // infinite loop (until program is terminated) - while (!args.shouldStop) - { - sleep(5); - } +int main(int argc, char* argv[]) { + pcpp::AppName::init(argc, argv); + + std::vector dpdkPortVec; + + bool writePacketsToDisk = false; + + std::string packetFilePath = ""; + + pcpp::CoreMask coreMaskToUse = pcpp::getCoreMaskForAllMachineCores(); + + int sendPacketsToPort = -1; + + int optionIndex = 0; + int opt = 0; + + uint32_t mBufPoolSize = DEFAULT_MBUF_POOL_SIZE; + + pcpp::IPv4Address srcIPToMatch = pcpp::IPv4Address::Zero; + pcpp::IPv4Address dstIPToMatch = pcpp::IPv4Address::Zero; + uint16_t srcPortToMatch = 0; + uint16_t dstPortToMatch = 0; + pcpp::ProtocolType protocolToMatch = pcpp::UnknownProtocol; + + uint16_t rxQueues = 1; + uint16_t txQueues = 1; + + while ((opt = getopt_long(argc, argv, "d:c:s:f:m:i:I:p:P:o:r:t:hvl", + FilterTrafficOptions, &optionIndex)) != -1) { + switch (opt) { + case 0: { + break; + } + case 'd': { + std::string portListAsString = std::string(optarg); + std::stringstream stream(portListAsString); + std::string portAsString; + int port; + // break comma-separated string into string list + while (getline(stream, portAsString, ',')) { + char c; + std::stringstream stream2(portAsString); + stream2 >> port; + if (stream2.fail() || stream2.get(c)) { + // not an integer + EXIT_WITH_ERROR_AND_PRINT_USAGE("DPDK ports list is invalid"); + } + dpdkPortVec.push_back(port); + } + + // verify list is not empty + if (dpdkPortVec.empty()) { + EXIT_WITH_ERROR_AND_PRINT_USAGE("DPDK ports list is empty"); + } + break; + } + case 's': { + sendPacketsToPort = atoi(optarg); + break; + } + case 'c': { + coreMaskToUse = atoi(optarg); + break; + } + case 'f': { + packetFilePath = std::string(optarg); + writePacketsToDisk = true; + if (packetFilePath.empty()) { + EXIT_WITH_ERROR_AND_PRINT_USAGE("Filename to write packets is empty"); + } + break; + } + case 'm': { + mBufPoolSize = atoi(optarg); + break; + } + case 'i': { + srcIPToMatch = pcpp::IPv4Address(optarg); + if (!srcIPToMatch.isValid()) { + EXIT_WITH_ERROR_AND_PRINT_USAGE( + "Source IP to match isn't a valid IP address"); + } + break; + } + case 'I': { + dstIPToMatch = pcpp::IPv4Address(optarg); + if (!dstIPToMatch.isValid()) { + EXIT_WITH_ERROR_AND_PRINT_USAGE( + "Destination IP to match isn't a valid IP address"); + } + break; + } + case 'p': { + int ret = atoi(optarg); + if (ret <= 0 || ret > 65535) { + EXIT_WITH_ERROR_AND_PRINT_USAGE( + "Source port to match isn't a valid TCP/UDP port"); + } + srcPortToMatch = ret; + break; + } + case 'P': { + int ret = atoi(optarg); + if (ret <= 0 || ret > 65535) { + EXIT_WITH_ERROR_AND_PRINT_USAGE( + "Destination port to match isn't a valid TCP/UDP port"); + } + dstPortToMatch = ret; + break; + } + case 'o': { + std::string protocol = std::string(optarg); + if (protocol == "TCP") + protocolToMatch = pcpp::TCP; + else if (protocol == "UDP") + protocolToMatch = pcpp::UDP; + else { + EXIT_WITH_ERROR_AND_PRINT_USAGE("Protocol to match isn't TCP or UDP"); + } + break; + } + case 'r': { + rxQueues = atoi(optarg); + if (rxQueues == 0) { + EXIT_WITH_ERROR("Cannot open the device with 0 RX queues"); + } + if (rxQueues > MAX_QUEUES) { + EXIT_WITH_ERROR("The number of RX queues cannot exceed " << MAX_QUEUES); + } + break; + } + case 't': { + txQueues = atoi(optarg); + if (txQueues == 0) { + EXIT_WITH_ERROR("Cannot open the device with 0 TX queues"); + } + if (txQueues > MAX_QUEUES) { + EXIT_WITH_ERROR("The number of TX queues cannot exceed " << MAX_QUEUES); + } + break; + } + case 'h': { + printUsage(); + exit(0); + } + case 'v': { + printAppVersion(); + break; + } + case 'l': { + listDpdkPorts(); + exit(0); + } + default: { + printUsage(); + exit(0); + } + } + } + + // verify list is not empty + if (dpdkPortVec.empty()) { + EXIT_WITH_ERROR_AND_PRINT_USAGE( + "DPDK ports list is empty. Please use the -d switch"); + } + + // extract core vector from core mask + std::vector coresToUse; + pcpp::createCoreVectorFromCoreMask(coreMaskToUse, coresToUse); + + // need minimum of 2 cores to start - 1 management core + 1 (or more) worker + // thread(s) + if (coresToUse.size() < 2) { + EXIT_WITH_ERROR("Needed minimum of 2 cores to start the application"); + } + + // initialize DPDK + if (!pcpp::DpdkDeviceList::initDpdk(coreMaskToUse, mBufPoolSize)) { + EXIT_WITH_ERROR("Couldn't initialize DPDK"); + } + + // removing DPDK master core from core mask because DPDK worker threads cannot + // run on master core + coreMaskToUse = + coreMaskToUse & + ~(pcpp::DpdkDeviceList::getInstance().getDpdkMasterCore().Mask); + + // re-calculate cores to use after removing master core + coresToUse.clear(); + createCoreVectorFromCoreMask(coreMaskToUse, coresToUse); + + // collect the list of DPDK devices + std::vector dpdkDevicesToUse; + for (std::vector::iterator iter = dpdkPortVec.begin(); + iter != dpdkPortVec.end(); iter++) { + pcpp::DpdkDevice* dev = + pcpp::DpdkDeviceList::getInstance().getDeviceByPort(*iter); + if (dev == NULL) { + EXIT_WITH_ERROR("DPDK device for port " << *iter << " doesn't exist"); + } + dpdkDevicesToUse.push_back(dev); + } + + // go over all devices and open them + for (std::vector::iterator iter = + dpdkDevicesToUse.begin(); + iter != dpdkDevicesToUse.end(); iter++) { + if (rxQueues > (*iter)->getTotalNumOfRxQueues()) { + EXIT_WITH_ERROR("Number of RX errors cannot exceed the max allowed by " + "the device which is " + << (*iter)->getTotalNumOfRxQueues()); + } + if (txQueues > (*iter)->getTotalNumOfTxQueues()) { + EXIT_WITH_ERROR("Number of TX errors cannot exceed the max allowed by " + "the device which is " + << (*iter)->getTotalNumOfTxQueues()); + } + if (!(*iter)->openMultiQueues(rxQueues, txQueues)) { + EXIT_WITH_ERROR("Couldn't open DPDK device #" + << (*iter)->getDeviceId() << ", PMD '" + << (*iter)->getPMDName() << "'"); + } + std::cout << "Opened device #" << (*iter)->getDeviceId() << " with " + << rxQueues << " RX queues and " << txQueues << " TX queues." + << " RSS hash functions:" << std::endl; + std::vector rssHashFunctions = + (*iter)->rssHashFunctionMaskToString( + (*iter)->getConfiguredRssHashFunction()); + for (std::vector::iterator it = rssHashFunctions.begin(); + it != rssHashFunctions.end(); ++it) { + std::cout << " " << (*it) << std::endl; + } + } + + // get DPDK device to send packets to (or NULL if doesn't exist) + pcpp::DpdkDevice* sendPacketsTo = + pcpp::DpdkDeviceList::getInstance().getDeviceByPort(sendPacketsToPort); + if (sendPacketsTo != NULL && !sendPacketsTo->isOpened() && + !sendPacketsTo->open()) { + EXIT_WITH_ERROR("Could not open port#" << sendPacketsToPort + << " for sending matched packets"); + } + + // prepare configuration for every core + AppWorkerConfig workerConfigArr[coresToUse.size()]; + prepareCoreConfiguration(dpdkDevicesToUse, coresToUse, writePacketsToDisk, + packetFilePath, sendPacketsTo, workerConfigArr, + coresToUse.size(), rxQueues); + + PacketMatchingEngine matchingEngine(srcIPToMatch, dstIPToMatch, + srcPortToMatch, dstPortToMatch, + protocolToMatch); + + // create worker thread for every core + std::vector workerThreadVec; + int i = 0; + for (std::vector::iterator iter = coresToUse.begin(); + iter != coresToUse.end(); iter++) { + AppWorkerThread* newWorker = + new AppWorkerThread(workerConfigArr[i], matchingEngine); + workerThreadVec.push_back(newWorker); + i++; + } + + // start all worker threads + if (!pcpp::DpdkDeviceList::getInstance().startDpdkWorkerThreads( + coreMaskToUse, workerThreadVec)) { + EXIT_WITH_ERROR("Couldn't start worker threads"); + } + + // register the on app close event to print summary stats on app termination + FilterTrafficArgs args; + args.workerThreadsVector = &workerThreadVec; + pcpp::ApplicationEventHandler::getInstance().onApplicationInterrupted( + onApplicationInterrupted, &args); + + // infinite loop (until program is terminated) + while (!args.shouldStop) { + sleep(5); + } } diff --git a/Examples/ExampleApp/main.cpp b/Examples/ExampleApp/main.cpp index fcad0e09fb..116d67ea90 100644 --- a/Examples/ExampleApp/main.cpp +++ b/Examples/ExampleApp/main.cpp @@ -1,45 +1,41 @@ -#include #include #include #include +#include -int main(int argc, char* argv[]) -{ - // open a pcap file for reading - pcpp::PcapFileReaderDevice reader("1_packet.pcap"); - if (!reader.open()) - { - std::cerr << "Error opening the pcap file" << std::endl; - return 1; - } +int main(int argc, char* argv[]) { + // open a pcap file for reading + pcpp::PcapFileReaderDevice reader("1_packet.pcap"); + if (!reader.open()) { + std::cerr << "Error opening the pcap file" << std::endl; + return 1; + } - // read the first (and only) packet from the file - pcpp::RawPacket rawPacket; - if (!reader.getNextPacket(rawPacket)) - { - std::cerr << "Couldn't read the first packet in the file" << std::endl; - return 1; - } + // read the first (and only) packet from the file + pcpp::RawPacket rawPacket; + if (!reader.getNextPacket(rawPacket)) { + std::cerr << "Couldn't read the first packet in the file" << std::endl; + return 1; + } - // parse the raw packet into a parsed packet - pcpp::Packet parsedPacket(&rawPacket); + // parse the raw packet into a parsed packet + pcpp::Packet parsedPacket(&rawPacket); - // verify the packet is IPv4 - if (parsedPacket.isPacketOfType(pcpp::IPv4)) - { - // extract source and dest IPs - pcpp::IPv4Address srcIP = parsedPacket.getLayerOfType()->getSrcIPv4Address(); - pcpp::IPv4Address destIP = parsedPacket.getLayerOfType()->getDstIPv4Address(); + // verify the packet is IPv4 + if (parsedPacket.isPacketOfType(pcpp::IPv4)) { + // extract source and dest IPs + pcpp::IPv4Address srcIP = + parsedPacket.getLayerOfType()->getSrcIPv4Address(); + pcpp::IPv4Address destIP = + parsedPacket.getLayerOfType()->getDstIPv4Address(); - // print source and dest IPs - std::cout - << "Source IP is '" << srcIP << "'; " - << "Dest IP is '" << destIP << "'" - << std::endl; - } + // print source and dest IPs + std::cout << "Source IP is '" << srcIP << "'; " + << "Dest IP is '" << destIP << "'" << std::endl; + } - // close the file - reader.close(); + // close the file + reader.close(); - return 0; + return 0; } diff --git a/Examples/HttpAnalyzer/HttpStatsCollector.h b/Examples/HttpAnalyzer/HttpStatsCollector.h index cf45af7629..fce2a360ea 100644 --- a/Examples/HttpAnalyzer/HttpStatsCollector.h +++ b/Examples/HttpAnalyzer/HttpStatsCollector.h @@ -2,470 +2,501 @@ #include -#include -#include #include "HttpLayer.h" -#include "TcpLayer.h" #include "IPv4Layer.h" -#include "PayloadLayer.h" #include "PacketUtils.h" +#include "PayloadLayer.h" #include "SystemUtils.h" +#include "TcpLayer.h" +#include +#include /** * An auxiliary struct for encapsulating rate stats */ -struct Rate -{ - double currentRate; // periodic rate - double totalRate; // overlal rate - - void clear() - { - currentRate = 0; - totalRate = 0; - } +struct Rate { + double currentRate; // periodic rate + double totalRate; // overlal rate + + void clear() { + currentRate = 0; + totalRate = 0; + } }; /** * A struct for collecting general HTTP stats */ -struct HttpGeneralStats -{ - int numOfHttpFlows; // total number of HTTP flows - Rate httpFlowRate; // rate of HTTP flows - int numOfHttpPipeliningFlows; // total number of HTTP flows that contains at least on HTTP pipelining transaction - int numOfHttpTransactions; // total number of HTTP transactions - Rate httpTransactionsRate; // rate of HTTP transactions - double averageNumOfHttpTransactionsPerFlow; // average number of HTTP transactions per flow - int numOfHttpPackets; // total number of HTTP packets - Rate httpPacketRate; // rate of HTTP packets - double averageNumOfPacketsPerFlow; // average number of HTTP packets per flow - int amountOfHttpTraffic; // total HTTP traffic in bytes - double averageAmountOfDataPerFlow; // average number of HTTP traffic per flow - Rate httpTrafficRate; // rate of HTTP traffic - double sampleTime; // total stats collection time - - void clear() - { - numOfHttpFlows = 0; - httpFlowRate.clear(); - numOfHttpPipeliningFlows = 0; - numOfHttpTransactions = 0; - httpTransactionsRate.clear(); - averageNumOfHttpTransactionsPerFlow = 0; - numOfHttpPackets = 0; - httpPacketRate.clear(); - averageNumOfPacketsPerFlow = 0; - amountOfHttpTraffic = 0; - averageAmountOfDataPerFlow = 0; - httpTrafficRate.clear(); - sampleTime = 0; - } +struct HttpGeneralStats { + int numOfHttpFlows; // total number of HTTP flows + Rate httpFlowRate; // rate of HTTP flows + int numOfHttpPipeliningFlows; // total number of HTTP flows that contains at + // least on HTTP pipelining transaction + int numOfHttpTransactions; // total number of HTTP transactions + Rate httpTransactionsRate; // rate of HTTP transactions + double averageNumOfHttpTransactionsPerFlow; // average number of HTTP + // transactions per flow + int numOfHttpPackets; // total number of HTTP packets + Rate httpPacketRate; // rate of HTTP packets + double averageNumOfPacketsPerFlow; // average number of HTTP packets per flow + int amountOfHttpTraffic; // total HTTP traffic in bytes + double averageAmountOfDataPerFlow; // average number of HTTP traffic per flow + Rate httpTrafficRate; // rate of HTTP traffic + double sampleTime; // total stats collection time + + void clear() { + numOfHttpFlows = 0; + httpFlowRate.clear(); + numOfHttpPipeliningFlows = 0; + numOfHttpTransactions = 0; + httpTransactionsRate.clear(); + averageNumOfHttpTransactionsPerFlow = 0; + numOfHttpPackets = 0; + httpPacketRate.clear(); + averageNumOfPacketsPerFlow = 0; + amountOfHttpTraffic = 0; + averageAmountOfDataPerFlow = 0; + httpTrafficRate.clear(); + sampleTime = 0; + } }; - /** * A base struct for collecting stats on HTTP messages */ -struct HttpMessageStats -{ - int numOfMessages; // total number of HTTP messages of that type (request/response) - Rate messageRate; // rate of HTTP messages of that type - int totalMessageHeaderSize; // total size (in bytes) of data in headers - double averageMessageHeaderSize; // average header size - - virtual ~HttpMessageStats() {} - - virtual void clear() - { - numOfMessages = 0; - messageRate.clear(); - totalMessageHeaderSize = 0; - averageMessageHeaderSize = 0; - } +struct HttpMessageStats { + int numOfMessages; // total number of HTTP messages of that type + // (request/response) + Rate messageRate; // rate of HTTP messages of that type + int totalMessageHeaderSize; // total size (in bytes) of data in headers + double averageMessageHeaderSize; // average header size + + virtual ~HttpMessageStats() {} + + virtual void clear() { + numOfMessages = 0; + messageRate.clear(); + totalMessageHeaderSize = 0; + averageMessageHeaderSize = 0; + } }; - /** * A struct for collecting stats on all HTTP requests */ -struct HttpRequestStats : HttpMessageStats -{ - std::unordered_map > methodCount; // a map for counting the different HTTP methods seen in traffic - std::unordered_map hostnameCount; // a map for counting the hostnames seen in traffic - - void clear() override - { - HttpMessageStats::clear(); - methodCount.clear(); - hostnameCount.clear(); - } +struct HttpRequestStats : HttpMessageStats { + std::unordered_map> + methodCount; // a map for counting the different HTTP methods seen in + // traffic + std::unordered_map + hostnameCount; // a map for counting the hostnames seen in traffic + + void clear() override { + HttpMessageStats::clear(); + methodCount.clear(); + hostnameCount.clear(); + } }; - /** * A struct for collecting stats on all HTTP responses */ -struct HttpResponseStats : HttpMessageStats -{ - std::unordered_map statusCodeCount; // a map for counting the different status codes seen in traffic - std::unordered_map contentTypeCount; // a map for counting the content-types seen in traffic - int numOfMessagesWithContentLength; // total number of responses containing the "content-length" field - int totalContentLengthSize; // total body size extracted by responses containing "content-length" field - double averageContentLengthSize; // average body size - - void clear() override - { - HttpMessageStats::clear(); - numOfMessagesWithContentLength = 0; - totalContentLengthSize = 0; - averageContentLengthSize = 0; - statusCodeCount.clear(); - contentTypeCount.clear(); - } +struct HttpResponseStats : HttpMessageStats { + std::unordered_map + statusCodeCount; // a map for counting the different status codes seen in + // traffic + std::unordered_map + contentTypeCount; // a map for counting the content-types seen in traffic + int numOfMessagesWithContentLength; // total number of responses containing + // the "content-length" field + int totalContentLengthSize; // total body size extracted by responses + // containing "content-length" field + double averageContentLengthSize; // average body size + + void clear() override { + HttpMessageStats::clear(); + numOfMessagesWithContentLength = 0; + totalContentLengthSize = 0; + averageContentLengthSize = 0; + statusCodeCount.clear(); + contentTypeCount.clear(); + } }; - /** - * The HTTP stats collector. Should be called for every packet arriving and also periodically to calculate rates + * The HTTP stats collector. Should be called for every packet arriving and also + * periodically to calculate rates */ -class HttpStatsCollector -{ -public: - - /** - * C'tor - clear all structures - */ - explicit HttpStatsCollector(uint16_t dstPort) - { - clear(); - m_DstPort = dstPort; - } - - /** - * Collect stats for a single packet - */ - void collectStats(pcpp::Packet* httpPacket) - { - // verify packet is TCP - if (!httpPacket->isPacketOfType(pcpp::TCP)) - return; - - // verify packet is port 80 - pcpp::TcpLayer* tcpLayer = httpPacket->getLayerOfType(); - if (!(tcpLayer->getDstPort() == m_DstPort || tcpLayer->getSrcPort() == m_DstPort)) - return; - - // collect general HTTP traffic stats on this packet - uint32_t hashVal = collectHttpTrafficStats(httpPacket); - - // if packet is an HTTP request - collect HTTP request stats on this packet - if (httpPacket->isPacketOfType(pcpp::HTTPRequest)) - { - pcpp::HttpRequestLayer* req = httpPacket->getLayerOfType(); - pcpp::TcpLayer* tcpLayer1 = httpPacket->getLayerOfType(); - collectHttpGeneralStats(tcpLayer1, req, hashVal); - collectRequestStats(req); - } - // if packet is an HTTP response - collect HTTP response stats on this packet - else if (httpPacket->isPacketOfType(pcpp::HTTPResponse)) - { - pcpp::HttpResponseLayer* res = httpPacket->getLayerOfType(); - pcpp::TcpLayer* tcpLayer1 = httpPacket->getLayerOfType(); - collectHttpGeneralStats(tcpLayer1, res, hashVal); - collectResponseStats(res); - } - - // calculate current sample time which is the time-span from start time until current time - m_GeneralStats.sampleTime = getCurTime() - m_StartTime; - } - - /** - * Calculate rates. Should be called periodically - */ - void calcRates() - { - // getting current machine time - double curTime = getCurTime(); - - // getting time from last rate calculation until now - double diffSec = curTime - m_LastCalcRateTime; - - // calculating current rates which are the changes from last rate calculation until now divided by the time passed from - // last rate calculation until now - if (diffSec != 0) - { - m_GeneralStats.httpTrafficRate.currentRate = (m_GeneralStats.amountOfHttpTraffic - m_PrevGeneralStats.amountOfHttpTraffic) / diffSec; - m_GeneralStats.httpPacketRate.currentRate = (m_GeneralStats.numOfHttpPackets - m_PrevGeneralStats.numOfHttpPackets) / diffSec; - m_GeneralStats.httpFlowRate.currentRate = (m_GeneralStats.numOfHttpFlows - m_PrevGeneralStats.numOfHttpFlows) / diffSec; - m_GeneralStats.httpTransactionsRate.currentRate = (m_GeneralStats.numOfHttpTransactions - m_PrevGeneralStats.numOfHttpTransactions) / diffSec; - m_RequestStats.messageRate.currentRate = (m_RequestStats.numOfMessages - m_PrevRequestStats.numOfMessages) / diffSec; - m_ResponseStats.messageRate.currentRate = (m_ResponseStats.numOfMessages - m_PrevResponseStats.numOfMessages) / diffSec; - } - - // getting the time from the beginning of stats collection until now - double diffSecTotal = curTime - m_StartTime; - - // calculating total rate which is the change from beginning of stats collection until now divided by time passed from - // beginning of stats collection until now - if (diffSecTotal != 0) - { - m_GeneralStats.httpTrafficRate.totalRate = m_GeneralStats.amountOfHttpTraffic / diffSecTotal; - m_GeneralStats.httpPacketRate.totalRate = m_GeneralStats.numOfHttpPackets / diffSecTotal; - m_GeneralStats.httpFlowRate.totalRate = m_GeneralStats.numOfHttpFlows / diffSecTotal; - m_GeneralStats.httpTransactionsRate.totalRate = m_GeneralStats.numOfHttpTransactions / diffSecTotal; - m_RequestStats.messageRate.totalRate = m_RequestStats.numOfMessages / diffSecTotal; - m_ResponseStats.messageRate.totalRate = m_ResponseStats.numOfMessages / diffSecTotal; - } - - // saving current numbers for using them in the next rate calculation - m_PrevGeneralStats = m_GeneralStats; - m_PrevRequestStats = m_RequestStats; - m_PrevResponseStats = m_ResponseStats; - - // saving the current time for using in the next rate calculation - m_LastCalcRateTime = curTime; - } - - /** - * Clear all stats collected so far - */ - void clear() - { - m_GeneralStats.clear(); - m_PrevGeneralStats.clear(); - m_RequestStats.clear(); - m_PrevRequestStats.clear(); - m_ResponseStats.clear(); - m_PrevResponseStats.clear(); - m_LastCalcRateTime = getCurTime(); - m_StartTime = m_LastCalcRateTime; - } - - /** - * Get HTTP general stats - */ - HttpGeneralStats& getGeneralStats() { return m_GeneralStats; } - - /** - * Get HTTP request stats - */ - HttpRequestStats& getRequestStats() { return m_RequestStats; } - - /** - * Get HTTP response stats - */ - HttpResponseStats& getResponseStats() { return m_ResponseStats; } - -private: - - /** - * Auxiliary data collected for each flow for help calculating stats on this flow - */ - struct HttpFlowData - { - int numOfOpenTransactions; // number of transactions that were started (request has arrived) but weren't closed yet (response hasn't arrived yet) - pcpp::ProtocolType lastSeenMessage; // the last HTTP message seen on this flow (request, response or neither). Used to identify HTTP pipelining - bool httpPipeliningFlow; // was HTTP pipelining identified on this flow - uint32_t curSeqNumberRequests; // the current TCP sequence number from client to server. Used to identify TCP re-transmission - uint32_t curSeqNumberResponses; // the current TCP sequence number from server to client. Used to identify TCP re-transmission - - void clear() - { - numOfOpenTransactions = 0; - lastSeenMessage = pcpp::UnknownProtocol; - httpPipeliningFlow = false; - } - }; - - - /** - * Collect stats relevant for every HTTP packet (request, response or any other) - * This method calculates and returns the flow key for this packet - */ - uint32_t collectHttpTrafficStats(pcpp::Packet* httpPacket) - { - pcpp::TcpLayer* tcpLayer = httpPacket->getLayerOfType(); - - // count traffic - m_GeneralStats.amountOfHttpTraffic += tcpLayer->getLayerPayloadSize(); - - // count packet num - m_GeneralStats.numOfHttpPackets++; - - // calculate a hash key for this flow to be used in the flow table - uint32_t hashVal = pcpp::hash5Tuple(httpPacket); - - // if flow is a new flow (meaning it's not already in the flow table) - if (m_FlowTable.find(hashVal) == m_FlowTable.end()) - { - // count this new flow - m_GeneralStats.numOfHttpFlows++; - m_FlowTable[hashVal].clear(); - } - - // calculate averages - if (m_FlowTable.size() != 0) - { - m_GeneralStats.averageAmountOfDataPerFlow = (double)m_GeneralStats.amountOfHttpTraffic / (double)m_FlowTable.size(); - m_GeneralStats.averageNumOfPacketsPerFlow = (double)m_GeneralStats.numOfHttpPackets / (double)m_FlowTable.size(); - } - - return hashVal; - } - - - /** - * Collect stats relevant for HTTP messages (requests or responses) - */ - void collectHttpGeneralStats(pcpp::TcpLayer* tcpLayer, pcpp::HttpMessage* message, uint32_t flowKey) - { - // if num of current opened transaction is negative it means something went completely wrong - if (m_FlowTable[flowKey].numOfOpenTransactions < 0) - return; - - if (message->getProtocol() == pcpp::HTTPRequest) - { - // if new packet seq number is smaller than previous seen seq number current it means this packet is - // a re-transmitted packet and should be ignored - if (m_FlowTable[flowKey].curSeqNumberRequests >= pcpp::netToHost32(tcpLayer->getTcpHeader()->sequenceNumber)) - return; - - // a new request - increase num of open transactions - m_FlowTable[flowKey].numOfOpenTransactions++; - - // if the previous message seen on this flow is HTTP request and if flow is not already marked as HTTP pipelining - - // mark it as so and increase number of HTTP pipelining flows - if (!m_FlowTable[flowKey].httpPipeliningFlow && m_FlowTable[flowKey].lastSeenMessage == pcpp::HTTPRequest) - { - m_FlowTable[flowKey].httpPipeliningFlow = true; - m_GeneralStats.numOfHttpPipeliningFlows++; - } - - // set last seen message on flow as HTTP request - m_FlowTable[flowKey].lastSeenMessage = pcpp::HTTPRequest; - - // set last seen sequence number - m_FlowTable[flowKey].curSeqNumberRequests = pcpp::netToHost32(tcpLayer->getTcpHeader()->sequenceNumber); - } - else if (message->getProtocol() == pcpp::HTTPResponse) - { - // if new packet seq number is smaller than previous seen seq number current it means this packet is - // a re-transmitted packet and should be ignored - if (m_FlowTable[flowKey].curSeqNumberResponses >= pcpp::netToHost32(tcpLayer->getTcpHeader()->sequenceNumber)) - return; - - // a response - decrease num of open transactions - m_FlowTable[flowKey].numOfOpenTransactions--; - - // if the previous message seen on this flow is HTTP response and if flow is not already marked as HTTP pipelining - - // mark it as so and increase number of HTTP pipelining flows - if (!m_FlowTable[flowKey].httpPipeliningFlow && m_FlowTable[flowKey].lastSeenMessage == pcpp::HTTPResponse) - { - m_FlowTable[flowKey].httpPipeliningFlow = true; - m_GeneralStats.numOfHttpPipeliningFlows++; - } - - // set last seen message on flow as HTTP response - m_FlowTable[flowKey].lastSeenMessage = pcpp::HTTPResponse; - - if (m_FlowTable[flowKey].numOfOpenTransactions >= 0) - { - // a transaction was closed - increase number of complete transactions - m_GeneralStats.numOfHttpTransactions++; - - // calc average transactions per flow - if (m_FlowTable.size() != 0) - m_GeneralStats.averageNumOfHttpTransactionsPerFlow = (double)m_GeneralStats.numOfHttpTransactions / (double)m_FlowTable.size(); - } - - // set last seen sequence number - m_FlowTable[flowKey].curSeqNumberResponses = pcpp::netToHost32(tcpLayer->getTcpHeader()->sequenceNumber); - } - } - - - /** - * Collect stats relevant for HTTP request messages - */ - void collectRequestStats(pcpp::HttpRequestLayer* req) - { - m_RequestStats.numOfMessages++; - m_RequestStats.totalMessageHeaderSize += req->getHeaderLen(); - if (m_RequestStats.numOfMessages != 0) - m_RequestStats.averageMessageHeaderSize = (double)m_RequestStats.totalMessageHeaderSize / (double)m_RequestStats.numOfMessages; - - // extract hostname and add to hostname count map - pcpp::HeaderField* hostField = req->getFieldByName(PCPP_HTTP_HOST_FIELD); - if (hostField != NULL) - m_RequestStats.hostnameCount[hostField->getFieldValue()]++; - - m_RequestStats.methodCount[req->getFirstLine()->getMethod()]++; - } - - - /** - * Collect stats relevant for HTTP response messages - */ - void collectResponseStats(pcpp::HttpResponseLayer* res) - { - m_ResponseStats.numOfMessages++; - m_ResponseStats.totalMessageHeaderSize += res->getHeaderLen(); - if (m_ResponseStats.numOfMessages != 0) - m_ResponseStats.averageMessageHeaderSize = (double)m_ResponseStats.totalMessageHeaderSize / (double)m_ResponseStats.numOfMessages; - - // extract content-length (if exists) - pcpp::HeaderField* contentLengthField = res->getFieldByName(PCPP_HTTP_CONTENT_LENGTH_FIELD); - if (contentLengthField != NULL) - { - m_ResponseStats.numOfMessagesWithContentLength++; - m_ResponseStats.totalContentLengthSize += atoi(contentLengthField->getFieldValue().c_str()); - if (m_ResponseStats.numOfMessagesWithContentLength != 0) - m_ResponseStats.averageContentLengthSize = (double)m_ResponseStats.totalContentLengthSize / (double)m_ResponseStats.numOfMessagesWithContentLength; - } - - // extract content-type and add to content-type map - pcpp::HeaderField* contentTypeField = res->getFieldByName(PCPP_HTTP_CONTENT_TYPE_FIELD); - if (contentTypeField != NULL) - { - std::string contentType = contentTypeField->getFieldValue(); - - // sometimes content-type contains also the charset it uses. - // for example: "application/javascript; charset=UTF-8" - // remove charset as it's not relevant for these stats - size_t charsetPos = contentType.find(";"); - if (charsetPos != std::string::npos) - contentType.resize(charsetPos); - - m_ResponseStats.contentTypeCount[contentType]++; - } - - // collect status code - create one string from status code and status description (for example: 200 OK) - std::ostringstream stream; - stream << res->getFirstLine()->getStatusCodeAsInt(); - std::string statusCode = stream.str() + " " + res->getFirstLine()->getStatusCodeString(); - m_ResponseStats.statusCodeCount[statusCode]++; - } - - double getCurTime(void) - { - struct timeval tv; - - gettimeofday(&tv, NULL); - - return (((double) tv.tv_sec) + (double) (tv.tv_usec / 1000000.0)); - } - - HttpGeneralStats m_GeneralStats; - HttpGeneralStats m_PrevGeneralStats; - HttpRequestStats m_RequestStats; - HttpRequestStats m_PrevRequestStats; - HttpResponseStats m_ResponseStats; - HttpResponseStats m_PrevResponseStats; - - std::unordered_map m_FlowTable; - - double m_LastCalcRateTime; - double m_StartTime; - uint16_t m_DstPort; +class HttpStatsCollector { + public: + /** + * C'tor - clear all structures + */ + explicit HttpStatsCollector(uint16_t dstPort) { + clear(); + m_DstPort = dstPort; + } + + /** + * Collect stats for a single packet + */ + void collectStats(pcpp::Packet* httpPacket) { + // verify packet is TCP + if (!httpPacket->isPacketOfType(pcpp::TCP)) + return; + + // verify packet is port 80 + pcpp::TcpLayer* tcpLayer = httpPacket->getLayerOfType(); + if (!(tcpLayer->getDstPort() == m_DstPort || + tcpLayer->getSrcPort() == m_DstPort)) + return; + + // collect general HTTP traffic stats on this packet + uint32_t hashVal = collectHttpTrafficStats(httpPacket); + + // if packet is an HTTP request - collect HTTP request stats on this packet + if (httpPacket->isPacketOfType(pcpp::HTTPRequest)) { + pcpp::HttpRequestLayer* req = + httpPacket->getLayerOfType(); + pcpp::TcpLayer* tcpLayer1 = httpPacket->getLayerOfType(); + collectHttpGeneralStats(tcpLayer1, req, hashVal); + collectRequestStats(req); + } + // if packet is an HTTP response - collect HTTP response stats on this + // packet + else if (httpPacket->isPacketOfType(pcpp::HTTPResponse)) { + pcpp::HttpResponseLayer* res = + httpPacket->getLayerOfType(); + pcpp::TcpLayer* tcpLayer1 = httpPacket->getLayerOfType(); + collectHttpGeneralStats(tcpLayer1, res, hashVal); + collectResponseStats(res); + } + + // calculate current sample time which is the time-span from start time + // until current time + m_GeneralStats.sampleTime = getCurTime() - m_StartTime; + } + + /** + * Calculate rates. Should be called periodically + */ + void calcRates() { + // getting current machine time + double curTime = getCurTime(); + + // getting time from last rate calculation until now + double diffSec = curTime - m_LastCalcRateTime; + + // calculating current rates which are the changes from last rate + // calculation until now divided by the time passed from last rate + // calculation until now + if (diffSec != 0) { + m_GeneralStats.httpTrafficRate.currentRate = + (m_GeneralStats.amountOfHttpTraffic - + m_PrevGeneralStats.amountOfHttpTraffic) / + diffSec; + m_GeneralStats.httpPacketRate.currentRate = + (m_GeneralStats.numOfHttpPackets - + m_PrevGeneralStats.numOfHttpPackets) / + diffSec; + m_GeneralStats.httpFlowRate.currentRate = + (m_GeneralStats.numOfHttpFlows - m_PrevGeneralStats.numOfHttpFlows) / + diffSec; + m_GeneralStats.httpTransactionsRate.currentRate = + (m_GeneralStats.numOfHttpTransactions - + m_PrevGeneralStats.numOfHttpTransactions) / + diffSec; + m_RequestStats.messageRate.currentRate = + (m_RequestStats.numOfMessages - m_PrevRequestStats.numOfMessages) / + diffSec; + m_ResponseStats.messageRate.currentRate = + (m_ResponseStats.numOfMessages - m_PrevResponseStats.numOfMessages) / + diffSec; + } + + // getting the time from the beginning of stats collection until now + double diffSecTotal = curTime - m_StartTime; + + // calculating total rate which is the change from beginning of stats + // collection until now divided by time passed from beginning of stats + // collection until now + if (diffSecTotal != 0) { + m_GeneralStats.httpTrafficRate.totalRate = + m_GeneralStats.amountOfHttpTraffic / diffSecTotal; + m_GeneralStats.httpPacketRate.totalRate = + m_GeneralStats.numOfHttpPackets / diffSecTotal; + m_GeneralStats.httpFlowRate.totalRate = + m_GeneralStats.numOfHttpFlows / diffSecTotal; + m_GeneralStats.httpTransactionsRate.totalRate = + m_GeneralStats.numOfHttpTransactions / diffSecTotal; + m_RequestStats.messageRate.totalRate = + m_RequestStats.numOfMessages / diffSecTotal; + m_ResponseStats.messageRate.totalRate = + m_ResponseStats.numOfMessages / diffSecTotal; + } + + // saving current numbers for using them in the next rate calculation + m_PrevGeneralStats = m_GeneralStats; + m_PrevRequestStats = m_RequestStats; + m_PrevResponseStats = m_ResponseStats; + + // saving the current time for using in the next rate calculation + m_LastCalcRateTime = curTime; + } + + /** + * Clear all stats collected so far + */ + void clear() { + m_GeneralStats.clear(); + m_PrevGeneralStats.clear(); + m_RequestStats.clear(); + m_PrevRequestStats.clear(); + m_ResponseStats.clear(); + m_PrevResponseStats.clear(); + m_LastCalcRateTime = getCurTime(); + m_StartTime = m_LastCalcRateTime; + } + + /** + * Get HTTP general stats + */ + HttpGeneralStats& getGeneralStats() { return m_GeneralStats; } + + /** + * Get HTTP request stats + */ + HttpRequestStats& getRequestStats() { return m_RequestStats; } + + /** + * Get HTTP response stats + */ + HttpResponseStats& getResponseStats() { return m_ResponseStats; } + + private: + /** + * Auxiliary data collected for each flow for help calculating stats on this + * flow + */ + struct HttpFlowData { + int numOfOpenTransactions; // number of transactions that were started + // (request has arrived) but weren't closed yet + // (response hasn't arrived yet) + pcpp::ProtocolType lastSeenMessage; // the last HTTP message seen on this + // flow (request, response or neither). + // Used to identify HTTP pipelining + bool httpPipeliningFlow; // was HTTP pipelining identified on this flow + uint32_t + curSeqNumberRequests; // the current TCP sequence number from client to + // server. Used to identify TCP re-transmission + uint32_t + curSeqNumberResponses; // the current TCP sequence number from server to + // client. Used to identify TCP re-transmission + + void clear() { + numOfOpenTransactions = 0; + lastSeenMessage = pcpp::UnknownProtocol; + httpPipeliningFlow = false; + } + }; + + /** + * Collect stats relevant for every HTTP packet (request, response or any + * other) This method calculates and returns the flow key for this packet + */ + uint32_t collectHttpTrafficStats(pcpp::Packet* httpPacket) { + pcpp::TcpLayer* tcpLayer = httpPacket->getLayerOfType(); + + // count traffic + m_GeneralStats.amountOfHttpTraffic += tcpLayer->getLayerPayloadSize(); + + // count packet num + m_GeneralStats.numOfHttpPackets++; + + // calculate a hash key for this flow to be used in the flow table + uint32_t hashVal = pcpp::hash5Tuple(httpPacket); + + // if flow is a new flow (meaning it's not already in the flow table) + if (m_FlowTable.find(hashVal) == m_FlowTable.end()) { + // count this new flow + m_GeneralStats.numOfHttpFlows++; + m_FlowTable[hashVal].clear(); + } + + // calculate averages + if (m_FlowTable.size() != 0) { + m_GeneralStats.averageAmountOfDataPerFlow = + (double)m_GeneralStats.amountOfHttpTraffic / + (double)m_FlowTable.size(); + m_GeneralStats.averageNumOfPacketsPerFlow = + (double)m_GeneralStats.numOfHttpPackets / (double)m_FlowTable.size(); + } + + return hashVal; + } + + /** + * Collect stats relevant for HTTP messages (requests or responses) + */ + void collectHttpGeneralStats(pcpp::TcpLayer* tcpLayer, + pcpp::HttpMessage* message, uint32_t flowKey) { + // if num of current opened transaction is negative it means something went + // completely wrong + if (m_FlowTable[flowKey].numOfOpenTransactions < 0) + return; + + if (message->getProtocol() == pcpp::HTTPRequest) { + // if new packet seq number is smaller than previous seen seq number + // current it means this packet is a re-transmitted packet and should be + // ignored + if (m_FlowTable[flowKey].curSeqNumberRequests >= + pcpp::netToHost32(tcpLayer->getTcpHeader()->sequenceNumber)) + return; + + // a new request - increase num of open transactions + m_FlowTable[flowKey].numOfOpenTransactions++; + + // if the previous message seen on this flow is HTTP request and if flow + // is not already marked as HTTP pipelining - mark it as so and increase + // number of HTTP pipelining flows + if (!m_FlowTable[flowKey].httpPipeliningFlow && + m_FlowTable[flowKey].lastSeenMessage == pcpp::HTTPRequest) { + m_FlowTable[flowKey].httpPipeliningFlow = true; + m_GeneralStats.numOfHttpPipeliningFlows++; + } + + // set last seen message on flow as HTTP request + m_FlowTable[flowKey].lastSeenMessage = pcpp::HTTPRequest; + + // set last seen sequence number + m_FlowTable[flowKey].curSeqNumberRequests = + pcpp::netToHost32(tcpLayer->getTcpHeader()->sequenceNumber); + } else if (message->getProtocol() == pcpp::HTTPResponse) { + // if new packet seq number is smaller than previous seen seq number + // current it means this packet is a re-transmitted packet and should be + // ignored + if (m_FlowTable[flowKey].curSeqNumberResponses >= + pcpp::netToHost32(tcpLayer->getTcpHeader()->sequenceNumber)) + return; + + // a response - decrease num of open transactions + m_FlowTable[flowKey].numOfOpenTransactions--; + + // if the previous message seen on this flow is HTTP response and if flow + // is not already marked as HTTP pipelining - mark it as so and increase + // number of HTTP pipelining flows + if (!m_FlowTable[flowKey].httpPipeliningFlow && + m_FlowTable[flowKey].lastSeenMessage == pcpp::HTTPResponse) { + m_FlowTable[flowKey].httpPipeliningFlow = true; + m_GeneralStats.numOfHttpPipeliningFlows++; + } + + // set last seen message on flow as HTTP response + m_FlowTable[flowKey].lastSeenMessage = pcpp::HTTPResponse; + + if (m_FlowTable[flowKey].numOfOpenTransactions >= 0) { + // a transaction was closed - increase number of complete transactions + m_GeneralStats.numOfHttpTransactions++; + + // calc average transactions per flow + if (m_FlowTable.size() != 0) + m_GeneralStats.averageNumOfHttpTransactionsPerFlow = + (double)m_GeneralStats.numOfHttpTransactions / + (double)m_FlowTable.size(); + } + + // set last seen sequence number + m_FlowTable[flowKey].curSeqNumberResponses = + pcpp::netToHost32(tcpLayer->getTcpHeader()->sequenceNumber); + } + } + + /** + * Collect stats relevant for HTTP request messages + */ + void collectRequestStats(pcpp::HttpRequestLayer* req) { + m_RequestStats.numOfMessages++; + m_RequestStats.totalMessageHeaderSize += req->getHeaderLen(); + if (m_RequestStats.numOfMessages != 0) + m_RequestStats.averageMessageHeaderSize = + (double)m_RequestStats.totalMessageHeaderSize / + (double)m_RequestStats.numOfMessages; + + // extract hostname and add to hostname count map + pcpp::HeaderField* hostField = req->getFieldByName(PCPP_HTTP_HOST_FIELD); + if (hostField != NULL) + m_RequestStats.hostnameCount[hostField->getFieldValue()]++; + + m_RequestStats.methodCount[req->getFirstLine()->getMethod()]++; + } + + /** + * Collect stats relevant for HTTP response messages + */ + void collectResponseStats(pcpp::HttpResponseLayer* res) { + m_ResponseStats.numOfMessages++; + m_ResponseStats.totalMessageHeaderSize += res->getHeaderLen(); + if (m_ResponseStats.numOfMessages != 0) + m_ResponseStats.averageMessageHeaderSize = + (double)m_ResponseStats.totalMessageHeaderSize / + (double)m_ResponseStats.numOfMessages; + + // extract content-length (if exists) + pcpp::HeaderField* contentLengthField = + res->getFieldByName(PCPP_HTTP_CONTENT_LENGTH_FIELD); + if (contentLengthField != NULL) { + m_ResponseStats.numOfMessagesWithContentLength++; + m_ResponseStats.totalContentLengthSize += + atoi(contentLengthField->getFieldValue().c_str()); + if (m_ResponseStats.numOfMessagesWithContentLength != 0) + m_ResponseStats.averageContentLengthSize = + (double)m_ResponseStats.totalContentLengthSize / + (double)m_ResponseStats.numOfMessagesWithContentLength; + } + + // extract content-type and add to content-type map + pcpp::HeaderField* contentTypeField = + res->getFieldByName(PCPP_HTTP_CONTENT_TYPE_FIELD); + if (contentTypeField != NULL) { + std::string contentType = contentTypeField->getFieldValue(); + + // sometimes content-type contains also the charset it uses. + // for example: "application/javascript; charset=UTF-8" + // remove charset as it's not relevant for these stats + size_t charsetPos = contentType.find(";"); + if (charsetPos != std::string::npos) + contentType.resize(charsetPos); + + m_ResponseStats.contentTypeCount[contentType]++; + } + + // collect status code - create one string from status code and status + // description (for example: 200 OK) + std::ostringstream stream; + stream << res->getFirstLine()->getStatusCodeAsInt(); + std::string statusCode = + stream.str() + " " + res->getFirstLine()->getStatusCodeString(); + m_ResponseStats.statusCodeCount[statusCode]++; + } + + double getCurTime(void) { + struct timeval tv; + + gettimeofday(&tv, NULL); + + return (((double)tv.tv_sec) + (double)(tv.tv_usec / 1000000.0)); + } + + HttpGeneralStats m_GeneralStats; + HttpGeneralStats m_PrevGeneralStats; + HttpRequestStats m_RequestStats; + HttpRequestStats m_PrevRequestStats; + HttpResponseStats m_ResponseStats; + HttpResponseStats m_PrevResponseStats; + + std::unordered_map m_FlowTable; + + double m_LastCalcRateTime; + double m_StartTime; + uint16_t m_DstPort; }; diff --git a/Examples/HttpAnalyzer/main.cpp b/Examples/HttpAnalyzer/main.cpp index c4240ee888..408a0896ca 100644 --- a/Examples/HttpAnalyzer/main.cpp +++ b/Examples/HttpAnalyzer/main.cpp @@ -1,13 +1,18 @@ /** * HttpAnalyzer application * ======================== - * This application analyzes HTTP traffic and presents detailed and diverse information about it. It can operate in live traffic - * mode where this information is collected on live packets or in file mode where packets are being read from a pcap/pcapng file. The - * information collected by this application includes: + * This application analyzes HTTP traffic and presents detailed and diverse + * information about it. It can operate in live traffic mode where this + * information is collected on live packets or in file mode where packets are + * being read from a pcap/pcapng file. The information collected by this + * application includes: * - general data: number of packets, packet rate, amount of traffic, bandwidth - * - flow data: number of flow, flow rate, average packets per flow, average data per flow - * - HTTP data: number and rate of HTTP requests, number and rate of HTTP responses, transaction count and rate, - * average transactions per flow, HTTP header size (total and average), HTTP body size, number of HTTP pipelining flows + * - flow data: number of flow, flow rate, average packets per flow, average + * data per flow + * - HTTP data: number and rate of HTTP requests, number and rate of HTTP + * responses, transaction count and rate, average transactions per flow, HTTP + * header size (total and average), HTTP body size, number of HTTP pipelining + * flows * - hostname map * - HTTP method map * - HTTP status code map @@ -16,549 +21,644 @@ * For more details about modes of operation and parameters run HttpAnalyzer -h */ -#include -#include -#include -#include -#include "PcapLiveDeviceList.h" -#include "PcapFilter.h" -#include "PcapFileDevice.h" #include "HttpStatsCollector.h" -#include "TablePrinter.h" -#include "SystemUtils.h" +#include "PcapFileDevice.h" +#include "PcapFilter.h" +#include "PcapLiveDeviceList.h" #include "PcapPlusPlusVersion.h" +#include "SystemUtils.h" +#include "TablePrinter.h" +#include #include +#include #include #include +#include +#include -#define EXIT_WITH_ERROR(reason) do { \ - printUsage(); \ - std::cout << std::endl << "ERROR: " << reason << std::endl << std::endl; \ - exit(1); \ - } while(0) - - -#define PRINT_STAT_LINE(description, counter, measurement) \ - std::cout \ - << std::left << std::setw(40) << (std::string(description) + ":") \ - << std::right << std::setw(15) << std::fixed << std::showpoint << std::setprecision(3) << counter \ - << " [" << measurement << "]" << std::endl; - +#define EXIT_WITH_ERROR(reason) \ + do { \ + printUsage(); \ + std::cout << std::endl \ + << "ERROR: " << reason << std::endl \ + << std::endl; \ + exit(1); \ + } while (0) + +#define PRINT_STAT_LINE(description, counter, measurement) \ + std::cout << std::left << std::setw(40) << (std::string(description) + ":") \ + << std::right << std::setw(15) << std::fixed << std::showpoint \ + << std::setprecision(3) << counter << " [" << measurement << "]" \ + << std::endl; #define DEFAULT_CALC_RATES_PERIOD_SEC 2 - -static struct option HttpAnalyzerOptions[] = -{ - {"interface", required_argument, nullptr, 'i'}, - {"dst-port", required_argument, nullptr, 'p'}, - {"input-file", required_argument, nullptr, 'f'}, - {"output-file", required_argument, nullptr, 'o'}, - {"rate-calc-period", required_argument, nullptr, 'r'}, - {"disable-rates-print", no_argument, nullptr, 'd'}, - {"list-interfaces", no_argument, nullptr, 'l'}, - {"help", no_argument, nullptr, 'h'}, - {"version", no_argument, nullptr, 'v'}, - {nullptr, 0, nullptr, 0} +static struct option HttpAnalyzerOptions[] = { + {"interface", required_argument, nullptr, 'i'}, + {"dst-port", required_argument, nullptr, 'p'}, + {"input-file", required_argument, nullptr, 'f'}, + {"output-file", required_argument, nullptr, 'o'}, + {"rate-calc-period", required_argument, nullptr, 'r'}, + {"disable-rates-print", no_argument, nullptr, 'd'}, + {"list-interfaces", no_argument, nullptr, 'l'}, + {"help", no_argument, nullptr, 'h'}, + {"version", no_argument, nullptr, 'v'}, + {nullptr, 0, nullptr, 0}}; + +struct HttpPacketArrivedData { + HttpStatsCollector* statsCollector; + pcpp::PcapFileWriterDevice* pcapWriter; }; - -struct HttpPacketArrivedData -{ - HttpStatsCollector* statsCollector; - pcpp::PcapFileWriterDevice* pcapWriter; -}; - - /** * Print application usage */ -void printUsage() -{ - std::cout << std::endl - << "Usage: PCAP file mode:" << std::endl - << "----------------------" << std::endl - << pcpp::AppName::get() << " [-vh] -f input_file" << std::endl - << std::endl - << "Options:" << std::endl - << std::endl - << " -f : The input pcap/pcapng file to analyze. Required argument for this mode" << std::endl - << " -v : Displays the current version and exists" << std::endl - << " -h : Displays this help message and exits" << std::endl - << std::endl - << "Usage: Live traffic mode:" << std::endl - << "-------------------------" << std::endl - << pcpp::AppName::get() << " [-hvld] [-o output_file] [-r calc_period] [-p dst_port] -i interface" << std::endl - << std::endl - << "Options:" << std::endl - << std::endl - << " -i interface : Use the specified interface. Can be interface name (e.g eth0) or interface IPv4 address" << std::endl - << " -p dst_port : Use the specified port (optional parameter, the default is 80)" << std::endl - << " -o output_file : Save all captured HTTP packets to a pcap file. Notice this may cause performance degradation" << std::endl - << " -r calc_period : The period in seconds to calculate rates. If not provided default is 2 seconds" << std::endl - << " -d : Disable periodic rates calculation" << std::endl - << " -h : Displays this help message and exits" << std::endl - << " -v : Displays the current version and exists" << std::endl - << " -l : Print the list of interfaces and exists" << std::endl - << std::endl; +void printUsage() { + std::cout + << std::endl + << "Usage: PCAP file mode:" << std::endl + << "----------------------" << std::endl + << pcpp::AppName::get() << " [-vh] -f input_file" << std::endl + << std::endl + << "Options:" << std::endl + << std::endl + << " -f : The input pcap/pcapng file to analyze. Required " + "argument for this mode" + << std::endl + << " -v : Displays the current version and exists" + << std::endl + << " -h : Displays this help message and exits" + << std::endl + << std::endl + << "Usage: Live traffic mode:" << std::endl + << "-------------------------" << std::endl + << pcpp::AppName::get() + << " [-hvld] [-o output_file] [-r calc_period] [-p dst_port] -i interface" + << std::endl + << std::endl + << "Options:" << std::endl + << std::endl + << " -i interface : Use the specified interface. Can be interface " + "name (e.g eth0) or interface IPv4 address" + << std::endl + << " -p dst_port : Use the specified port (optional parameter, the " + "default is 80)" + << std::endl + << " -o output_file : Save all captured HTTP packets to a pcap file. " + "Notice this may cause performance degradation" + << std::endl + << " -r calc_period : The period in seconds to calculate rates. If " + "not provided default is 2 seconds" + << std::endl + << " -d : Disable periodic rates calculation" << std::endl + << " -h : Displays this help message and exits" + << std::endl + << " -v : Displays the current version and exists" + << std::endl + << " -l : Print the list of interfaces and exists" + << std::endl + << std::endl; } - /** * Print application version */ -void printAppVersion() -{ - std::cout - << pcpp::AppName::get() << " " << pcpp::getPcapPlusPlusVersionFull() << std::endl - << "Built: " << pcpp::getBuildDateTime() << std::endl - << "Built from: " << pcpp::getGitInfo() << std::endl; - exit(0); +void printAppVersion() { + std::cout << pcpp::AppName::get() << " " << pcpp::getPcapPlusPlusVersionFull() + << std::endl + << "Built: " << pcpp::getBuildDateTime() << std::endl + << "Built from: " << pcpp::getGitInfo() << std::endl; + exit(0); } - /** * Go over all interfaces and output their names */ -void listInterfaces() -{ - const std::vector& liveDevices = pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDevicesList(); - - std::cout << std::endl << "Network interfaces:" << std::endl; - for(const auto& device : liveDevices){ - std::cout << " -> Name: '" << device->getName() << "' IP address: " << device->getIPv4Address().toString() << std::endl; - } - exit(0); +void listInterfaces() { + const std::vector& liveDevices = + pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDevicesList(); + + std::cout << std::endl + << "Network interfaces:" << std::endl; + for (const auto& device : liveDevices) { + std::cout << " -> Name: '" << device->getName() + << "' IP address: " << device->getIPv4Address().toString() + << std::endl; + } + exit(0); } - -void printStatsHeadline(const std::string &description) -{ - std::cout << std::endl << description << std::endl << std::string(description.length(),'-') << std::endl << std::endl; +void printStatsHeadline(const std::string& description) { + std::cout << std::endl + << description << std::endl + << std::string(description.length(), '-') << std::endl + << std::endl; } - /** * packet capture callback - called whenever a packet arrives */ -void httpPacketArrive(pcpp::RawPacket* packet, pcpp::PcapLiveDevice* dev, void* cookie) -{ - // parse the packet - pcpp::Packet parsedPacket(packet); +void httpPacketArrive(pcpp::RawPacket* packet, pcpp::PcapLiveDevice* dev, + void* cookie) { + // parse the packet + pcpp::Packet parsedPacket(packet); - HttpPacketArrivedData* data = (HttpPacketArrivedData*)cookie; + HttpPacketArrivedData* data = (HttpPacketArrivedData*)cookie; - // give the packet to the collector - data->statsCollector->collectStats(&parsedPacket); + // give the packet to the collector + data->statsCollector->collectStats(&parsedPacket); - // if needed - write the packet to the output pcap file - if (data->pcapWriter != nullptr) - { - data->pcapWriter->writePacket(*packet); - } + // if needed - write the packet to the output pcap file + if (data->pcapWriter != nullptr) { + data->pcapWriter->writePacket(*packet); + } } /** * Print the method count table */ -void printMethods(const HttpRequestStats& reqStatscollector) -{ - // create the table - std::vector columnNames = {"Method", "Count"}; - std::vector columnsWidths = {9, 5}; - pcpp::TablePrinter printer(columnNames, columnsWidths); - - // Copy elements to a vector - std::vector> map2vec(reqStatscollector.methodCount.begin(), reqStatscollector.methodCount.end()); - std::sort(map2vec.begin(), map2vec.end(), - [](const std::pair& left, - const std::pair& right) { return left.second > right.second; }); - - // go over the method count table, print each method and the aggregated figure - for (auto iter : map2vec) - { - std::stringstream values; - - switch (iter.first) - { - case pcpp::HttpRequestLayer::HttpGET: - values << "GET" << "|" << reqStatscollector.methodCount.at(pcpp::HttpRequestLayer::HttpGET); - break; - case pcpp::HttpRequestLayer::HttpPOST: - values << "POST" << "|" << reqStatscollector.methodCount.at(pcpp::HttpRequestLayer::HttpPOST); - break; - case pcpp::HttpRequestLayer::HttpCONNECT: - values << "CONNECT" << "|" << reqStatscollector.methodCount.at(pcpp::HttpRequestLayer::HttpCONNECT); - break; - case pcpp::HttpRequestLayer::HttpDELETE: - values << "DELETE" << "|" << reqStatscollector.methodCount.at(pcpp::HttpRequestLayer::HttpDELETE); - break; - case pcpp::HttpRequestLayer::HttpHEAD: - values << "HEAD" << "|" << reqStatscollector.methodCount.at(pcpp::HttpRequestLayer::HttpHEAD); - break; - case pcpp::HttpRequestLayer::HttpOPTIONS: - values << "OPTIONS" << "|" << reqStatscollector.methodCount.at(pcpp::HttpRequestLayer::HttpOPTIONS); - break; - case pcpp::HttpRequestLayer::HttpPATCH: - values << "PATCH" << "|" << reqStatscollector.methodCount.at(pcpp::HttpRequestLayer::HttpPATCH); - break; - case pcpp::HttpRequestLayer::HttpPUT: - values << "PUT" << "|" << reqStatscollector.methodCount.at(pcpp::HttpRequestLayer::HttpPUT); - break; - case pcpp::HttpRequestLayer::HttpTRACE: - values << "TRACE" << "|" << reqStatscollector.methodCount.at(pcpp::HttpRequestLayer::HttpTRACE); - break; - default: - break; - } - - if(iter.first != pcpp::HttpRequestLayer::HttpMethod::HttpMethodUnknown) - { - printer.printRow(values.str(), '|'); - } - } +void printMethods(const HttpRequestStats& reqStatscollector) { + // create the table + std::vector columnNames = {"Method", "Count"}; + std::vector columnsWidths = {9, 5}; + pcpp::TablePrinter printer(columnNames, columnsWidths); + + // Copy elements to a vector + std::vector> map2vec( + reqStatscollector.methodCount.begin(), + reqStatscollector.methodCount.end()); + std::sort( + map2vec.begin(), map2vec.end(), + [](const std::pair& left, + const std::pair& right) { + return left.second > right.second; + }); + + // go over the method count table, print each method and the aggregated figure + for (auto iter : map2vec) { + std::stringstream values; + + switch (iter.first) { + case pcpp::HttpRequestLayer::HttpGET: + values << "GET" + << "|" + << reqStatscollector.methodCount.at( + pcpp::HttpRequestLayer::HttpGET); + break; + case pcpp::HttpRequestLayer::HttpPOST: + values << "POST" + << "|" + << reqStatscollector.methodCount.at( + pcpp::HttpRequestLayer::HttpPOST); + break; + case pcpp::HttpRequestLayer::HttpCONNECT: + values << "CONNECT" + << "|" + << reqStatscollector.methodCount.at( + pcpp::HttpRequestLayer::HttpCONNECT); + break; + case pcpp::HttpRequestLayer::HttpDELETE: + values << "DELETE" + << "|" + << reqStatscollector.methodCount.at( + pcpp::HttpRequestLayer::HttpDELETE); + break; + case pcpp::HttpRequestLayer::HttpHEAD: + values << "HEAD" + << "|" + << reqStatscollector.methodCount.at( + pcpp::HttpRequestLayer::HttpHEAD); + break; + case pcpp::HttpRequestLayer::HttpOPTIONS: + values << "OPTIONS" + << "|" + << reqStatscollector.methodCount.at( + pcpp::HttpRequestLayer::HttpOPTIONS); + break; + case pcpp::HttpRequestLayer::HttpPATCH: + values << "PATCH" + << "|" + << reqStatscollector.methodCount.at( + pcpp::HttpRequestLayer::HttpPATCH); + break; + case pcpp::HttpRequestLayer::HttpPUT: + values << "PUT" + << "|" + << reqStatscollector.methodCount.at( + pcpp::HttpRequestLayer::HttpPUT); + break; + case pcpp::HttpRequestLayer::HttpTRACE: + values << "TRACE" + << "|" + << reqStatscollector.methodCount.at( + pcpp::HttpRequestLayer::HttpTRACE); + break; + default: + break; + } + + if (iter.first != pcpp::HttpRequestLayer::HttpMethod::HttpMethodUnknown) { + printer.printRow(values.str(), '|'); + } + } } /** - * An auxiliary method for sorting the hostname count map. Used only in printHostnames() + * An auxiliary method for sorting the hostname count map. Used only in + * printHostnames() */ -bool hostnameComparer(const std::pair& leftHost, const std::pair& rightHost) -{ - return leftHost.second > rightHost.second || (leftHost.second == rightHost.second && leftHost.first > rightHost.first); +bool hostnameComparer(const std::pair& leftHost, + const std::pair& rightHost) { + return leftHost.second > rightHost.second || + (leftHost.second == rightHost.second && + leftHost.first > rightHost.first); } /** - * Print the hostname count map to a table sorted by popularity (most popular hostnames will be first) + * Print the hostname count map to a table sorted by popularity (most popular + * hostnames will be first) */ -void printHostnames(HttpRequestStats& reqStatscollector) -{ - // create the table - std::vector columnNames = {"Hostname", "Count"}; - std::vector columnsWidths = {40, 5}; - - pcpp::TablePrinter printer(columnNames, columnsWidths); - - // sort the hostname count map so the most popular hostnames will be first - // since it's not possible to sort a std::map you must copy it to a std::vector and sort it then - std::vector > map2vec(reqStatscollector.hostnameCount.begin(), reqStatscollector.hostnameCount.end()); - std::sort(map2vec.begin(), map2vec.end(), &hostnameComparer); - - // go over all items (hostname + count) in the sorted vector and print them - for(const auto& hostname : map2vec) - { - std::stringstream values; - values << hostname.first << "|" << hostname.second; - printer.printRow(values.str(), '|'); - } +void printHostnames(HttpRequestStats& reqStatscollector) { + // create the table + std::vector columnNames = {"Hostname", "Count"}; + std::vector columnsWidths = {40, 5}; + + pcpp::TablePrinter printer(columnNames, columnsWidths); + + // sort the hostname count map so the most popular hostnames will be first + // since it's not possible to sort a std::map you must copy it to a + // std::vector and sort it then + std::vector> map2vec( + reqStatscollector.hostnameCount.begin(), + reqStatscollector.hostnameCount.end()); + std::sort(map2vec.begin(), map2vec.end(), &hostnameComparer); + + // go over all items (hostname + count) in the sorted vector and print them + for (const auto& hostname : map2vec) { + std::stringstream values; + values << hostname.first << "|" << hostname.second; + printer.printRow(values.str(), '|'); + } } - /** * Print the status code count table */ -void printStatusCodes(const HttpResponseStats& resStatscollector) -{ - // create the table - std::vector columnNames = {"Status Code", "Count"}; - std::vector columnsWidths = {28, 5}; - pcpp::TablePrinter printer(columnNames, columnsWidths); - - // prints the status codes in lexical order - std::vector> map2vec(resStatscollector.statusCodeCount.begin(), resStatscollector.statusCodeCount.end()); - std::sort(map2vec.begin(), map2vec.end(), [](const std::pair& left, const std::pair& right) { return left.first < right.first; }); - for(const auto& statusCodeStat : map2vec) - { - std::stringstream values; - values << statusCodeStat.first << "|" << statusCodeStat.second; - printer.printRow(values.str(), '|'); - } +void printStatusCodes(const HttpResponseStats& resStatscollector) { + // create the table + std::vector columnNames = {"Status Code", "Count"}; + std::vector columnsWidths = {28, 5}; + pcpp::TablePrinter printer(columnNames, columnsWidths); + + // prints the status codes in lexical order + std::vector> map2vec( + resStatscollector.statusCodeCount.begin(), + resStatscollector.statusCodeCount.end()); + std::sort(map2vec.begin(), map2vec.end(), + [](const std::pair& left, + const std::pair& right) { + return left.first < right.first; + }); + for (const auto& statusCodeStat : map2vec) { + std::stringstream values; + values << statusCodeStat.first << "|" << statusCodeStat.second; + printer.printRow(values.str(), '|'); + } } - /** * Print the content-type count table */ -void printContentTypes(const HttpResponseStats& resStatscollector) -{ - // create the table - std::vector columnNames = {"Content-type", "Count"}; - std::vector columnsWidths = {30, 5}; - pcpp::TablePrinter printer(columnNames, columnsWidths); - - // prints the content-types in lexical order - std::vector> map2vec(resStatscollector.contentTypeCount.begin(), resStatscollector.contentTypeCount.end()); - std::sort(map2vec.begin(), map2vec.end(), [](const std::pair& left, const std::pair& right) { return left.first < right.first; }); - for(const auto &contentTypeStat : map2vec) - { - std::stringstream values; - values << contentTypeStat.first << "|" << contentTypeStat.second; - printer.printRow(values.str(), '|'); - } +void printContentTypes(const HttpResponseStats& resStatscollector) { + // create the table + std::vector columnNames = {"Content-type", "Count"}; + std::vector columnsWidths = {30, 5}; + pcpp::TablePrinter printer(columnNames, columnsWidths); + + // prints the content-types in lexical order + std::vector> map2vec( + resStatscollector.contentTypeCount.begin(), + resStatscollector.contentTypeCount.end()); + std::sort(map2vec.begin(), map2vec.end(), + [](const std::pair& left, + const std::pair& right) { + return left.first < right.first; + }); + for (const auto& contentTypeStat : map2vec) { + std::stringstream values; + values << contentTypeStat.first << "|" << contentTypeStat.second; + printer.printRow(values.str(), '|'); + } } - /** - * Print a summary of all statistics collected by the HttpStatsCollector. Should be called when traffic capture was finished + * Print a summary of all statistics collected by the HttpStatsCollector. Should + * be called when traffic capture was finished */ -void printStatsSummary(HttpStatsCollector& collector) -{ - printStatsHeadline("General stats"); - PRINT_STAT_LINE("Sample time", collector.getGeneralStats().sampleTime, "Seconds"); - PRINT_STAT_LINE("Number of HTTP packets", collector.getGeneralStats().numOfHttpPackets, "Packets"); - PRINT_STAT_LINE("Rate of HTTP packets", collector.getGeneralStats().httpPacketRate.totalRate, "Packets/sec"); - PRINT_STAT_LINE("Number of HTTP flows", collector.getGeneralStats().numOfHttpFlows, "Flows"); - PRINT_STAT_LINE("Rate of HTTP flows", collector.getGeneralStats().httpFlowRate.totalRate, "Flows/sec"); - PRINT_STAT_LINE("Number of HTTP pipelining flows", collector.getGeneralStats().numOfHttpPipeliningFlows, "Flows"); - PRINT_STAT_LINE("Number of HTTP transactions", collector.getGeneralStats().numOfHttpTransactions, "Transactions"); - PRINT_STAT_LINE("Rate of HTTP transactions", collector.getGeneralStats().httpTransactionsRate.totalRate, "Transactions/sec"); - PRINT_STAT_LINE("Total HTTP data", collector.getGeneralStats().amountOfHttpTraffic, "Bytes"); - PRINT_STAT_LINE("Rate of HTTP data", collector.getGeneralStats().httpTrafficRate.totalRate, "Bytes/sec"); - PRINT_STAT_LINE("Average packets per flow", collector.getGeneralStats().averageNumOfPacketsPerFlow, "Packets"); - PRINT_STAT_LINE("Average transactions per flow", collector.getGeneralStats().averageNumOfHttpTransactionsPerFlow, "Transactions"); - PRINT_STAT_LINE("Average data per flow", collector.getGeneralStats().averageAmountOfDataPerFlow, "Bytes"); - - printStatsHeadline("HTTP request stats"); - PRINT_STAT_LINE("Number of HTTP requests", collector.getRequestStats().numOfMessages, "Requests"); - PRINT_STAT_LINE("Rate of HTTP requests", collector.getRequestStats().messageRate.totalRate, "Requests/sec"); - PRINT_STAT_LINE("Total data in headers", collector.getRequestStats().totalMessageHeaderSize, "Bytes"); - PRINT_STAT_LINE("Average header size", collector.getRequestStats().averageMessageHeaderSize, "Bytes"); - - printStatsHeadline("HTTP response stats"); - PRINT_STAT_LINE("Number of HTTP responses", collector.getResponseStats().numOfMessages, "Responses"); - PRINT_STAT_LINE("Rate of HTTP responses", collector.getResponseStats().messageRate.totalRate, "Responses/sec"); - PRINT_STAT_LINE("Total data in headers", collector.getResponseStats().totalMessageHeaderSize, "Bytes"); - PRINT_STAT_LINE("Average header size", collector.getResponseStats().averageMessageHeaderSize, "Bytes"); - PRINT_STAT_LINE("Num of responses with content-length", collector.getResponseStats().numOfMessagesWithContentLength, "Responses"); - PRINT_STAT_LINE("Total body size (may be compressed)", collector.getResponseStats().totalContentLengthSize, "Bytes"); - PRINT_STAT_LINE("Average body size", collector.getResponseStats().averageContentLengthSize, "Bytes"); - - printStatsHeadline("HTTP request methods"); - printMethods(collector.getRequestStats()); - - printStatsHeadline("Hostnames count"); - printHostnames(collector.getRequestStats()); - - printStatsHeadline("Status code count"); - printStatusCodes(collector.getResponseStats()); - - printStatsHeadline("Content-type count"); - printContentTypes(collector.getResponseStats()); +void printStatsSummary(HttpStatsCollector& collector) { + printStatsHeadline("General stats"); + PRINT_STAT_LINE("Sample time", collector.getGeneralStats().sampleTime, + "Seconds"); + PRINT_STAT_LINE("Number of HTTP packets", + collector.getGeneralStats().numOfHttpPackets, "Packets"); + PRINT_STAT_LINE("Rate of HTTP packets", + collector.getGeneralStats().httpPacketRate.totalRate, + "Packets/sec"); + PRINT_STAT_LINE("Number of HTTP flows", + collector.getGeneralStats().numOfHttpFlows, "Flows"); + PRINT_STAT_LINE("Rate of HTTP flows", + collector.getGeneralStats().httpFlowRate.totalRate, + "Flows/sec"); + PRINT_STAT_LINE("Number of HTTP pipelining flows", + collector.getGeneralStats().numOfHttpPipeliningFlows, + "Flows"); + PRINT_STAT_LINE("Number of HTTP transactions", + collector.getGeneralStats().numOfHttpTransactions, + "Transactions"); + PRINT_STAT_LINE("Rate of HTTP transactions", + collector.getGeneralStats().httpTransactionsRate.totalRate, + "Transactions/sec"); + PRINT_STAT_LINE("Total HTTP data", + collector.getGeneralStats().amountOfHttpTraffic, "Bytes"); + PRINT_STAT_LINE("Rate of HTTP data", + collector.getGeneralStats().httpTrafficRate.totalRate, + "Bytes/sec"); + PRINT_STAT_LINE("Average packets per flow", + collector.getGeneralStats().averageNumOfPacketsPerFlow, + "Packets"); + PRINT_STAT_LINE( + "Average transactions per flow", + collector.getGeneralStats().averageNumOfHttpTransactionsPerFlow, + "Transactions"); + PRINT_STAT_LINE("Average data per flow", + collector.getGeneralStats().averageAmountOfDataPerFlow, + "Bytes"); + + printStatsHeadline("HTTP request stats"); + PRINT_STAT_LINE("Number of HTTP requests", + collector.getRequestStats().numOfMessages, "Requests"); + PRINT_STAT_LINE("Rate of HTTP requests", + collector.getRequestStats().messageRate.totalRate, + "Requests/sec"); + PRINT_STAT_LINE("Total data in headers", + collector.getRequestStats().totalMessageHeaderSize, "Bytes"); + PRINT_STAT_LINE("Average header size", + collector.getRequestStats().averageMessageHeaderSize, + "Bytes"); + + printStatsHeadline("HTTP response stats"); + PRINT_STAT_LINE("Number of HTTP responses", + collector.getResponseStats().numOfMessages, "Responses"); + PRINT_STAT_LINE("Rate of HTTP responses", + collector.getResponseStats().messageRate.totalRate, + "Responses/sec"); + PRINT_STAT_LINE("Total data in headers", + collector.getResponseStats().totalMessageHeaderSize, "Bytes"); + PRINT_STAT_LINE("Average header size", + collector.getResponseStats().averageMessageHeaderSize, + "Bytes"); + PRINT_STAT_LINE("Num of responses with content-length", + collector.getResponseStats().numOfMessagesWithContentLength, + "Responses"); + PRINT_STAT_LINE("Total body size (may be compressed)", + collector.getResponseStats().totalContentLengthSize, "Bytes"); + PRINT_STAT_LINE("Average body size", + collector.getResponseStats().averageContentLengthSize, + "Bytes"); + + printStatsHeadline("HTTP request methods"); + printMethods(collector.getRequestStats()); + + printStatsHeadline("Hostnames count"); + printHostnames(collector.getRequestStats()); + + printStatsHeadline("Status code count"); + printStatusCodes(collector.getResponseStats()); + + printStatsHeadline("Content-type count"); + printContentTypes(collector.getResponseStats()); } - /** * Print the current rates. Should be called periodically during traffic capture */ -void printCurrentRates(HttpStatsCollector& collector) -{ - printStatsHeadline("Current HTTP rates"); - PRINT_STAT_LINE("Rate of HTTP packets", collector.getGeneralStats().httpPacketRate.currentRate, "Packets/sec"); - PRINT_STAT_LINE("Rate of HTTP flows", collector.getGeneralStats().httpFlowRate.currentRate, "Flows/sec"); - PRINT_STAT_LINE("Rate of HTTP transactions", collector.getGeneralStats().httpTransactionsRate.currentRate, "Transactions/sec"); - PRINT_STAT_LINE("Rate of HTTP data", collector.getGeneralStats().httpTrafficRate.currentRate, "Bytes/sec"); - PRINT_STAT_LINE("Rate of HTTP requests", collector.getRequestStats().messageRate.currentRate, "Requests/sec"); - PRINT_STAT_LINE("Rate of HTTP responses", collector.getResponseStats().messageRate.currentRate, "Responses/sec"); +void printCurrentRates(HttpStatsCollector& collector) { + printStatsHeadline("Current HTTP rates"); + PRINT_STAT_LINE("Rate of HTTP packets", + collector.getGeneralStats().httpPacketRate.currentRate, + "Packets/sec"); + PRINT_STAT_LINE("Rate of HTTP flows", + collector.getGeneralStats().httpFlowRate.currentRate, + "Flows/sec"); + PRINT_STAT_LINE("Rate of HTTP transactions", + collector.getGeneralStats().httpTransactionsRate.currentRate, + "Transactions/sec"); + PRINT_STAT_LINE("Rate of HTTP data", + collector.getGeneralStats().httpTrafficRate.currentRate, + "Bytes/sec"); + PRINT_STAT_LINE("Rate of HTTP requests", + collector.getRequestStats().messageRate.currentRate, + "Requests/sec"); + PRINT_STAT_LINE("Rate of HTTP responses", + collector.getResponseStats().messageRate.currentRate, + "Responses/sec"); } - /** - * The callback to be called when application is terminated by ctrl-c. Stops the endless while loop + * The callback to be called when application is terminated by ctrl-c. Stops the + * endless while loop */ -void onApplicationInterrupted(void* cookie) -{ - bool* shouldStop = (bool*)cookie; - *shouldStop = true; +void onApplicationInterrupted(void* cookie) { + bool* shouldStop = (bool*)cookie; + *shouldStop = true; } - /** * activate HTTP analysis from pcap file */ -void analyzeHttpFromPcapFile(const std::string& pcapFileName, uint16_t dstPort) -{ - // open input file (pcap or pcapng file) - pcpp::IFileReaderDevice* reader = pcpp::IFileReaderDevice::getReader(pcapFileName); - - if (!reader->open()) - EXIT_WITH_ERROR("Could not open input pcap file"); - - // set a port filter on the reader device to process only HTTP packets - pcpp::PortFilter httpPortFilter(dstPort, pcpp::SRC_OR_DST); - if (!reader->setFilter(httpPortFilter)) - EXIT_WITH_ERROR("Could not set up filter on file"); - - // read the input file packet by packet and give it to the HttpStatsCollector for collecting stats - HttpStatsCollector collector(dstPort); - pcpp::RawPacket rawPacket; - while(reader->getNextPacket(rawPacket)) - { - pcpp::Packet parsedPacket(&rawPacket); - collector.collectStats(&parsedPacket); - } - - // print stats summary - std::cout << std::endl << std::endl - << "STATS SUMMARY" << std::endl - << "=============" << std::endl; - printStatsSummary(collector); - - // close input file - reader->close(); - - // free reader memory - delete reader; +void analyzeHttpFromPcapFile(const std::string& pcapFileName, + uint16_t dstPort) { + // open input file (pcap or pcapng file) + pcpp::IFileReaderDevice* reader = + pcpp::IFileReaderDevice::getReader(pcapFileName); + + if (!reader->open()) + EXIT_WITH_ERROR("Could not open input pcap file"); + + // set a port filter on the reader device to process only HTTP packets + pcpp::PortFilter httpPortFilter(dstPort, pcpp::SRC_OR_DST); + if (!reader->setFilter(httpPortFilter)) + EXIT_WITH_ERROR("Could not set up filter on file"); + + // read the input file packet by packet and give it to the HttpStatsCollector + // for collecting stats + HttpStatsCollector collector(dstPort); + pcpp::RawPacket rawPacket; + while (reader->getNextPacket(rawPacket)) { + pcpp::Packet parsedPacket(&rawPacket); + collector.collectStats(&parsedPacket); + } + + // print stats summary + std::cout << std::endl + << std::endl + << "STATS SUMMARY" << std::endl + << "=============" << std::endl; + printStatsSummary(collector); + + // close input file + reader->close(); + + // free reader memory + delete reader; } - /** * activate HTTP analysis from live traffic */ -void analyzeHttpFromLiveTraffic(pcpp::PcapLiveDevice* dev, bool printRatesPeriodically, int printRatePeriod, const std::string& savePacketsToFileName, uint16_t dstPort) -{ - // open the device - if (!dev->open()) - EXIT_WITH_ERROR("Could not open the device"); - - pcpp::PortFilter httpPortFilter(dstPort, pcpp::SRC_OR_DST); - if (!dev->setFilter(httpPortFilter)) - EXIT_WITH_ERROR("Could not set up filter on device"); - - // if needed to save the captured packets to file - open a writer device - pcpp::PcapFileWriterDevice* pcapWriter = nullptr; - if (savePacketsToFileName != "") - { - pcapWriter = new pcpp::PcapFileWriterDevice(savePacketsToFileName); - if (!pcapWriter->open()) - { - EXIT_WITH_ERROR("Could not open pcap file for writing"); - } - } - - // start capturing packets and collecting stats - HttpPacketArrivedData data; - HttpStatsCollector collector(dstPort); - data.statsCollector = &collector; - data.pcapWriter = pcapWriter; - dev->startCapture(httpPacketArrive, &data); - - - // register the on app close event to print summary stats on app termination - bool shouldStop = false; - pcpp::ApplicationEventHandler::getInstance().onApplicationInterrupted(onApplicationInterrupted, &shouldStop); - - while(!shouldStop) - { - pcpp::multiPlatformSleep(printRatePeriod); - - // calculate rates - if (printRatesPeriodically) - { - collector.calcRates(); - printCurrentRates(collector); - } - } - - // stop capturing and close the live device - dev->stopCapture(); - dev->close(); - - // calculate final rates - collector.calcRates(); - - // print stats summary - std::cout << std::endl << std::endl - << "STATS SUMMARY" << std::endl - << "=============" << std::endl; - printStatsSummary(collector); - - // close and free the writer device - if (pcapWriter != nullptr) - { - pcapWriter->close(); - delete pcapWriter; - } +void analyzeHttpFromLiveTraffic(pcpp::PcapLiveDevice* dev, + bool printRatesPeriodically, + int printRatePeriod, + const std::string& savePacketsToFileName, + uint16_t dstPort) { + // open the device + if (!dev->open()) + EXIT_WITH_ERROR("Could not open the device"); + + pcpp::PortFilter httpPortFilter(dstPort, pcpp::SRC_OR_DST); + if (!dev->setFilter(httpPortFilter)) + EXIT_WITH_ERROR("Could not set up filter on device"); + + // if needed to save the captured packets to file - open a writer device + pcpp::PcapFileWriterDevice* pcapWriter = nullptr; + if (savePacketsToFileName != "") { + pcapWriter = new pcpp::PcapFileWriterDevice(savePacketsToFileName); + if (!pcapWriter->open()) { + EXIT_WITH_ERROR("Could not open pcap file for writing"); + } + } + + // start capturing packets and collecting stats + HttpPacketArrivedData data; + HttpStatsCollector collector(dstPort); + data.statsCollector = &collector; + data.pcapWriter = pcapWriter; + dev->startCapture(httpPacketArrive, &data); + + // register the on app close event to print summary stats on app termination + bool shouldStop = false; + pcpp::ApplicationEventHandler::getInstance().onApplicationInterrupted( + onApplicationInterrupted, &shouldStop); + + while (!shouldStop) { + pcpp::multiPlatformSleep(printRatePeriod); + + // calculate rates + if (printRatesPeriodically) { + collector.calcRates(); + printCurrentRates(collector); + } + } + + // stop capturing and close the live device + dev->stopCapture(); + dev->close(); + + // calculate final rates + collector.calcRates(); + + // print stats summary + std::cout << std::endl + << std::endl + << "STATS SUMMARY" << std::endl + << "=============" << std::endl; + printStatsSummary(collector); + + // close and free the writer device + if (pcapWriter != nullptr) { + pcapWriter->close(); + delete pcapWriter; + } } /** * main method of this utility */ -int main(int argc, char* argv[]) -{ - pcpp::AppName::init(argc, argv); - - std::string interfaceNameOrIP = ""; - std::string port = "80"; - bool printRatesPeriodically = true; - int printRatePeriod = DEFAULT_CALC_RATES_PERIOD_SEC; - std::string savePacketsToFileName = ""; - - std::string readPacketsFromPcapFileName = ""; - - - int optionIndex = 0; - int opt = 0; - - while((opt = getopt_long(argc, argv, "i:p:f:o:r:hvld", HttpAnalyzerOptions, &optionIndex)) != -1) - { - switch (opt) - { - case 0: - break; - case 'i': - interfaceNameOrIP = optarg; - break; - case 'p': - port = optarg; - break; - case 'f': - readPacketsFromPcapFileName = optarg; - break; - case 'o': - savePacketsToFileName = optarg; - break; - case 'r': - printRatePeriod = atoi(optarg); - break; - case 'd': - printRatesPeriodically = false; - break; - case 'h': - printUsage(); - exit(0); - break; - case 'v': - printAppVersion(); - break; - case 'l': - listInterfaces(); - break; - default: - printUsage(); - exit(-1); - } - } - - // if no interface nor input pcap file were provided - exit with error - if (readPacketsFromPcapFileName == "" && interfaceNameOrIP == "") - EXIT_WITH_ERROR("Neither interface nor input pcap file were provided"); - - // get the port - int nPort = atoi(port.c_str()); - if (nPort <= 0 || nPort > 65535) - EXIT_WITH_ERROR("Please input a number between 0 to 65535"); - - // analyze in pcap file mode - if (readPacketsFromPcapFileName != "") - { - analyzeHttpFromPcapFile(readPacketsFromPcapFileName, nPort); - } - else // analyze in live traffic mode - { - pcpp::PcapLiveDevice* dev = pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDeviceByIpOrName(interfaceNameOrIP); - if (dev == nullptr) - EXIT_WITH_ERROR("Couldn't find interface by provided IP address or name"); - - // start capturing and analyzing traffic - analyzeHttpFromLiveTraffic(dev, printRatesPeriodically, printRatePeriod, savePacketsToFileName, nPort); - } +int main(int argc, char* argv[]) { + pcpp::AppName::init(argc, argv); + + std::string interfaceNameOrIP = ""; + std::string port = "80"; + bool printRatesPeriodically = true; + int printRatePeriod = DEFAULT_CALC_RATES_PERIOD_SEC; + std::string savePacketsToFileName = ""; + + std::string readPacketsFromPcapFileName = ""; + + int optionIndex = 0; + int opt = 0; + + while ((opt = getopt_long(argc, argv, "i:p:f:o:r:hvld", HttpAnalyzerOptions, + &optionIndex)) != -1) { + switch (opt) { + case 0: + break; + case 'i': + interfaceNameOrIP = optarg; + break; + case 'p': + port = optarg; + break; + case 'f': + readPacketsFromPcapFileName = optarg; + break; + case 'o': + savePacketsToFileName = optarg; + break; + case 'r': + printRatePeriod = atoi(optarg); + break; + case 'd': + printRatesPeriodically = false; + break; + case 'h': + printUsage(); + exit(0); + break; + case 'v': + printAppVersion(); + break; + case 'l': + listInterfaces(); + break; + default: + printUsage(); + exit(-1); + } + } + + // if no interface nor input pcap file were provided - exit with error + if (readPacketsFromPcapFileName == "" && interfaceNameOrIP == "") + EXIT_WITH_ERROR("Neither interface nor input pcap file were provided"); + + // get the port + int nPort = atoi(port.c_str()); + if (nPort <= 0 || nPort > 65535) + EXIT_WITH_ERROR("Please input a number between 0 to 65535"); + + // analyze in pcap file mode + if (readPacketsFromPcapFileName != "") { + analyzeHttpFromPcapFile(readPacketsFromPcapFileName, nPort); + } else // analyze in live traffic mode + { + pcpp::PcapLiveDevice* dev = + pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDeviceByIpOrName( + interfaceNameOrIP); + if (dev == nullptr) + EXIT_WITH_ERROR("Couldn't find interface by provided IP address or name"); + + // start capturing and analyzing traffic + analyzeHttpFromLiveTraffic(dev, printRatesPeriodically, printRatePeriod, + savePacketsToFileName, nPort); + } } diff --git a/Examples/IPDefragUtil/main.cpp b/Examples/IPDefragUtil/main.cpp index 4a84bd8854..9e98d24025 100644 --- a/Examples/IPDefragUtil/main.cpp +++ b/Examples/IPDefragUtil/main.cpp @@ -1,430 +1,419 @@ -#include -#include -#include -#include -#include -#include "PcapPlusPlusVersion.h" +#include "IPReassembly.h" #include "IPv4Layer.h" #include "IPv6Layer.h" -#include "IPReassembly.h" #include "PcapFileDevice.h" +#include "PcapPlusPlusVersion.h" #include "SystemUtils.h" #include "getopt.h" +#include +#include +#include +#include +#include - -#define EXIT_WITH_ERROR(reason) do { \ - printUsage(); \ - std::cout << std::endl << "ERROR: " << reason << std::endl << std::endl; \ - exit(1); \ - } while(0) - - -static struct option DefragUtilOptions[] = -{ - {"output-file", required_argument, nullptr, 'o'}, - {"filter-by-ipid", required_argument, nullptr, 'd'}, - {"bpf-filter", required_argument, nullptr, 'f'}, - {"copy-all-packets", no_argument, nullptr, 'a'}, - {"help", no_argument, nullptr, 'h'}, - {"version", no_argument, nullptr, 'v'}, - {nullptr, 0, nullptr, 0} -}; +#define EXIT_WITH_ERROR(reason) \ + do { \ + printUsage(); \ + std::cout << std::endl \ + << "ERROR: " << reason << std::endl \ + << std::endl; \ + exit(1); \ + } while (0) + +static struct option DefragUtilOptions[] = { + {"output-file", required_argument, nullptr, 'o'}, + {"filter-by-ipid", required_argument, nullptr, 'd'}, + {"bpf-filter", required_argument, nullptr, 'f'}, + {"copy-all-packets", no_argument, nullptr, 'a'}, + {"help", no_argument, nullptr, 'h'}, + {"version", no_argument, nullptr, 'v'}, + {nullptr, 0, nullptr, 0}}; /** * A struct for collecting stats during the de-fragmentation process */ -struct DefragStats -{ - int totalPacketsRead; - int ipv4Packets; - int ipv6Packets; - int ipv4PacketsMatchIpIDs; - int ipv6PacketsMatchFragIDs; - int ipPacketsMatchBpfFilter; - int ipv4FragmentsMatched; - int ipv6FragmentsMatched; - int ipv4PacketsDefragmented; - int ipv6PacketsDefragmented; - int totalPacketsWritten; - - void clear() { memset(this, 0, sizeof(DefragStats)); } - DefragStats() { clear(); } +struct DefragStats { + int totalPacketsRead; + int ipv4Packets; + int ipv6Packets; + int ipv4PacketsMatchIpIDs; + int ipv6PacketsMatchFragIDs; + int ipPacketsMatchBpfFilter; + int ipv4FragmentsMatched; + int ipv6FragmentsMatched; + int ipv4PacketsDefragmented; + int ipv6PacketsDefragmented; + int totalPacketsWritten; + + void clear() { memset(this, 0, sizeof(DefragStats)); } + DefragStats() { clear(); } }; - /** * Print application usage */ -void printUsage() -{ - std::cout << std::endl - << "Usage:" << std::endl - << "------" << std::endl - << pcpp::AppName::get() << " input_file -o output_file [-d frag_ids] [-f bpf_filter] [-a] [-h] [-v]" << std::endl - << std::endl - << "Options:" << std::endl - << std::endl - << " input_file : Input pcap/pcapng file" << std::endl - << " -o output_file : Output file. Output file type (pcap/pcapng) will match the input file type" << std::endl - << " -d frag_ids : De-fragment only fragments that match this comma-separated list of IP IDs (for IPv4) or" << std::endl - << " fragment IDs (for IPv6) in decimal format" << std::endl - << " -f bpf_filter : De-fragment only fragments that match bpf_filter. Filter should be provided in Berkeley Packet Filter (BPF)" << std::endl - << " syntax (http://biot.com/capstats/bpf.html) i.e: 'ip net 1.1.1.1'" << std::endl - << " -a : Copy all packets (those who were de-fragmented and those who weren't) to output file" << std::endl - << " -v : Displays the current version and exits" << std::endl - << " -h : Displays this help message and exits" << std::endl - << std::endl; +void printUsage() { + std::cout + << std::endl + << "Usage:" << std::endl + << "------" << std::endl + << pcpp::AppName::get() + << " input_file -o output_file [-d frag_ids] [-f bpf_filter] [-a] [-h] " + "[-v]" + << std::endl + << std::endl + << "Options:" << std::endl + << std::endl + << " input_file : Input pcap/pcapng file" << std::endl + << " -o output_file : Output file. Output file type (pcap/pcapng) " + "will match the input file type" + << std::endl + << " -d frag_ids : De-fragment only fragments that match this " + "comma-separated list of IP IDs (for IPv4) or" + << std::endl + << " fragment IDs (for IPv6) in decimal format" + << std::endl + << " -f bpf_filter : De-fragment only fragments that match " + "bpf_filter. Filter should be provided in Berkeley Packet Filter (BPF)" + << std::endl + << " syntax (http://biot.com/capstats/bpf.html) " + "i.e: 'ip net 1.1.1.1'" + << std::endl + << " -a : Copy all packets (those who were de-fragmented " + "and those who weren't) to output file" + << std::endl + << " -v : Displays the current version and exits" + << std::endl + << " -h : Displays this help message and exits" + << std::endl + << std::endl; } - /** * Print application version */ -void printAppVersion() -{ - std::cout - << pcpp::AppName::get() << " " << pcpp::getPcapPlusPlusVersionFull() << std::endl - << "Built: " << pcpp::getBuildDateTime() << std::endl - << "Built from: " << pcpp::getGitInfo() << std::endl; - exit(0); +void printAppVersion() { + std::cout << pcpp::AppName::get() << " " << pcpp::getPcapPlusPlusVersionFull() + << std::endl + << "Built: " << pcpp::getBuildDateTime() << std::endl + << "Built from: " << pcpp::getGitInfo() << std::endl; + exit(0); } - /** - * This method reads packets from the input file, decided which fragments pass the filters set by the user, de-fragment the fragments - * who pass them, and writes the result packets to the output file + * This method reads packets from the input file, decided which fragments pass + * the filters set by the user, de-fragment the fragments who pass them, and + * writes the result packets to the output file */ -void processPackets(pcpp::IFileReaderDevice* reader, pcpp::IFileWriterDevice* writer, - bool filterByBpf, const std::string& bpfFilter, - bool filterByIpID, std::map fragIDs, - bool copyAllPacketsToOutputFile, - DefragStats& stats) -{ - pcpp::RawPacket rawPacket; - pcpp::BPFStringFilter filter(bpfFilter); - - // create an instance of IPReassembly - pcpp::IPReassembly ipReassembly; - - pcpp::IPReassembly::ReassemblyStatus status; - - // read all packet from input file - while (reader->getNextPacket(rawPacket)) - { - bool defragPacket = true; - - stats.totalPacketsRead++; - - // if user requested to filter by BPF - if (filterByBpf) - { - // check if packet matches the BPF filter supplied by the user - if (pcpp::IPcapDevice::matchPacketWithFilter(filter, &rawPacket)) - { - stats.ipPacketsMatchBpfFilter++; - } - else // if not - set the packet as not marked for de-fragmentation - { - defragPacket = false; - } - } - - bool isIPv4Packet = false; - bool isIPv6Packet = false; - - // check if packet is of type IPv4 or IPv6 - pcpp::Packet parsedPacket(&rawPacket); - if (parsedPacket.isPacketOfType(pcpp::IPv4)) - { - stats.ipv4Packets++; - isIPv4Packet = true; - } - else if (parsedPacket.isPacketOfType(pcpp::IPv6)) - { - stats.ipv6Packets++; - isIPv6Packet = true; - } - else // if not - set the packet as not marked for de-fragmentation - { - defragPacket = false; - } - - // if user requested to filter by IP ID - if (filterByIpID) - { - // get the IPv4 layer - pcpp::IPv4Layer* ipv4Layer = parsedPacket.getLayerOfType(); - if (ipv4Layer != nullptr) - { - // check if packet ID matches one of the IP IDs requested by the user - if (fragIDs.find((uint32_t)pcpp::netToHost16(ipv4Layer->getIPv4Header()->ipId)) != fragIDs.end()) - { - stats.ipv4PacketsMatchIpIDs++; - } - else // if not - set the packet as not marked for de-fragmentation - { - defragPacket = false; - } - } - - // get the IPv6 layer - pcpp::IPv6Layer* ipv6Layer = parsedPacket.getLayerOfType(); - if (ipv6Layer != nullptr && ipv6Layer->isFragment()) - { - // if this packet is a fragment, get the fragmentation header - pcpp::IPv6FragmentationHeader* fragHdr = ipv6Layer->getExtensionOfType(); - - // check if fragment ID matches one of the fragment IDs requested by the user - if (fragIDs.find(pcpp::netToHost32(fragHdr->getFragHeader()->id)) != fragIDs.end()) - { - stats.ipv6PacketsMatchFragIDs++; - } - else // if not - set the packet as not marked for de-fragmentation - { - defragPacket = false; - } - } - } - - // if fragment is marked for de-fragmentation - if (defragPacket) - { - // process the packet in the IP reassembly mechanism - pcpp::Packet* result = ipReassembly.processPacket(&parsedPacket, status); - - // write fragment/packet to file if: - // - packet is fully reassembled (status of REASSEMBLED) - // - packet isn't a fragment or isn't an IP packet and the user asked to write all packets to output - if (status == pcpp::IPReassembly::REASSEMBLED || - ((status == pcpp::IPReassembly::NON_IP_PACKET || status == pcpp::IPReassembly::NON_FRAGMENT) && copyAllPacketsToOutputFile)) - { - writer->writePacket(*result->getRawPacket()); - stats.totalPacketsWritten++; - } - - // update statistics if packet is fully reassembled (status of REASSEMBLED) and - if (status == pcpp::IPReassembly::REASSEMBLED) - { - if (isIPv4Packet) - stats.ipv4PacketsDefragmented++; - else if (isIPv6Packet) - stats.ipv6PacketsDefragmented++; - - // free packet - delete result; - } - - // update statistics if packet isn't fully reassembled - if (status == pcpp::IPReassembly::FIRST_FRAGMENT || - status == pcpp::IPReassembly::FRAGMENT || - status == pcpp::IPReassembly::OUT_OF_ORDER_FRAGMENT || - status == pcpp::IPReassembly::MALFORMED_FRAGMENT || - status == pcpp::IPReassembly::REASSEMBLED) - { - if (isIPv4Packet) - stats.ipv4FragmentsMatched++; - else if (isIPv6Packet) - stats.ipv6FragmentsMatched++; - } - } - // if packet isn't marked for de-fragmentation but the user asked to write all packets to output file - else if (copyAllPacketsToOutputFile) - { - writer->writePacket(rawPacket); - stats.totalPacketsWritten++; - } - - } +void processPackets(pcpp::IFileReaderDevice* reader, + pcpp::IFileWriterDevice* writer, bool filterByBpf, + const std::string& bpfFilter, bool filterByIpID, + std::map fragIDs, + bool copyAllPacketsToOutputFile, DefragStats& stats) { + pcpp::RawPacket rawPacket; + pcpp::BPFStringFilter filter(bpfFilter); + + // create an instance of IPReassembly + pcpp::IPReassembly ipReassembly; + + pcpp::IPReassembly::ReassemblyStatus status; + + // read all packet from input file + while (reader->getNextPacket(rawPacket)) { + bool defragPacket = true; + + stats.totalPacketsRead++; + + // if user requested to filter by BPF + if (filterByBpf) { + // check if packet matches the BPF filter supplied by the user + if (pcpp::IPcapDevice::matchPacketWithFilter(filter, &rawPacket)) { + stats.ipPacketsMatchBpfFilter++; + } else // if not - set the packet as not marked for de-fragmentation + { + defragPacket = false; + } + } + + bool isIPv4Packet = false; + bool isIPv6Packet = false; + + // check if packet is of type IPv4 or IPv6 + pcpp::Packet parsedPacket(&rawPacket); + if (parsedPacket.isPacketOfType(pcpp::IPv4)) { + stats.ipv4Packets++; + isIPv4Packet = true; + } else if (parsedPacket.isPacketOfType(pcpp::IPv6)) { + stats.ipv6Packets++; + isIPv6Packet = true; + } else // if not - set the packet as not marked for de-fragmentation + { + defragPacket = false; + } + + // if user requested to filter by IP ID + if (filterByIpID) { + // get the IPv4 layer + pcpp::IPv4Layer* ipv4Layer = + parsedPacket.getLayerOfType(); + if (ipv4Layer != nullptr) { + // check if packet ID matches one of the IP IDs requested by the user + if (fragIDs.find((uint32_t)pcpp::netToHost16( + ipv4Layer->getIPv4Header()->ipId)) != fragIDs.end()) { + stats.ipv4PacketsMatchIpIDs++; + } else // if not - set the packet as not marked for de-fragmentation + { + defragPacket = false; + } + } + + // get the IPv6 layer + pcpp::IPv6Layer* ipv6Layer = + parsedPacket.getLayerOfType(); + if (ipv6Layer != nullptr && ipv6Layer->isFragment()) { + // if this packet is a fragment, get the fragmentation header + pcpp::IPv6FragmentationHeader* fragHdr = + ipv6Layer->getExtensionOfType(); + + // check if fragment ID matches one of the fragment IDs requested by the + // user + if (fragIDs.find(pcpp::netToHost32(fragHdr->getFragHeader()->id)) != + fragIDs.end()) { + stats.ipv6PacketsMatchFragIDs++; + } else // if not - set the packet as not marked for de-fragmentation + { + defragPacket = false; + } + } + } + + // if fragment is marked for de-fragmentation + if (defragPacket) { + // process the packet in the IP reassembly mechanism + pcpp::Packet* result = ipReassembly.processPacket(&parsedPacket, status); + + // write fragment/packet to file if: + // - packet is fully reassembled (status of REASSEMBLED) + // - packet isn't a fragment or isn't an IP packet and the user asked to + // write all packets to output + if (status == pcpp::IPReassembly::REASSEMBLED || + ((status == pcpp::IPReassembly::NON_IP_PACKET || + status == pcpp::IPReassembly::NON_FRAGMENT) && + copyAllPacketsToOutputFile)) { + writer->writePacket(*result->getRawPacket()); + stats.totalPacketsWritten++; + } + + // update statistics if packet is fully reassembled (status of + // REASSEMBLED) and + if (status == pcpp::IPReassembly::REASSEMBLED) { + if (isIPv4Packet) + stats.ipv4PacketsDefragmented++; + else if (isIPv6Packet) + stats.ipv6PacketsDefragmented++; + + // free packet + delete result; + } + + // update statistics if packet isn't fully reassembled + if (status == pcpp::IPReassembly::FIRST_FRAGMENT || + status == pcpp::IPReassembly::FRAGMENT || + status == pcpp::IPReassembly::OUT_OF_ORDER_FRAGMENT || + status == pcpp::IPReassembly::MALFORMED_FRAGMENT || + status == pcpp::IPReassembly::REASSEMBLED) { + if (isIPv4Packet) + stats.ipv4FragmentsMatched++; + else if (isIPv6Packet) + stats.ipv6FragmentsMatched++; + } + } + // if packet isn't marked for de-fragmentation but the user asked to write + // all packets to output file + else if (copyAllPacketsToOutputFile) { + writer->writePacket(rawPacket); + stats.totalPacketsWritten++; + } + } } - /** * A method for printing fragmentation process stats */ -void printStats(const DefragStats& stats, bool filterByIpID, bool filterByBpf) -{ - std::ostringstream stream; - stream << "Summary:\n"; - stream << "========\n"; - stream << "Total packets read: " << stats.totalPacketsRead << std::endl; - stream << "IPv4 packets read: " << stats.ipv4Packets << std::endl; - stream << "IPv6 packets read: " << stats.ipv6Packets << std::endl; - if (filterByIpID) - { - stream << "IPv4 packets match fragment ID list: " << stats.ipv4PacketsMatchIpIDs << std::endl; - stream << "IPv6 packets match fragment ID list: " << stats.ipv6PacketsMatchFragIDs << std::endl; - } - if (filterByBpf) - stream << "IP packets match BPF filter: " << stats.ipPacketsMatchBpfFilter << std::endl; - stream << "Total fragments matched: " << (stats.ipv4FragmentsMatched + stats.ipv6FragmentsMatched) << std::endl; - stream << "IPv4 fragments matched: " << stats.ipv4FragmentsMatched << std::endl; - stream << "IPv6 fragments matched: " << stats.ipv6FragmentsMatched << std::endl; - stream << "Total packets reassembled: " << (stats.ipv4PacketsDefragmented + stats.ipv6PacketsDefragmented) << std::endl; - stream << "IPv4 packets reassembled: " << stats.ipv4PacketsDefragmented << std::endl; - stream << "IPv6 packets reassembled: " << stats.ipv6PacketsDefragmented << std::endl; - stream << "Total packets written to output file: " << stats.totalPacketsWritten << std::endl; - - std::cout << stream.str(); +void printStats(const DefragStats& stats, bool filterByIpID, bool filterByBpf) { + std::ostringstream stream; + stream << "Summary:\n"; + stream << "========\n"; + stream << "Total packets read: " + << stats.totalPacketsRead << std::endl; + stream << "IPv4 packets read: " << stats.ipv4Packets + << std::endl; + stream << "IPv6 packets read: " << stats.ipv6Packets + << std::endl; + if (filterByIpID) { + stream << "IPv4 packets match fragment ID list: " + << stats.ipv4PacketsMatchIpIDs << std::endl; + stream << "IPv6 packets match fragment ID list: " + << stats.ipv6PacketsMatchFragIDs << std::endl; + } + if (filterByBpf) + stream << "IP packets match BPF filter: " + << stats.ipPacketsMatchBpfFilter << std::endl; + stream << "Total fragments matched: " + << (stats.ipv4FragmentsMatched + stats.ipv6FragmentsMatched) + << std::endl; + stream << "IPv4 fragments matched: " + << stats.ipv4FragmentsMatched << std::endl; + stream << "IPv6 fragments matched: " + << stats.ipv6FragmentsMatched << std::endl; + stream << "Total packets reassembled: " + << (stats.ipv4PacketsDefragmented + stats.ipv6PacketsDefragmented) + << std::endl; + stream << "IPv4 packets reassembled: " + << stats.ipv4PacketsDefragmented << std::endl; + stream << "IPv6 packets reassembled: " + << stats.ipv6PacketsDefragmented << std::endl; + stream << "Total packets written to output file: " + << stats.totalPacketsWritten << std::endl; + + std::cout << stream.str(); } - /** * main method of the application */ -int main(int argc, char* argv[]) -{ - pcpp::AppName::init(argc, argv); - - int optionIndex = 0; - int opt = 0; - - std::string outputFile = ""; - bool filterByBpfFilter = false; - std::string bpfFilter = ""; - bool filterByFragID = false; - std::map fragIDMap; - bool copyAllPacketsToOutputFile = false; - - - while((opt = getopt_long(argc, argv, "o:d:f:ahv", DefragUtilOptions, &optionIndex)) != -1) - { - switch (opt) - { - case 0: - { - break; - } - case 'o': - { - outputFile = optarg; - break; - } - case 'd': - { - filterByFragID = true; - // read the IP ID / Frag ID list into the map - fragIDMap.clear(); - std::string ipIDsAsString = std::string(optarg); - std::stringstream stream(ipIDsAsString); - std::string ipIDStr; - // break comma-separated string into string list - while(std::getline(stream, ipIDStr, ',')) - { - // convert the IP ID to uint16_t - uint32_t fragID = (uint32_t)atoi(ipIDStr.c_str()); - // add the frag ID into the map if it doesn't already exist - fragIDMap.emplace(fragID, true); - } - - // verify list is not empty - if (fragIDMap.empty()) - { - EXIT_WITH_ERROR("Couldn't parse fragment ID list"); - } - break; - } - case 'f': - { - filterByBpfFilter = true; - bpfFilter = optarg; - pcpp::BPFStringFilter filter(bpfFilter); - if (!filter.verifyFilter()) - EXIT_WITH_ERROR("Illegal BPF filter"); - break; - } - case 'a': - { - copyAllPacketsToOutputFile = true; - break; - } - case 'h': - { - printUsage(); - exit(0); - } - case 'v': - { - printAppVersion(); - break; - } - } - } - - std::string inputFile = ""; - - int expectedParams = 1; - int paramIndex = -1; - - for (int i = optind; i < argc; i++) - { - paramIndex++; - if (paramIndex > expectedParams) - EXIT_WITH_ERROR("Unexpected parameter: " << argv[i]); - - switch (paramIndex) - { - case 0: - { - inputFile = argv[i]; - break; - } - - default: - EXIT_WITH_ERROR("Unexpected parameter: " << argv[i]); - } - } - - if (inputFile == "") - { - EXIT_WITH_ERROR("Input file name was not given"); - } - - if (outputFile == "") - { - EXIT_WITH_ERROR("Output file name was not given"); - } - - // create a reader device from input file - pcpp::IFileReaderDevice* reader = pcpp::IFileReaderDevice::getReader(inputFile); - - if (!reader->open()) - { - EXIT_WITH_ERROR("Error opening input file"); - } - - - // create a writer device for output file in the same file type as input file - pcpp::IFileWriterDevice* writer = nullptr; - - if (dynamic_cast(reader) != nullptr) - { - writer = new pcpp::PcapFileWriterDevice(outputFile, ((pcpp::PcapFileReaderDevice*)reader)->getLinkLayerType()); - } - else if (dynamic_cast(reader) != nullptr) - { - writer = new pcpp::PcapNgFileWriterDevice(outputFile); - } - else - { - EXIT_WITH_ERROR("Cannot determine input file type"); - } - - if (!writer->open()) - { - EXIT_WITH_ERROR("Error opening output file"); - } - - // run the de-fragmentation process - DefragStats stats; - processPackets(reader, writer, filterByBpfFilter, bpfFilter, filterByFragID, fragIDMap, copyAllPacketsToOutputFile, stats); - - // close files - reader->close(); - writer->close(); - - // print summary stats to console - printStats(stats, filterByFragID, filterByBpfFilter); - - delete reader; - delete writer; +int main(int argc, char* argv[]) { + pcpp::AppName::init(argc, argv); + + int optionIndex = 0; + int opt = 0; + + std::string outputFile = ""; + bool filterByBpfFilter = false; + std::string bpfFilter = ""; + bool filterByFragID = false; + std::map fragIDMap; + bool copyAllPacketsToOutputFile = false; + + while ((opt = getopt_long(argc, argv, "o:d:f:ahv", DefragUtilOptions, + &optionIndex)) != -1) { + switch (opt) { + case 0: { + break; + } + case 'o': { + outputFile = optarg; + break; + } + case 'd': { + filterByFragID = true; + // read the IP ID / Frag ID list into the map + fragIDMap.clear(); + std::string ipIDsAsString = std::string(optarg); + std::stringstream stream(ipIDsAsString); + std::string ipIDStr; + // break comma-separated string into string list + while (std::getline(stream, ipIDStr, ',')) { + // convert the IP ID to uint16_t + uint32_t fragID = (uint32_t)atoi(ipIDStr.c_str()); + // add the frag ID into the map if it doesn't already exist + fragIDMap.emplace(fragID, true); + } + + // verify list is not empty + if (fragIDMap.empty()) { + EXIT_WITH_ERROR("Couldn't parse fragment ID list"); + } + break; + } + case 'f': { + filterByBpfFilter = true; + bpfFilter = optarg; + pcpp::BPFStringFilter filter(bpfFilter); + if (!filter.verifyFilter()) + EXIT_WITH_ERROR("Illegal BPF filter"); + break; + } + case 'a': { + copyAllPacketsToOutputFile = true; + break; + } + case 'h': { + printUsage(); + exit(0); + } + case 'v': { + printAppVersion(); + break; + } + } + } + + std::string inputFile = ""; + + int expectedParams = 1; + int paramIndex = -1; + + for (int i = optind; i < argc; i++) { + paramIndex++; + if (paramIndex > expectedParams) + EXIT_WITH_ERROR("Unexpected parameter: " << argv[i]); + + switch (paramIndex) { + case 0: { + inputFile = argv[i]; + break; + } + + default: + EXIT_WITH_ERROR("Unexpected parameter: " << argv[i]); + } + } + + if (inputFile == "") { + EXIT_WITH_ERROR("Input file name was not given"); + } + + if (outputFile == "") { + EXIT_WITH_ERROR("Output file name was not given"); + } + + // create a reader device from input file + pcpp::IFileReaderDevice* reader = + pcpp::IFileReaderDevice::getReader(inputFile); + + if (!reader->open()) { + EXIT_WITH_ERROR("Error opening input file"); + } + + // create a writer device for output file in the same file type as input file + pcpp::IFileWriterDevice* writer = nullptr; + + if (dynamic_cast(reader) != nullptr) { + writer = new pcpp::PcapFileWriterDevice( + outputFile, ((pcpp::PcapFileReaderDevice*)reader)->getLinkLayerType()); + } else if (dynamic_cast(reader) != nullptr) { + writer = new pcpp::PcapNgFileWriterDevice(outputFile); + } else { + EXIT_WITH_ERROR("Cannot determine input file type"); + } + + if (!writer->open()) { + EXIT_WITH_ERROR("Error opening output file"); + } + + // run the de-fragmentation process + DefragStats stats; + processPackets(reader, writer, filterByBpfFilter, bpfFilter, filterByFragID, + fragIDMap, copyAllPacketsToOutputFile, stats); + + // close files + reader->close(); + writer->close(); + + // print summary stats to console + printStats(stats, filterByFragID, filterByBpfFilter); + + delete reader; + delete writer; } diff --git a/Examples/IPFragUtil/main.cpp b/Examples/IPFragUtil/main.cpp index 89fcde8389..1c9a2729ca 100644 --- a/Examples/IPFragUtil/main.cpp +++ b/Examples/IPFragUtil/main.cpp @@ -1,543 +1,525 @@ -#include -#include -#include -#include -#include -#include -#include "PcapPlusPlusVersion.h" -#include "Packet.h" #include "IPv4Layer.h" #include "IPv6Layer.h" +#include "Packet.h" #include "PayloadLayer.h" #include "PcapFileDevice.h" +#include "PcapPlusPlusVersion.h" #include "SystemUtils.h" #include "getopt.h" +#include +#include +#include +#include +#include +#include - -#define EXIT_WITH_ERROR(reason) do { \ - printUsage(); \ - std::cout << std::endl << "ERROR: " << reason << std::endl << std::endl; \ - exit(1); \ - } while(0) - - -static struct option FragUtilOptions[] = -{ - {"output-file", required_argument, nullptr, 'o'}, - {"frag-size", required_argument, nullptr, 's'}, - {"filter-by-ipid", required_argument, nullptr, 'd'}, - {"bpf-filter", required_argument, nullptr, 'f'}, - {"copy-all-packets", no_argument, nullptr, 'a'}, - {"help", no_argument, nullptr, 'h'}, - {"version", no_argument, nullptr, 'v'}, - {nullptr, 0, nullptr, 0} -}; +#define EXIT_WITH_ERROR(reason) \ + do { \ + printUsage(); \ + std::cout << std::endl \ + << "ERROR: " << reason << std::endl \ + << std::endl; \ + exit(1); \ + } while (0) + +static struct option FragUtilOptions[] = { + {"output-file", required_argument, nullptr, 'o'}, + {"frag-size", required_argument, nullptr, 's'}, + {"filter-by-ipid", required_argument, nullptr, 'd'}, + {"bpf-filter", required_argument, nullptr, 'f'}, + {"copy-all-packets", no_argument, nullptr, 'a'}, + {"help", no_argument, nullptr, 'h'}, + {"version", no_argument, nullptr, 'v'}, + {nullptr, 0, nullptr, 0}}; /** * A struct for collecting stats during the fragmentation process */ -struct FragStats -{ - int totalPacketsRead; - int ipv4Packets; - int ipv6Packets; - int ipv4PacketsMatchIpIDs; - int ipPacketsMatchBpfFilter; - int ipPacketsUnderSize; - int ipv4PacketsFragmented; - int ipv6PacketsFragmented; - int totalPacketsWritten; - - void clear() { memset(this, 0, sizeof(FragStats)); } - FragStats() { clear(); } +struct FragStats { + int totalPacketsRead; + int ipv4Packets; + int ipv6Packets; + int ipv4PacketsMatchIpIDs; + int ipPacketsMatchBpfFilter; + int ipPacketsUnderSize; + int ipv4PacketsFragmented; + int ipv6PacketsFragmented; + int totalPacketsWritten; + + void clear() { memset(this, 0, sizeof(FragStats)); } + FragStats() { clear(); } }; - /** * Print application usage */ -void printUsage() -{ - std::cout << std::endl - << "Usage:" << std::endl - << "------" << std::endl - << pcpp::AppName::get() << " input_file -s frag_size -o output_file [-d ip_ids] [-f bpf_filter] [-a] [-h] [-v]" << std::endl - << std::endl - << "Options:" << std::endl - << std::endl - << " input_file : Input pcap/pcapng file" << std::endl - << " -s frag_size : Size of each fragment" << std::endl - << " -o output_file : Output file. Output file type (pcap/pcapng) will match the input file type" << std::endl - << " -d ip_ids : Fragment only packets that match this comma-separated list of IP IDs in decimal format" << std::endl - << " -f bpf_filter : Fragment only packets that match bpf_filter. Filter should be provided in Berkeley Packet Filter (BPF)" << std::endl - << " syntax (http://biot.com/capstats/bpf.html) i.e: 'ip net 1.1.1.1'" << std::endl - << " -a : Copy all packets (those who were fragmented and those who weren't) to output file" << std::endl - << " -v : Displays the current version and exits" << std::endl - << " -h : Displays this help message and exits" << std::endl - << std::endl; +void printUsage() { + std::cout + << std::endl + << "Usage:" << std::endl + << "------" << std::endl + << pcpp::AppName::get() + << " input_file -s frag_size -o output_file [-d ip_ids] [-f bpf_filter] " + "[-a] [-h] [-v]" + << std::endl + << std::endl + << "Options:" << std::endl + << std::endl + << " input_file : Input pcap/pcapng file" << std::endl + << " -s frag_size : Size of each fragment" << std::endl + << " -o output_file : Output file. Output file type (pcap/pcapng) " + "will match the input file type" + << std::endl + << " -d ip_ids : Fragment only packets that match this " + "comma-separated list of IP IDs in decimal format" + << std::endl + << " -f bpf_filter : Fragment only packets that match bpf_filter. " + "Filter should be provided in Berkeley Packet Filter (BPF)" + << std::endl + << " syntax (http://biot.com/capstats/bpf.html) " + "i.e: 'ip net 1.1.1.1'" + << std::endl + << " -a : Copy all packets (those who were fragmented " + "and those who weren't) to output file" + << std::endl + << " -v : Displays the current version and exits" + << std::endl + << " -h : Displays this help message and exits" + << std::endl + << std::endl; } - /** * Print application version */ -void printAppVersion() -{ - std::cout - << pcpp::AppName::get() << " " << pcpp::getPcapPlusPlusVersionFull() << std::endl - << "Built: " << pcpp::getBuildDateTime() << std::endl - << "Built from: " << pcpp::getGitInfo() << std::endl; - exit(0); +void printAppVersion() { + std::cout << pcpp::AppName::get() << " " << pcpp::getPcapPlusPlusVersionFull() + << std::endl + << "Built: " << pcpp::getBuildDateTime() << std::endl + << "Built from: " << pcpp::getGitInfo() << std::endl; + exit(0); } - /** * Set fragment parameters in an IPv4 fragment packet */ -void setIPv4FragmentParams(pcpp::IPv4Layer* fragIpLayer, size_t fragOffset, bool lastFrag) -{ - // calculate the fragment offset field - uint16_t fragOffsetValue = pcpp::hostToNet16((uint16_t)(fragOffset/8)); +void setIPv4FragmentParams(pcpp::IPv4Layer* fragIpLayer, size_t fragOffset, + bool lastFrag) { + // calculate the fragment offset field + uint16_t fragOffsetValue = pcpp::hostToNet16((uint16_t)(fragOffset / 8)); - // set the fragment flags bits to zero - fragOffsetValue &= (uint16_t)0xff1f; + // set the fragment flags bits to zero + fragOffsetValue &= (uint16_t)0xff1f; - // if this is not the last fragment, set a "more fragments" flag - if (!lastFrag) - fragOffsetValue |= (uint16_t)0x20; + // if this is not the last fragment, set a "more fragments" flag + if (!lastFrag) + fragOffsetValue |= (uint16_t)0x20; - // write fragment flags + fragment offset to packet - fragIpLayer->getIPv4Header()->fragmentOffset = fragOffsetValue; + // write fragment flags + fragment offset to packet + fragIpLayer->getIPv4Header()->fragmentOffset = fragOffsetValue; } - /** - * Add IPv6 fragmentation extension to an IPv6 fragment packet and set fragmentation parameters + * Add IPv6 fragmentation extension to an IPv6 fragment packet and set + * fragmentation parameters */ -void setIPv6FragmentParams(pcpp::IPv6Layer* fragIpLayer, size_t fragOffset, bool lastFrag, uint32_t fragId) -{ - pcpp::IPv6FragmentationHeader fragHeader(fragId, fragOffset, lastFrag); - fragIpLayer->addExtension(fragHeader); +void setIPv6FragmentParams(pcpp::IPv6Layer* fragIpLayer, size_t fragOffset, + bool lastFrag, uint32_t fragId) { + pcpp::IPv6FragmentationHeader fragHeader(fragId, fragOffset, lastFrag); + fragIpLayer->addExtension(fragHeader); } - /** - * Generate a 4-byte positive random number. Used for generating IPv6 fragment ID + * Generate a 4-byte positive random number. Used for generating IPv6 fragment + * ID */ -uint32_t generateRandomNumber() -{ - uint32_t result = 0; - for (int i = 4; i > 0; i--) - { - uint8_t randomNum = (uint8_t)rand() % 256; - result += (uint32_t)pow(randomNum, i); - } - - return result; +uint32_t generateRandomNumber() { + uint32_t result = 0; + for (int i = 4; i > 0; i--) { + uint8_t randomNum = (uint8_t)rand() % 256; + result += (uint32_t)pow(randomNum, i); + } + + return result; } /** - * A method that takes a raw packet and a requested fragment size and splits the packet into fragments. - * Fragments are written to a RawPacketVector instance supplied by the user. - * The input packet isn't modified in any way. - * If the packet isn't of type IPv4 or IPv6, nothing happens and the result vector remains empty. - * If the packet payload size is smaller or equal than the request fragment size the packet isn't fragmented, but the packet is copied + * A method that takes a raw packet and a requested fragment size and splits the + * packet into fragments. Fragments are written to a RawPacketVector instance + * supplied by the user. The input packet isn't modified in any way. If the + * packet isn't of type IPv4 or IPv6, nothing happens and the result vector + * remains empty. If the packet payload size is smaller or equal than the + * request fragment size the packet isn't fragmented, but the packet is copied * and pushed into the result vector */ -void splitIPPacketToFragmentsBySize(pcpp::RawPacket* rawPacket, size_t fragmentSize, pcpp::RawPacketVector& resultFragments) -{ - // parse raw packet - pcpp::Packet packet(rawPacket); - - // check if IPv4/6 - pcpp::ProtocolType ipProto = pcpp::UnknownProtocol; - if (packet.isPacketOfType(pcpp::IPv4)) - ipProto = pcpp::IPv4; - else if (packet.isPacketOfType(pcpp::IPv6)) - ipProto = pcpp::IPv6; - else - return; - - pcpp::Layer* ipLayer = nullptr; - if (ipProto == pcpp::IPv4) - ipLayer = packet.getLayerOfType(); - else // ipProto == IPv6 - ipLayer = packet.getLayerOfType(); - - // if packet payload size is less than the requested fragment size, don't fragment and return - if (ipLayer->getLayerPayloadSize() <= fragmentSize) - { - pcpp::RawPacket* copyOfRawPacket = new pcpp::RawPacket(*rawPacket); - resultFragments.pushBack(copyOfRawPacket); - return; - } - - // generate a random number for IPv6 fragment ID (not used in IPv4 packets) - uint32_t randomNum = generateRandomNumber(); - - // go over the payload and create fragments until reaching the end of the payload - size_t curOffset = 0; - while (curOffset < ipLayer->getLayerPayloadSize()) - { - bool lastFrag = false; - size_t curFragSize = fragmentSize; - - // check if this is the last fragment by comparing the size of the rest of the payload to the requested fragment size - if (ipLayer->getLayerPayloadSize() - curOffset <= fragmentSize) - { - curFragSize = ipLayer->getLayerPayloadSize() - curOffset; - lastFrag = true; - } - - // create the fragment packet - // first, duplicate the input packet and create a new parsed packet out of it - pcpp::RawPacket* newFragRawPacket = new pcpp::RawPacket(*packet.getRawPacket()); - pcpp::Packet newFrag(newFragRawPacket); - - // find the IPv4/6 layer of the new fragment - pcpp::Layer* fragIpLayer = nullptr; - if (ipProto == pcpp::IPv4) - fragIpLayer = newFrag.getLayerOfType(); - else // ipProto == IPv6 - fragIpLayer = newFrag.getLayerOfType(); - - // delete all layers above IP layer - newFrag.removeAllLayersAfter(fragIpLayer); - - // create a new PayloadLayer with the fragmented data and add it to the new fragment packet - pcpp::PayloadLayer newPayload(ipLayer->getLayerPayload() + curOffset, curFragSize, false); - newFrag.addLayer(&newPayload); - - // set fragment parameters in IPv4/6 layer - if (ipProto == pcpp::IPv4) - setIPv4FragmentParams((pcpp::IPv4Layer*)fragIpLayer, curOffset, lastFrag); - else // ipProto == IPv6 - setIPv6FragmentParams((pcpp::IPv6Layer*)fragIpLayer, curOffset, lastFrag, randomNum); - - // compute all calculated fields of the new fragment - newFrag.computeCalculateFields(); - - // add fragment to result list - resultFragments.pushBack(newFrag.getRawPacket()); - - // increment offset pointer - curOffset += curFragSize; - } - +void splitIPPacketToFragmentsBySize(pcpp::RawPacket* rawPacket, + size_t fragmentSize, + pcpp::RawPacketVector& resultFragments) { + // parse raw packet + pcpp::Packet packet(rawPacket); + + // check if IPv4/6 + pcpp::ProtocolType ipProto = pcpp::UnknownProtocol; + if (packet.isPacketOfType(pcpp::IPv4)) + ipProto = pcpp::IPv4; + else if (packet.isPacketOfType(pcpp::IPv6)) + ipProto = pcpp::IPv6; + else + return; + + pcpp::Layer* ipLayer = nullptr; + if (ipProto == pcpp::IPv4) + ipLayer = packet.getLayerOfType(); + else // ipProto == IPv6 + ipLayer = packet.getLayerOfType(); + + // if packet payload size is less than the requested fragment size, don't + // fragment and return + if (ipLayer->getLayerPayloadSize() <= fragmentSize) { + pcpp::RawPacket* copyOfRawPacket = new pcpp::RawPacket(*rawPacket); + resultFragments.pushBack(copyOfRawPacket); + return; + } + + // generate a random number for IPv6 fragment ID (not used in IPv4 packets) + uint32_t randomNum = generateRandomNumber(); + + // go over the payload and create fragments until reaching the end of the + // payload + size_t curOffset = 0; + while (curOffset < ipLayer->getLayerPayloadSize()) { + bool lastFrag = false; + size_t curFragSize = fragmentSize; + + // check if this is the last fragment by comparing the size of the rest of + // the payload to the requested fragment size + if (ipLayer->getLayerPayloadSize() - curOffset <= fragmentSize) { + curFragSize = ipLayer->getLayerPayloadSize() - curOffset; + lastFrag = true; + } + + // create the fragment packet + // first, duplicate the input packet and create a new parsed packet out of + // it + pcpp::RawPacket* newFragRawPacket = + new pcpp::RawPacket(*packet.getRawPacket()); + pcpp::Packet newFrag(newFragRawPacket); + + // find the IPv4/6 layer of the new fragment + pcpp::Layer* fragIpLayer = nullptr; + if (ipProto == pcpp::IPv4) + fragIpLayer = newFrag.getLayerOfType(); + else // ipProto == IPv6 + fragIpLayer = newFrag.getLayerOfType(); + + // delete all layers above IP layer + newFrag.removeAllLayersAfter(fragIpLayer); + + // create a new PayloadLayer with the fragmented data and add it to the new + // fragment packet + pcpp::PayloadLayer newPayload(ipLayer->getLayerPayload() + curOffset, + curFragSize, false); + newFrag.addLayer(&newPayload); + + // set fragment parameters in IPv4/6 layer + if (ipProto == pcpp::IPv4) + setIPv4FragmentParams((pcpp::IPv4Layer*)fragIpLayer, curOffset, + lastFrag); + else // ipProto == IPv6 + setIPv6FragmentParams((pcpp::IPv6Layer*)fragIpLayer, curOffset, lastFrag, + randomNum); + + // compute all calculated fields of the new fragment + newFrag.computeCalculateFields(); + + // add fragment to result list + resultFragments.pushBack(newFrag.getRawPacket()); + + // increment offset pointer + curOffset += curFragSize; + } } - /** - * This method reads packets from the input file, decided which packets pass the filters set by the user, fragment packets who pass them, - * and write the result packets to the output file + * This method reads packets from the input file, decided which packets pass the + * filters set by the user, fragment packets who pass them, and write the result + * packets to the output file */ -void processPackets(pcpp::IFileReaderDevice* reader, pcpp::IFileWriterDevice* writer, - int fragSize, - bool filterByBpf, const std::string& bpfFilter, - bool filterByIpID, std::map ipIDs, - bool copyAllPacketsToOutputFile, - FragStats& stats) -{ - stats.clear(); - - pcpp::RawPacket rawPacket; - pcpp::BPFStringFilter filter(bpfFilter); - - // read all packet from input file - while (reader->getNextPacket(rawPacket)) - { - stats.totalPacketsRead++; - - // as default - set the packet as marked for fragmentation - bool fragPacket = true; - - // if user requested to filter by BPF - if (filterByBpf) - { - // check if packet matches the BPF filter supplied by the user - if (pcpp::IPcapDevice::matchPacketWithFilter(filter, &rawPacket)) - { - stats.ipPacketsMatchBpfFilter++; - } - else // if not - set the packet as not marked for fragmentation - { - fragPacket = false; - } - } - - pcpp::ProtocolType ipProto = pcpp::UnknownProtocol; - - // check if packet is of type IPv4 - pcpp::Packet parsedPacket(&rawPacket); - if (parsedPacket.isPacketOfType(pcpp::IPv4)) - { - ipProto = pcpp::IPv4; - stats.ipv4Packets++; - } - else if (parsedPacket.isPacketOfType(pcpp::IPv6)) // check if packet is of type IPv6 - { - ipProto = pcpp::IPv6; - stats.ipv6Packets++; - } - else // if not - set the packet as not marked for fragmentation - { - fragPacket = false; - } - - // if user requested to filter by IP ID (relevant only for IPv4 packets) - if (filterByIpID) - { - // get the IPv4 layer - pcpp::IPv4Layer* ipLayer = parsedPacket.getLayerOfType(); - if (ipLayer != nullptr) - { - // check if packet ID matches one of the IP IDs requested by the user - if (ipIDs.find(pcpp::netToHost16(ipLayer->getIPv4Header()->ipId)) != ipIDs.end()) - { - stats.ipv4PacketsMatchIpIDs++; - } - else // if not - set the packet as not marked for fragmentation - { - fragPacket = false; - } - } - } - - // if packet passed all filters and marked for fragmentation - if (fragPacket) - { - // call the method who splits the packet into fragments - pcpp::RawPacketVector resultFrags; - splitIPPacketToFragmentsBySize(&rawPacket, (size_t)fragSize, resultFrags); - - // if result list contains only 1 packet it means packet wasn't fragmented - update stats accordingly - if (resultFrags.size() == 1) - { - stats.ipPacketsUnderSize++; - } - else if (resultFrags.size() > 1) // packet was fragmented - { - if (ipProto == pcpp::IPv4) - stats.ipv4PacketsFragmented++; - else // ipProto == IPv6 - stats.ipv6PacketsFragmented++; - } - - // write the result fragments if either: (1) packet was indeed fragmented, - // or (2) user requested to write all packet to output file - if (resultFrags.size() > 1 || copyAllPacketsToOutputFile) - { - writer->writePackets(resultFrags); - stats.totalPacketsWritten += resultFrags.size(); - } - } - // even if packet didn't pass the filters but user requested to write all packet to output file, write it - else if (copyAllPacketsToOutputFile) - { - writer->writePacket(rawPacket); - stats.totalPacketsWritten++; - } - } +void processPackets(pcpp::IFileReaderDevice* reader, + pcpp::IFileWriterDevice* writer, int fragSize, + bool filterByBpf, const std::string& bpfFilter, + bool filterByIpID, std::map ipIDs, + bool copyAllPacketsToOutputFile, FragStats& stats) { + stats.clear(); + + pcpp::RawPacket rawPacket; + pcpp::BPFStringFilter filter(bpfFilter); + + // read all packet from input file + while (reader->getNextPacket(rawPacket)) { + stats.totalPacketsRead++; + + // as default - set the packet as marked for fragmentation + bool fragPacket = true; + + // if user requested to filter by BPF + if (filterByBpf) { + // check if packet matches the BPF filter supplied by the user + if (pcpp::IPcapDevice::matchPacketWithFilter(filter, &rawPacket)) { + stats.ipPacketsMatchBpfFilter++; + } else // if not - set the packet as not marked for fragmentation + { + fragPacket = false; + } + } + + pcpp::ProtocolType ipProto = pcpp::UnknownProtocol; + + // check if packet is of type IPv4 + pcpp::Packet parsedPacket(&rawPacket); + if (parsedPacket.isPacketOfType(pcpp::IPv4)) { + ipProto = pcpp::IPv4; + stats.ipv4Packets++; + } else if (parsedPacket.isPacketOfType( + pcpp::IPv6)) // check if packet is of type IPv6 + { + ipProto = pcpp::IPv6; + stats.ipv6Packets++; + } else // if not - set the packet as not marked for fragmentation + { + fragPacket = false; + } + + // if user requested to filter by IP ID (relevant only for IPv4 packets) + if (filterByIpID) { + // get the IPv4 layer + pcpp::IPv4Layer* ipLayer = parsedPacket.getLayerOfType(); + if (ipLayer != nullptr) { + // check if packet ID matches one of the IP IDs requested by the user + if (ipIDs.find(pcpp::netToHost16(ipLayer->getIPv4Header()->ipId)) != + ipIDs.end()) { + stats.ipv4PacketsMatchIpIDs++; + } else // if not - set the packet as not marked for fragmentation + { + fragPacket = false; + } + } + } + + // if packet passed all filters and marked for fragmentation + if (fragPacket) { + // call the method who splits the packet into fragments + pcpp::RawPacketVector resultFrags; + splitIPPacketToFragmentsBySize(&rawPacket, (size_t)fragSize, resultFrags); + + // if result list contains only 1 packet it means packet wasn't fragmented + // - update stats accordingly + if (resultFrags.size() == 1) { + stats.ipPacketsUnderSize++; + } else if (resultFrags.size() > 1) // packet was fragmented + { + if (ipProto == pcpp::IPv4) + stats.ipv4PacketsFragmented++; + else // ipProto == IPv6 + stats.ipv6PacketsFragmented++; + } + + // write the result fragments if either: (1) packet was indeed fragmented, + // or (2) user requested to write all packet to output file + if (resultFrags.size() > 1 || copyAllPacketsToOutputFile) { + writer->writePackets(resultFrags); + stats.totalPacketsWritten += resultFrags.size(); + } + } + // even if packet didn't pass the filters but user requested to write all + // packet to output file, write it + else if (copyAllPacketsToOutputFile) { + writer->writePacket(rawPacket); + stats.totalPacketsWritten++; + } + } } - /** * A method for printing fragmentation process stats */ -void printStats(const FragStats& stats, bool filterByIpID, bool filterByBpf) -{ - std::ostringstream stream; - stream << "Summary:\n"; - stream << "========\n"; - stream << "Total packets read: " << stats.totalPacketsRead << std::endl; - stream << "IPv4 packets read: " << stats.ipv4Packets << std::endl; - stream << "IPv6 packets read: " << stats.ipv6Packets << std::endl; - if (filterByIpID) - stream << "IPv4 packets match IP ID list: " << stats.ipv4PacketsMatchIpIDs << std::endl; - if (filterByBpf) - stream << "IP packets match BPF filter: " << stats.ipPacketsMatchBpfFilter << std::endl; - stream << "IP packets smaller than fragment size: " << stats.ipPacketsUnderSize << std::endl; - stream << "IPv4 packets fragmented: " << stats.ipv4PacketsFragmented << std::endl; - stream << "IPv6 packets fragmented: " << stats.ipv6PacketsFragmented << std::endl; - stream << "Total packets written to output file: " << stats.totalPacketsWritten << std::endl; - - std::cout << stream.str(); +void printStats(const FragStats& stats, bool filterByIpID, bool filterByBpf) { + std::ostringstream stream; + stream << "Summary:\n"; + stream << "========\n"; + stream << "Total packets read: " + << stats.totalPacketsRead << std::endl; + stream << "IPv4 packets read: " << stats.ipv4Packets + << std::endl; + stream << "IPv6 packets read: " << stats.ipv6Packets + << std::endl; + if (filterByIpID) + stream << "IPv4 packets match IP ID list: " + << stats.ipv4PacketsMatchIpIDs << std::endl; + if (filterByBpf) + stream << "IP packets match BPF filter: " + << stats.ipPacketsMatchBpfFilter << std::endl; + stream << "IP packets smaller than fragment size: " + << stats.ipPacketsUnderSize << std::endl; + stream << "IPv4 packets fragmented: " + << stats.ipv4PacketsFragmented << std::endl; + stream << "IPv6 packets fragmented: " + << stats.ipv6PacketsFragmented << std::endl; + stream << "Total packets written to output file: " + << stats.totalPacketsWritten << std::endl; + + std::cout << stream.str(); } - /** * main method of the application */ -int main(int argc, char* argv[]) -{ - pcpp::AppName::init(argc, argv); - - int optionIndex = 0; - int opt = 0; - - std::string outputFile = ""; - int fragSize = -1; - bool filterByBpfFilter = false; - std::string bpfFilter = ""; - bool filterByIpID = false; - std::map ipIDMap; - bool copyAllPacketsToOutputFile = false; - - while((opt = getopt_long(argc, argv, "o:s:d:f:ahv", FragUtilOptions, &optionIndex)) != -1) - { - switch (opt) - { - case 0: - { - break; - } - case 'o': - { - outputFile = optarg; - break; - } - case 's': - { - fragSize = atoi(optarg); - if (fragSize < 1) - EXIT_WITH_ERROR("Fragment size must be a positive integer"); - if (fragSize % 8 != 0) - EXIT_WITH_ERROR("Fragment size must divide by 8"); - break; - } - case 'd': - { - filterByIpID = true; - // read the IP ID list into the map - ipIDMap.clear(); - std::string ipIDsAsString = std::string(optarg); - std::stringstream stream(ipIDsAsString); - std::string ipIDStr; - // break comma-separated string into string list - while(std::getline(stream, ipIDStr, ',')) - { - // convert the IP ID to uint16_t - uint16_t ipID = (uint16_t)atoi(ipIDStr.c_str()); - // add the IP ID into the map if it doesn't already exist - ipIDMap.emplace(ipID, true); - } - - // verify list is not empty - if (ipIDMap.empty()) - { - EXIT_WITH_ERROR("Couldn't parse IP ID list"); - } - break; - } - case 'f': - { - filterByBpfFilter = true; - bpfFilter = optarg; - pcpp::BPFStringFilter filter(bpfFilter); - if (!filter.verifyFilter()) - EXIT_WITH_ERROR("Illegal BPF filter"); - break; - } - case 'a': - { - copyAllPacketsToOutputFile = true; - break; - } - case 'h': - { - printUsage(); - exit(0); - } - case 'v': - { - printAppVersion(); - break; - } - } - } - - std::string inputFile = ""; - - int expectedParams = 1; - int paramIndex = -1; - - // go over user params and look the input file - for (int i = optind; i < argc; i++) - { - paramIndex++; - if (paramIndex > expectedParams) - EXIT_WITH_ERROR("Unexpected parameter: " << argv[i]); - - switch (paramIndex) - { - case 0: - { - inputFile = argv[i]; - break; - } - - default: - EXIT_WITH_ERROR("Unexpected parameter: " << argv[i]); - } - } - - - if (inputFile == "") - { - EXIT_WITH_ERROR("Input file name was not given"); - } - - if (outputFile == "") - { - EXIT_WITH_ERROR("Output file name was not given"); - } - - if (fragSize < 0) - { - EXIT_WITH_ERROR("Need to choose fragment size using the '-s' flag"); - } - - // create a reader device from input file - pcpp::IFileReaderDevice* reader = pcpp::IFileReaderDevice::getReader(inputFile); - - if (!reader->open()) - { - EXIT_WITH_ERROR("Error opening input file"); - } - - - // create a writer device for output file in the same file type as input file - pcpp::IFileWriterDevice* writer = nullptr; - - if (dynamic_cast(reader) != nullptr) - { - writer = new pcpp::PcapFileWriterDevice(outputFile, ((pcpp::PcapFileReaderDevice*)reader)->getLinkLayerType()); - } - else if (dynamic_cast(reader) != nullptr) - { - writer = new pcpp::PcapNgFileWriterDevice(outputFile); - } - else - { - EXIT_WITH_ERROR("Cannot determine input file type"); - } - - if (!writer->open()) - { - EXIT_WITH_ERROR("Error opening output file"); - } - - // run the fragmentation process - FragStats stats; - processPackets(reader, writer, fragSize, filterByBpfFilter, bpfFilter, filterByIpID, ipIDMap, copyAllPacketsToOutputFile, stats); - - // close files - reader->close(); - writer->close(); - - // print summary stats to console - printStats(stats, filterByIpID, filterByBpfFilter); - - delete reader; - delete writer; +int main(int argc, char* argv[]) { + pcpp::AppName::init(argc, argv); + + int optionIndex = 0; + int opt = 0; + + std::string outputFile = ""; + int fragSize = -1; + bool filterByBpfFilter = false; + std::string bpfFilter = ""; + bool filterByIpID = false; + std::map ipIDMap; + bool copyAllPacketsToOutputFile = false; + + while ((opt = getopt_long(argc, argv, "o:s:d:f:ahv", FragUtilOptions, + &optionIndex)) != -1) { + switch (opt) { + case 0: { + break; + } + case 'o': { + outputFile = optarg; + break; + } + case 's': { + fragSize = atoi(optarg); + if (fragSize < 1) + EXIT_WITH_ERROR("Fragment size must be a positive integer"); + if (fragSize % 8 != 0) + EXIT_WITH_ERROR("Fragment size must divide by 8"); + break; + } + case 'd': { + filterByIpID = true; + // read the IP ID list into the map + ipIDMap.clear(); + std::string ipIDsAsString = std::string(optarg); + std::stringstream stream(ipIDsAsString); + std::string ipIDStr; + // break comma-separated string into string list + while (std::getline(stream, ipIDStr, ',')) { + // convert the IP ID to uint16_t + uint16_t ipID = (uint16_t)atoi(ipIDStr.c_str()); + // add the IP ID into the map if it doesn't already exist + ipIDMap.emplace(ipID, true); + } + + // verify list is not empty + if (ipIDMap.empty()) { + EXIT_WITH_ERROR("Couldn't parse IP ID list"); + } + break; + } + case 'f': { + filterByBpfFilter = true; + bpfFilter = optarg; + pcpp::BPFStringFilter filter(bpfFilter); + if (!filter.verifyFilter()) + EXIT_WITH_ERROR("Illegal BPF filter"); + break; + } + case 'a': { + copyAllPacketsToOutputFile = true; + break; + } + case 'h': { + printUsage(); + exit(0); + } + case 'v': { + printAppVersion(); + break; + } + } + } + + std::string inputFile = ""; + + int expectedParams = 1; + int paramIndex = -1; + + // go over user params and look the input file + for (int i = optind; i < argc; i++) { + paramIndex++; + if (paramIndex > expectedParams) + EXIT_WITH_ERROR("Unexpected parameter: " << argv[i]); + + switch (paramIndex) { + case 0: { + inputFile = argv[i]; + break; + } + + default: + EXIT_WITH_ERROR("Unexpected parameter: " << argv[i]); + } + } + + if (inputFile == "") { + EXIT_WITH_ERROR("Input file name was not given"); + } + + if (outputFile == "") { + EXIT_WITH_ERROR("Output file name was not given"); + } + + if (fragSize < 0) { + EXIT_WITH_ERROR("Need to choose fragment size using the '-s' flag"); + } + + // create a reader device from input file + pcpp::IFileReaderDevice* reader = + pcpp::IFileReaderDevice::getReader(inputFile); + + if (!reader->open()) { + EXIT_WITH_ERROR("Error opening input file"); + } + + // create a writer device for output file in the same file type as input file + pcpp::IFileWriterDevice* writer = nullptr; + + if (dynamic_cast(reader) != nullptr) { + writer = new pcpp::PcapFileWriterDevice( + outputFile, ((pcpp::PcapFileReaderDevice*)reader)->getLinkLayerType()); + } else if (dynamic_cast(reader) != nullptr) { + writer = new pcpp::PcapNgFileWriterDevice(outputFile); + } else { + EXIT_WITH_ERROR("Cannot determine input file type"); + } + + if (!writer->open()) { + EXIT_WITH_ERROR("Error opening output file"); + } + + // run the fragmentation process + FragStats stats; + processPackets(reader, writer, fragSize, filterByBpfFilter, bpfFilter, + filterByIpID, ipIDMap, copyAllPacketsToOutputFile, stats); + + // close files + reader->close(); + writer->close(); + + // print summary stats to console + printStats(stats, filterByIpID, filterByBpfFilter); + + delete reader; + delete writer; } diff --git a/Examples/IcmpFileTransfer/Common.cpp b/Examples/IcmpFileTransfer/Common.cpp index 8998862d8f..b244ba1b7f 100644 --- a/Examples/IcmpFileTransfer/Common.cpp +++ b/Examples/IcmpFileTransfer/Common.cpp @@ -1,15 +1,14 @@ #include "Common.h" -#include -#include -#include -#include #include "EthLayer.h" #include "IPv4Layer.h" #include "IcmpLayer.h" #include "PcapLiveDeviceList.h" -#include "SystemUtils.h" #include "PcapPlusPlusVersion.h" - +#include "SystemUtils.h" +#include +#include +#include +#include #if defined(_WIN32) #define SEPARATOR '\\' @@ -19,269 +18,289 @@ #define DEFAULT_BLOCK_SIZE 1400 -static struct option IcmpFTOptions[] = -{ - {"interface", required_argument, nullptr, 'i'}, - {"dest-ip", required_argument, nullptr, 'd'}, - {"send-file", required_argument, nullptr, 's'}, - {"receive-file", no_argument, nullptr, 'r'}, - {"speed", required_argument, nullptr, 'p'}, - {"block-size", required_argument, nullptr, 'b'}, - {"list-interfaces", no_argument, nullptr, 'l'}, - {"help", no_argument, nullptr, 'h'}, - {"version", no_argument, nullptr, 'v'}, - {nullptr, no_argument, nullptr, no_argument} -}; - - -#define EXIT_WITH_ERROR_PRINT_USAGE(reason) do { \ - printUsage(thisSide, otherSide); \ - std::cout << std::endl << "ERROR: " << reason << std::endl << std::endl; \ - exit(1); \ - } while(0) - - -void printUsage(const std::string &thisSide, const std::string &otherSide) -{ - std::string messagesPerSecShort = (thisSide == "pitcher") ? "[-p messages_per_sec] " : ""; - std::string messagesPerSecLong = (thisSide == "pitcher") ? " -p messages_per_sec : Set number of messages to be sent per seconds. Default is max possible speed\n" : ""; - - std::string thisSideInterface = thisSide + "_interface"; - std::string otherSideIP = otherSide + "_ip"; - - std::cout << std::endl - << "Usage:" << std::endl - << "------" << std::endl - << pcpp::AppName::get() << " [-h] [-v] [-l] -i " << thisSideInterface << " -d " << otherSideIP << " -s file_path -r " << messagesPerSecShort << "[-b block_size]" << std::endl - << std::endl - << "Options:" << std::endl - << std::endl - << " -i " << thisSideInterface << " : Use the specified interface. Can be interface name (e.g eth0) or interface IPv4 address" << std::endl - << " -d " << otherSideIP << " : " << otherSide << " IPv4 address" << std::endl - << " -s file_path : Send file mode: send file_path to " << otherSide << std::endl - << " -r : Receive file mode: receive file from " << otherSide << std::endl - << messagesPerSecLong - << " -b block_size : Set the size of data chunk sent in each ICMP message (in bytes). Default is " << DEFAULT_BLOCK_SIZE << " bytes. Relevant only" << std::endl - << " in send file mode (when -s is set)" << std::endl - << " -l : Print the list of interfaces and exit" << std::endl - << " -v : Displays the current version and exists" << std::endl - << " -h : Display this help message and exit" << std::endl - << std::endl; +static struct option IcmpFTOptions[] = { + {"interface", required_argument, nullptr, 'i'}, + {"dest-ip", required_argument, nullptr, 'd'}, + {"send-file", required_argument, nullptr, 's'}, + {"receive-file", no_argument, nullptr, 'r'}, + {"speed", required_argument, nullptr, 'p'}, + {"block-size", required_argument, nullptr, 'b'}, + {"list-interfaces", no_argument, nullptr, 'l'}, + {"help", no_argument, nullptr, 'h'}, + {"version", no_argument, nullptr, 'v'}, + {nullptr, no_argument, nullptr, no_argument}}; + +#define EXIT_WITH_ERROR_PRINT_USAGE(reason) \ + do { \ + printUsage(thisSide, otherSide); \ + std::cout << std::endl \ + << "ERROR: " << reason << std::endl \ + << std::endl; \ + exit(1); \ + } while (0) + +void printUsage(const std::string& thisSide, const std::string& otherSide) { + std::string messagesPerSecShort = + (thisSide == "pitcher") ? "[-p messages_per_sec] " : ""; + std::string messagesPerSecLong = + (thisSide == "pitcher") + ? " -p messages_per_sec : Set number of messages to be sent per " + "seconds. Default is max possible speed\n" + : ""; + + std::string thisSideInterface = thisSide + "_interface"; + std::string otherSideIP = otherSide + "_ip"; + + std::cout + << std::endl + << "Usage:" << std::endl + << "------" << std::endl + << pcpp::AppName::get() << " [-h] [-v] [-l] -i " << thisSideInterface + << " -d " << otherSideIP << " -s file_path -r " << messagesPerSecShort + << "[-b block_size]" << std::endl + << std::endl + << "Options:" << std::endl + << std::endl + << " -i " << thisSideInterface + << " : Use the specified interface. Can be interface name (e.g eth0) or " + "interface IPv4 address" + << std::endl + << " -d " << otherSideIP << " : " << otherSide + << " IPv4 address" << std::endl + << " -s file_path : Send file mode: send file_path to " + << otherSide << std::endl + << " -r : Receive file mode: receive file from " + << otherSide << std::endl + << messagesPerSecLong + << " -b block_size : Set the size of data chunk sent in each " + "ICMP message (in bytes). Default is " + << DEFAULT_BLOCK_SIZE << " bytes. Relevant only" << std::endl + << " in send file mode (when -s is set)" + << std::endl + << " -l : Print the list of interfaces and exit" + << std::endl + << " -v : Displays the current version and exists" + << std::endl + << " -h : Display this help message and exit" + << std::endl + << std::endl; } - /** * Print application version */ -void printAppVersion() -{ - std::cout - << pcpp::AppName::get() << " " << pcpp::getPcapPlusPlusVersionFull() << std::endl - << "Built: " << pcpp::getBuildDateTime() << std::endl - << "Built from: " << pcpp::getGitInfo() << std::endl; - exit(0); +void printAppVersion() { + std::cout << pcpp::AppName::get() << " " << pcpp::getPcapPlusPlusVersionFull() + << std::endl + << "Built: " << pcpp::getBuildDateTime() << std::endl + << "Built from: " << pcpp::getGitInfo() << std::endl; + exit(0); } - /** * Go over all interfaces and output their names */ -void listInterfaces() -{ - const std::vector& devList = pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDevicesList(); - - std::cout << std::endl << "Network interfaces:" << std::endl; - for (std::vector::const_iterator iter = devList.begin(); iter != devList.end(); iter++) - { - std::cout << " -> Name: '" << (*iter)->getName() << "' IP address: " << (*iter)->getIPv4Address().toString() << std::endl; - } - exit(0); +void listInterfaces() { + const std::vector& devList = + pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDevicesList(); + + std::cout << std::endl + << "Network interfaces:" << std::endl; + for (std::vector::const_iterator iter = + devList.begin(); + iter != devList.end(); iter++) { + std::cout << " -> Name: '" << (*iter)->getName() + << "' IP address: " << (*iter)->getIPv4Address().toString() + << std::endl; + } + exit(0); } - void readCommandLineArguments(int argc, char* argv[], - const std::string &thisSide, const std::string &otherSide, - bool& sender, bool& receiver, - pcpp::IPv4Address& myIP, pcpp::IPv4Address& otherSideIP, - std::string& fileNameToSend, - int& packetsPerSec, size_t& blockSize) -{ - std::string interfaceNameOrIP; - std::string otherSideIPAsString; - fileNameToSend.clear(); - packetsPerSec = -1; - bool packetsPerSecSet = false; - receiver = false; - sender = false; - blockSize = DEFAULT_BLOCK_SIZE; - bool blockSizeSet = false; - - int optionIndex = 0; - int opt = 0; - - while((opt = getopt_long(argc, argv, "i:d:s:rp:b:hvl", IcmpFTOptions, &optionIndex)) != -1) - { - switch (opt) - { - case 0: - break; - case 'i': - interfaceNameOrIP = optarg; - break; - case 'd': - otherSideIPAsString = optarg; - break; - case 's': - fileNameToSend = optarg; - sender = true; - break; - case 'r': - receiver = true; - break; - case 'p': - if (thisSide == "catcher") - EXIT_WITH_ERROR_PRINT_USAGE("Unknown option -p"); - packetsPerSec = atoi(optarg); - packetsPerSecSet = true; - break; - case 'b': - blockSize = atoi(optarg); - blockSizeSet = true; - break; - case 'h': - printUsage(thisSide, otherSide); - exit(0); - break; - case 'v': - printAppVersion(); - break; - case 'l': - listInterfaces(); - break; - default: - printUsage(thisSide, otherSide); - exit(-1); - } - } - - // extract my IP address by interface name or IP address string - if (interfaceNameOrIP.empty()) - EXIT_WITH_ERROR_PRINT_USAGE("Please provide " << thisSide << " interface name or IP"); - - pcpp::IPv4Address interfaceIP(interfaceNameOrIP); - if (!interfaceIP.isValid()) - { - pcpp::PcapLiveDevice* dev = pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDeviceByName(interfaceNameOrIP); - if (dev == nullptr) - EXIT_WITH_ERROR_PRINT_USAGE("Cannot find interface by provided name"); - - myIP = dev->getIPv4Address(); - } - else - myIP = interfaceIP; - - // validate pitcher/catcher IP address - if (otherSideIPAsString.empty()) - EXIT_WITH_ERROR_PRINT_USAGE("Please provide " << otherSide << " IP address"); - - pcpp::IPv4Address tempIP = pcpp::IPv4Address(otherSideIPAsString); - if (!tempIP.isValid()) - EXIT_WITH_ERROR_PRINT_USAGE("Invalid " << otherSide << " IP address"); - otherSideIP = tempIP; - - // verify only one of sender and receiver switches are set - if (sender && receiver) - EXIT_WITH_ERROR_PRINT_USAGE("Cannot set both send file mode (-s) and receive file mode (-r) switches"); - - if (!sender && !receiver) - EXIT_WITH_ERROR_PRINT_USAGE("Must set either send file mode (-s) or receive file mode (-r) switches"); - - // cannot set block size if in receiving file mode - if (!sender && blockSizeSet) - EXIT_WITH_ERROR_PRINT_USAGE("Setting block size (-b switch) is relevant for sending files only"); - - // validate block size - if (blockSize < 1 || blockSize > 1464) - EXIT_WITH_ERROR_PRINT_USAGE("Block size must be a positive integer lower or equal to 1464 bytes (which is the maximum size for a standard packet)"); - - // validate packets per sec - if (packetsPerSecSet && packetsPerSec < 1) - EXIT_WITH_ERROR_PRINT_USAGE("message_per_sec must be a positive value greater or equal to 1"); + const std::string& thisSide, + const std::string& otherSide, bool& sender, + bool& receiver, pcpp::IPv4Address& myIP, + pcpp::IPv4Address& otherSideIP, + std::string& fileNameToSend, int& packetsPerSec, + size_t& blockSize) { + std::string interfaceNameOrIP; + std::string otherSideIPAsString; + fileNameToSend.clear(); + packetsPerSec = -1; + bool packetsPerSecSet = false; + receiver = false; + sender = false; + blockSize = DEFAULT_BLOCK_SIZE; + bool blockSizeSet = false; + + int optionIndex = 0; + int opt = 0; + + while ((opt = getopt_long(argc, argv, "i:d:s:rp:b:hvl", IcmpFTOptions, + &optionIndex)) != -1) { + switch (opt) { + case 0: + break; + case 'i': + interfaceNameOrIP = optarg; + break; + case 'd': + otherSideIPAsString = optarg; + break; + case 's': + fileNameToSend = optarg; + sender = true; + break; + case 'r': + receiver = true; + break; + case 'p': + if (thisSide == "catcher") + EXIT_WITH_ERROR_PRINT_USAGE("Unknown option -p"); + packetsPerSec = atoi(optarg); + packetsPerSecSet = true; + break; + case 'b': + blockSize = atoi(optarg); + blockSizeSet = true; + break; + case 'h': + printUsage(thisSide, otherSide); + exit(0); + break; + case 'v': + printAppVersion(); + break; + case 'l': + listInterfaces(); + break; + default: + printUsage(thisSide, otherSide); + exit(-1); + } + } + + // extract my IP address by interface name or IP address string + if (interfaceNameOrIP.empty()) + EXIT_WITH_ERROR_PRINT_USAGE("Please provide " << thisSide + << " interface name or IP"); + + pcpp::IPv4Address interfaceIP(interfaceNameOrIP); + if (!interfaceIP.isValid()) { + pcpp::PcapLiveDevice* dev = + pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDeviceByName( + interfaceNameOrIP); + if (dev == nullptr) + EXIT_WITH_ERROR_PRINT_USAGE("Cannot find interface by provided name"); + + myIP = dev->getIPv4Address(); + } else + myIP = interfaceIP; + + // validate pitcher/catcher IP address + if (otherSideIPAsString.empty()) + EXIT_WITH_ERROR_PRINT_USAGE("Please provide " << otherSide + << " IP address"); + + pcpp::IPv4Address tempIP = pcpp::IPv4Address(otherSideIPAsString); + if (!tempIP.isValid()) + EXIT_WITH_ERROR_PRINT_USAGE("Invalid " << otherSide << " IP address"); + otherSideIP = tempIP; + + // verify only one of sender and receiver switches are set + if (sender && receiver) + EXIT_WITH_ERROR_PRINT_USAGE("Cannot set both send file mode (-s) and " + "receive file mode (-r) switches"); + + if (!sender && !receiver) + EXIT_WITH_ERROR_PRINT_USAGE("Must set either send file mode (-s) or " + "receive file mode (-r) switches"); + + // cannot set block size if in receiving file mode + if (!sender && blockSizeSet) + EXIT_WITH_ERROR_PRINT_USAGE( + "Setting block size (-b switch) is relevant for sending files only"); + + // validate block size + if (blockSize < 1 || blockSize > 1464) + EXIT_WITH_ERROR_PRINT_USAGE( + "Block size must be a positive integer lower or equal to 1464 bytes " + "(which is the maximum size for a standard packet)"); + + // validate packets per sec + if (packetsPerSecSet && packetsPerSec < 1) + EXIT_WITH_ERROR_PRINT_USAGE( + "message_per_sec must be a positive value greater or equal to 1"); } -bool sendIcmpMessage(pcpp::PcapLiveDevice* dev, - pcpp::MacAddress srcMacAddr, pcpp::MacAddress dstMacAddr, - pcpp::IPv4Address srcIPAddr, pcpp::IPv4Address dstIPAddr, - size_t icmpMsgId, - uint64_t msgType, - uint8_t* data, size_t dataLen, - bool sendRequest) -{ - // a static variable that holds an incrementing IP ID - static uint16_t ipID = 0x1234; - - // keep IP ID in the range of 0x1234-0xfff0 - if (ipID == 0xfff0) - ipID = 0x1234; - - // create the different layers - - // Eth first - pcpp::EthLayer ethLayer(srcMacAddr, dstMacAddr, PCPP_ETHERTYPE_IP); - - // then IPv4 (IPv6 is not supported) - pcpp::IPv4Layer ipLayer(srcIPAddr, dstIPAddr); - ipLayer.getIPv4Header()->timeToLive = 128; - // set and increment the IP ID - ipLayer.getIPv4Header()->ipId = pcpp::hostToNet16(ipID++); - - // then ICMP - pcpp::IcmpLayer icmpLayer; - if (sendRequest && icmpLayer.setEchoRequestData(icmpMsgId, 0, msgType, data, dataLen) == nullptr) - EXIT_WITH_ERROR("Cannot set ICMP echo request data"); - else if (!sendRequest && icmpLayer.setEchoReplyData(icmpMsgId, 0, msgType, data, dataLen) == nullptr) - EXIT_WITH_ERROR("Cannot set ICMP echo response data"); - - // create an new packet and add all layers to it - pcpp::Packet packet; - packet.addLayer(ðLayer); - packet.addLayer(&ipLayer); - packet.addLayer(&icmpLayer); - packet.computeCalculateFields(); - - // send the packet through the device - return dev->sendPacket(&packet); +bool sendIcmpMessage(pcpp::PcapLiveDevice* dev, pcpp::MacAddress srcMacAddr, + pcpp::MacAddress dstMacAddr, pcpp::IPv4Address srcIPAddr, + pcpp::IPv4Address dstIPAddr, size_t icmpMsgId, + uint64_t msgType, uint8_t* data, size_t dataLen, + bool sendRequest) { + // a static variable that holds an incrementing IP ID + static uint16_t ipID = 0x1234; + + // keep IP ID in the range of 0x1234-0xfff0 + if (ipID == 0xfff0) + ipID = 0x1234; + + // create the different layers + + // Eth first + pcpp::EthLayer ethLayer(srcMacAddr, dstMacAddr, PCPP_ETHERTYPE_IP); + + // then IPv4 (IPv6 is not supported) + pcpp::IPv4Layer ipLayer(srcIPAddr, dstIPAddr); + ipLayer.getIPv4Header()->timeToLive = 128; + // set and increment the IP ID + ipLayer.getIPv4Header()->ipId = pcpp::hostToNet16(ipID++); + + // then ICMP + pcpp::IcmpLayer icmpLayer; + if (sendRequest && icmpLayer.setEchoRequestData(icmpMsgId, 0, msgType, data, + dataLen) == nullptr) + EXIT_WITH_ERROR("Cannot set ICMP echo request data"); + else if (!sendRequest && icmpLayer.setEchoReplyData(icmpMsgId, 0, msgType, + data, dataLen) == nullptr) + EXIT_WITH_ERROR("Cannot set ICMP echo response data"); + + // create an new packet and add all layers to it + pcpp::Packet packet; + packet.addLayer(ðLayer); + packet.addLayer(&ipLayer); + packet.addLayer(&icmpLayer); + packet.computeCalculateFields(); + + // send the packet through the device + return dev->sendPacket(&packet); } -bool sendIcmpRequest(pcpp::PcapLiveDevice* dev, - pcpp::MacAddress srcMacAddr, const pcpp::MacAddress dstMacAddr, - pcpp::IPv4Address srcIPAddr, const pcpp::IPv4Address dstIPAddr, - size_t icmpMsgId, - uint64_t msgType, - uint8_t* data, size_t dataLen) -{ - return sendIcmpMessage(dev, srcMacAddr, dstMacAddr, srcIPAddr, dstIPAddr, icmpMsgId, msgType, data, dataLen, true); +bool sendIcmpRequest(pcpp::PcapLiveDevice* dev, pcpp::MacAddress srcMacAddr, + const pcpp::MacAddress dstMacAddr, + pcpp::IPv4Address srcIPAddr, + const pcpp::IPv4Address dstIPAddr, size_t icmpMsgId, + uint64_t msgType, uint8_t* data, size_t dataLen) { + return sendIcmpMessage(dev, srcMacAddr, dstMacAddr, srcIPAddr, dstIPAddr, + icmpMsgId, msgType, data, dataLen, true); } -bool sendIcmpResponse(pcpp::PcapLiveDevice* dev, - pcpp::MacAddress srcMacAddr, pcpp::MacAddress dstMacAddr, - pcpp::IPv4Address srcIPAddr, pcpp::IPv4Address dstIPAddr, - size_t icmpMsgId, - uint64_t msgType, - uint8_t* data, size_t dataLen) -{ - return sendIcmpMessage(dev, srcMacAddr, dstMacAddr, srcIPAddr, dstIPAddr, icmpMsgId, msgType, data, dataLen, false); +bool sendIcmpResponse(pcpp::PcapLiveDevice* dev, pcpp::MacAddress srcMacAddr, + pcpp::MacAddress dstMacAddr, pcpp::IPv4Address srcIPAddr, + pcpp::IPv4Address dstIPAddr, size_t icmpMsgId, + uint64_t msgType, uint8_t* data, size_t dataLen) { + return sendIcmpMessage(dev, srcMacAddr, dstMacAddr, srcIPAddr, dstIPAddr, + icmpMsgId, msgType, data, dataLen, false); } -std::string getFileNameFromPath(const std::string& filePath) -{ - // find the last "\\" or "/" (depends on the os) - where path ends and filename starts - size_t i = filePath.rfind(SEPARATOR, filePath.length()); - if (i != std::string::npos) - { - // extract filename from path - return filePath.substr(i + 1, filePath.length() - i); - } - - return filePath; +std::string getFileNameFromPath(const std::string& filePath) { + // find the last "\\" or "/" (depends on the os) - where path ends and + // filename starts + size_t i = filePath.rfind(SEPARATOR, filePath.length()); + if (i != std::string::npos) { + // extract filename from path + return filePath.substr(i + 1, filePath.length() - i); + } + + return filePath; } diff --git a/Examples/IcmpFileTransfer/Common.h b/Examples/IcmpFileTransfer/Common.h index 1cc198d20c..d71db62fea 100644 --- a/Examples/IcmpFileTransfer/Common.h +++ b/Examples/IcmpFileTransfer/Common.h @@ -1,11 +1,10 @@ #ifndef COMMON_H_ #define COMMON_H_ -#include "MacAddress.h" #include "IpAddress.h" +#include "MacAddress.h" #include "PcapLiveDevice.h" - #define ICMP_FT_WAITING_FT_START 0x345a56c8e7f3cd67ULL #define ICMP_FT_START 0xd45ae6c2e7a3cd67ULL #define ICMP_FT_WAITING_DATA 0x6d5f86c817fb5d7eULL @@ -16,17 +15,22 @@ #define ONE_MBYTE 1048576 -#define EXIT_WITH_ERROR(reason) do { \ - std::cout << std::endl << "ERROR: " << reason << std::endl << std::endl; \ - exit(1); \ - } while(0) - -#define EXIT_WITH_ERROR_AND_RUN_COMMAND(reason, command) do { \ - command; \ - std::cout << std::endl << "ERROR: " << reason << std::endl << std::endl; \ - exit(1); \ - } while(0) +#define EXIT_WITH_ERROR(reason) \ + do { \ + std::cout << std::endl \ + << "ERROR: " << reason << std::endl \ + << std::endl; \ + exit(1); \ + } while (0) +#define EXIT_WITH_ERROR_AND_RUN_COMMAND(reason, command) \ + do { \ + command; \ + std::cout << std::endl \ + << "ERROR: " << reason << std::endl \ + << std::endl; \ + exit(1); \ + } while (0) /** * Go over all interfaces and output their names @@ -34,40 +38,41 @@ void listInterfaces(); /** - * Read and parse the command line arguments from the user. If arguments are wrong or parsing fails the method causes the program to exit + * Read and parse the command line arguments from the user. If arguments are + * wrong or parsing fails the method causes the program to exit */ void readCommandLineArguments(int argc, char* argv[], - const std::string &thisSide, const std::string &otherSide, - bool& sender, bool& receiver, - pcpp::IPv4Address& myIP, pcpp::IPv4Address& otherSideIP, - std::string& fileNameToSend, - int& packetPerSec, size_t& blockSize); + const std::string& thisSide, + const std::string& otherSide, bool& sender, + bool& receiver, pcpp::IPv4Address& myIP, + pcpp::IPv4Address& otherSideIP, + std::string& fileNameToSend, int& packetPerSec, + size_t& blockSize); /** - * Send an ICMP request from source to dest with certain ICMP ID, msgType will be written in the timestamp field of the request, and data - * will be written in the data section of the request + * Send an ICMP request from source to dest with certain ICMP ID, msgType will + * be written in the timestamp field of the request, and data will be written in + * the data section of the request */ -bool sendIcmpRequest(pcpp::PcapLiveDevice* dev, - pcpp::MacAddress srcMacAddr, pcpp::MacAddress dstMacAddr, - pcpp::IPv4Address srcIPAddr, pcpp::IPv4Address dstIPAddr, - size_t icmpMsgId, - uint64_t msgType, - uint8_t* data, size_t dataLen); +bool sendIcmpRequest(pcpp::PcapLiveDevice* dev, pcpp::MacAddress srcMacAddr, + pcpp::MacAddress dstMacAddr, pcpp::IPv4Address srcIPAddr, + pcpp::IPv4Address dstIPAddr, size_t icmpMsgId, + uint64_t msgType, uint8_t* data, size_t dataLen); /** - * Send an ICMP reply from source to dest with certain ICMP ID, msgType will be written in the timestamp field of the request, and data - * will be written in the data section of the request + * Send an ICMP reply from source to dest with certain ICMP ID, msgType will be + * written in the timestamp field of the request, and data will be written in + * the data section of the request */ -bool sendIcmpResponse(pcpp::PcapLiveDevice* dev, - pcpp::MacAddress srcMacAddr, pcpp::MacAddress dstMacAddr, - pcpp::IPv4Address srcIPAddr, pcpp::IPv4Address dstIPAddr, - size_t icmpMsgId, - uint64_t msgType, - uint8_t* data, size_t dataLen); +bool sendIcmpResponse(pcpp::PcapLiveDevice* dev, pcpp::MacAddress srcMacAddr, + pcpp::MacAddress dstMacAddr, pcpp::IPv4Address srcIPAddr, + pcpp::IPv4Address dstIPAddr, size_t icmpMsgId, + uint64_t msgType, uint8_t* data, size_t dataLen); /** * An auxiliary method for extracting the file name from file path, - * for example: for the input '/home/myuser/mypcap.pcap' -> return value will be 'mypcap.pcap' + * for example: for the input '/home/myuser/mypcap.pcap' -> return value will be + * 'mypcap.pcap' */ std::string getFileNameFromPath(const std::string& filePath); diff --git a/Examples/IcmpFileTransfer/IcmpFileTransfer-catcher.cpp b/Examples/IcmpFileTransfer/IcmpFileTransfer-catcher.cpp index 673bbfa6cb..e25d53c9c3 100644 --- a/Examples/IcmpFileTransfer/IcmpFileTransfer-catcher.cpp +++ b/Examples/IcmpFileTransfer/IcmpFileTransfer-catcher.cpp @@ -1,544 +1,568 @@ /** * ICMP file transfer utility - catcher * ======================================== - * This utility demonstrates how to transfer files between 2 machines using only ICMP messages. - * This is the catcher part of the utility - * For more information please refer to README.md + * This utility demonstrates how to transfer files between 2 machines using only + * ICMP messages. This is the catcher part of the utility For more information + * please refer to README.md */ -#include -#include -#include +#include "Common.h" #include "EthLayer.h" #include "IPv4Layer.h" #include "IcmpLayer.h" #include "Packet.h" -#include "PcapLiveDeviceList.h" #include "PcapFilter.h" -#include "Common.h" +#include "PcapLiveDeviceList.h" #include "SystemUtils.h" - +#include +#include +#include /** - * A struct used for starting a file transfer, mainly sending or getting the file name + * A struct used for starting a file transfer, mainly sending or getting the + * file name */ -struct IcmpFileTransferStart -{ - pcpp::IPv4Address pitcherIPAddr; - pcpp::IPv4Address catcherIPAddr; - std::string fileName; - uint16_t icmpId; +struct IcmpFileTransferStart { + pcpp::IPv4Address pitcherIPAddr; + pcpp::IPv4Address catcherIPAddr; + std::string fileName; + uint16_t icmpId; }; /** * A strcut used for receiving file data from the pitcher */ -struct IcmpFileContentDataRecv -{ - pcpp::IPv4Address pitcherIPAddr; - pcpp::IPv4Address catcherIPAddr; - std::ofstream* file; - std::string fileName; - uint16_t expectedIcmpId; - uint32_t fileSize; - uint32_t MBReceived; +struct IcmpFileContentDataRecv { + pcpp::IPv4Address pitcherIPAddr; + pcpp::IPv4Address catcherIPAddr; + std::ofstream* file; + std::string fileName; + uint16_t expectedIcmpId; + uint32_t fileSize; + uint32_t MBReceived; }; /** * A strcut used for sending file data to the pitcher */ -struct IcmpFileContentDataSend -{ - pcpp::IPv4Address pitcherIPAddr; - pcpp::IPv4Address catcherIPAddr; - std::ifstream* file; - bool readingFromFile; - uint32_t MBSent; - size_t blockSize; - char* memblock; +struct IcmpFileContentDataSend { + pcpp::IPv4Address pitcherIPAddr; + pcpp::IPv4Address catcherIPAddr; + std::ifstream* file; + bool readingFromFile; + uint32_t MBSent; + size_t blockSize; + char* memblock; }; - /** - * A callback used in the receiveFile() method and responsible to wait for the pitcher to send an ICMP request containing the file name - * to be received + * A callback used in the receiveFile() method and responsible to wait for the + * pitcher to send an ICMP request containing the file name to be received */ -static bool waitForFileTransferStart(pcpp::RawPacket* rawPacket, pcpp::PcapLiveDevice* dev, void* icmpVoidData) -{ - // first, parse the packet - pcpp::Packet parsedPacket(rawPacket); - - // verify it's ICMP and IPv4 (IPv6 and ICMPv6 are not supported) - if (!parsedPacket.isPacketOfType(pcpp::ICMP) || !parsedPacket.isPacketOfType(pcpp::IPv4)) - return false; - - if (icmpVoidData == nullptr) - return false; - - IcmpFileTransferStart* icmpFTStart = (IcmpFileTransferStart*)icmpVoidData; - - // extract the ICMP layer, verify it's an ICMP request - pcpp::IcmpLayer* icmpLayer = parsedPacket.getLayerOfType(); - if (icmpLayer->getEchoRequestData() == nullptr) - return false; - - // verify the source IP is the pitcher's IP and the dest IP is the catcher's IP - pcpp::IPv4Layer* ip4Layer = parsedPacket.getLayerOfType(); - if (ip4Layer->getSrcIPv4Address() != icmpFTStart->pitcherIPAddr || ip4Layer->getDstIPv4Address() != icmpFTStart->catcherIPAddr) - return false; - - // check the ICMP timestamp field which contains the type of message delivered between pitcher and catcher - // in this case the catcher is waiting for a file-transfer start message from the pitcher containing the file name - // which is of type ICMP_FT_START - uint64_t resMsg = icmpLayer->getEchoRequestData()->header->timestamp; - if (resMsg != ICMP_FT_START) - return false; - - // extract the file name from the ICMP request data - icmpFTStart->fileName = std::string((char*)icmpLayer->getEchoRequestData()->data); - - // extract ethernet layer and ICMP ID to be able to respond to the pitcher - pcpp::EthLayer* ethLayer = parsedPacket.getLayerOfType(); - uint16_t icmpId = pcpp::netToHost16(icmpLayer->getEchoRequestData()->header->id); - - // send the pitcher an ICMP response containing an ack message (of type ICMP_FT_ACK) so it knows the catcher has received - // the file name and it's ready to start getting the file data - if (!sendIcmpResponse(dev, - dev->getMacAddress(), ethLayer->getSourceMac(), - icmpFTStart->catcherIPAddr, icmpFTStart->pitcherIPAddr, - icmpId, ICMP_FT_ACK, - nullptr, 0)) - EXIT_WITH_ERROR("Cannot send ACK message to pitcher"); - - // set the current ICMP ID. It's important for the catcher to keep track of the ICMP ID to make sure it doesn't miss any message - icmpFTStart->icmpId = icmpId; - - // file name has arrived, stop receiveFile() from blocking - return true; +static bool waitForFileTransferStart(pcpp::RawPacket* rawPacket, + pcpp::PcapLiveDevice* dev, + void* icmpVoidData) { + // first, parse the packet + pcpp::Packet parsedPacket(rawPacket); + + // verify it's ICMP and IPv4 (IPv6 and ICMPv6 are not supported) + if (!parsedPacket.isPacketOfType(pcpp::ICMP) || + !parsedPacket.isPacketOfType(pcpp::IPv4)) + return false; + + if (icmpVoidData == nullptr) + return false; + + IcmpFileTransferStart* icmpFTStart = (IcmpFileTransferStart*)icmpVoidData; + + // extract the ICMP layer, verify it's an ICMP request + pcpp::IcmpLayer* icmpLayer = parsedPacket.getLayerOfType(); + if (icmpLayer->getEchoRequestData() == nullptr) + return false; + + // verify the source IP is the pitcher's IP and the dest IP is the catcher's + // IP + pcpp::IPv4Layer* ip4Layer = parsedPacket.getLayerOfType(); + if (ip4Layer->getSrcIPv4Address() != icmpFTStart->pitcherIPAddr || + ip4Layer->getDstIPv4Address() != icmpFTStart->catcherIPAddr) + return false; + + // check the ICMP timestamp field which contains the type of message delivered + // between pitcher and catcher in this case the catcher is waiting for a + // file-transfer start message from the pitcher containing the file name which + // is of type ICMP_FT_START + uint64_t resMsg = icmpLayer->getEchoRequestData()->header->timestamp; + if (resMsg != ICMP_FT_START) + return false; + + // extract the file name from the ICMP request data + icmpFTStart->fileName = + std::string((char*)icmpLayer->getEchoRequestData()->data); + + // extract ethernet layer and ICMP ID to be able to respond to the pitcher + pcpp::EthLayer* ethLayer = parsedPacket.getLayerOfType(); + uint16_t icmpId = + pcpp::netToHost16(icmpLayer->getEchoRequestData()->header->id); + + // send the pitcher an ICMP response containing an ack message (of type + // ICMP_FT_ACK) so it knows the catcher has received the file name and it's + // ready to start getting the file data + if (!sendIcmpResponse(dev, dev->getMacAddress(), ethLayer->getSourceMac(), + icmpFTStart->catcherIPAddr, icmpFTStart->pitcherIPAddr, + icmpId, ICMP_FT_ACK, nullptr, 0)) + EXIT_WITH_ERROR("Cannot send ACK message to pitcher"); + + // set the current ICMP ID. It's important for the catcher to keep track of + // the ICMP ID to make sure it doesn't miss any message + icmpFTStart->icmpId = icmpId; + + // file name has arrived, stop receiveFile() from blocking + return true; } - /** - * A callback used in the receiveFile() method and responsible to receive file data chunks arriving from the pitcher and write them to the - * local file + * A callback used in the receiveFile() method and responsible to receive file + * data chunks arriving from the pitcher and write them to the local file */ -static bool getFileContent(pcpp::RawPacket* rawPacket, pcpp::PcapLiveDevice* dev, void* icmpVoidData) -{ - // first, parse the packet - pcpp::Packet parsedPacket(rawPacket); - - // verify it's ICMP and IPv4 (IPv6 and ICMPv6 are not supported) - if (!parsedPacket.isPacketOfType(pcpp::ICMP) || !parsedPacket.isPacketOfType(pcpp::IPv4)) - return false; - - if (icmpVoidData == nullptr) - return false; - - IcmpFileContentDataRecv* icmpData = (IcmpFileContentDataRecv*)icmpVoidData; - - // extract the ICMP layer, verify it's an ICMP request - pcpp::IcmpLayer* icmpLayer = parsedPacket.getLayerOfType(); - if (icmpLayer->getEchoRequestData() == nullptr) - return false; - - // verify the source IP is the pitcher's IP and the dest IP is the catcher's IP - pcpp::IPv4Layer* ip4Layer = parsedPacket.getLayerOfType(); - if (ip4Layer->getSrcIPv4Address() != icmpData->pitcherIPAddr || ip4Layer->getDstIPv4Address() != icmpData->catcherIPAddr) - return false; - - // extract message type from the ICMP request. Message type is written in ICMP request timestamp field - uint64_t resMsg = icmpLayer->getEchoRequestData()->header->timestamp; - - // if message type is ICMP_FT_END it means pitcher finished sending all file chunks - if (resMsg == ICMP_FT_END) - { - // close the file and stop receiveFile() from blocking - icmpData->file->close(); - std::cout << "."; - return true; - } - // if message type is not ICMP_FT_END not ICMP_FT_DATA - do nothing, it's probably an ICMP request not relevant for this file transfer - else if (resMsg != ICMP_FT_DATA) - return false; - - // compare the ICMP ID of the request to the ICMP ID we expect to see. If it's smaller than expected it means catcher already - // saw this message so it can be ignored - if (pcpp::netToHost16(icmpLayer->getEchoRequestData()->header->id) < icmpData->expectedIcmpId) - return false; - - // if ICMP ID is bigger than expected it probably means catcher missed one or more packets. Since a reliability mechanism isn't currently - // implemented in this program, the only thing left to do is to exit the program with an error - if (pcpp::netToHost16(icmpLayer->getEchoRequestData()->header->id) > icmpData->expectedIcmpId) - { - // close the file, remove it and exit the program with error - icmpData->file->close(); - EXIT_WITH_ERROR_AND_RUN_COMMAND("Didn't get expected ICMP message #" << icmpData->expectedIcmpId << ", got #" << pcpp::netToHost16(icmpLayer->getEchoRequestData()->header->id), std::remove(icmpData->fileName.c_str())); - } - - // increment expected ICMP ID - icmpData->expectedIcmpId++; - - // write the data received from the pitcher to the local file - icmpData->file->write((char*)icmpLayer->getEchoRequestData()->data, icmpLayer->getEchoRequestData()->dataLength); - - // add chunk size to the aggregated file size - icmpData->fileSize += icmpLayer->getEchoRequestData()->dataLength; - - // print a dot (".") for every 1MB received - icmpData->MBReceived += icmpLayer->getEchoRequestData()->dataLength; - if (icmpData->MBReceived > ONE_MBYTE) - { - icmpData->MBReceived -= ONE_MBYTE; - std::cout << "."; - } - - // return and wait for the next data packet - return false; +static bool getFileContent(pcpp::RawPacket* rawPacket, + pcpp::PcapLiveDevice* dev, void* icmpVoidData) { + // first, parse the packet + pcpp::Packet parsedPacket(rawPacket); + + // verify it's ICMP and IPv4 (IPv6 and ICMPv6 are not supported) + if (!parsedPacket.isPacketOfType(pcpp::ICMP) || + !parsedPacket.isPacketOfType(pcpp::IPv4)) + return false; + + if (icmpVoidData == nullptr) + return false; + + IcmpFileContentDataRecv* icmpData = (IcmpFileContentDataRecv*)icmpVoidData; + + // extract the ICMP layer, verify it's an ICMP request + pcpp::IcmpLayer* icmpLayer = parsedPacket.getLayerOfType(); + if (icmpLayer->getEchoRequestData() == nullptr) + return false; + + // verify the source IP is the pitcher's IP and the dest IP is the catcher's + // IP + pcpp::IPv4Layer* ip4Layer = parsedPacket.getLayerOfType(); + if (ip4Layer->getSrcIPv4Address() != icmpData->pitcherIPAddr || + ip4Layer->getDstIPv4Address() != icmpData->catcherIPAddr) + return false; + + // extract message type from the ICMP request. Message type is written in ICMP + // request timestamp field + uint64_t resMsg = icmpLayer->getEchoRequestData()->header->timestamp; + + // if message type is ICMP_FT_END it means pitcher finished sending all file + // chunks + if (resMsg == ICMP_FT_END) { + // close the file and stop receiveFile() from blocking + icmpData->file->close(); + std::cout << "."; + return true; + } + // if message type is not ICMP_FT_END not ICMP_FT_DATA - do nothing, it's + // probably an ICMP request not relevant for this file transfer + else if (resMsg != ICMP_FT_DATA) + return false; + + // compare the ICMP ID of the request to the ICMP ID we expect to see. If it's + // smaller than expected it means catcher already saw this message so it can + // be ignored + if (pcpp::netToHost16(icmpLayer->getEchoRequestData()->header->id) < + icmpData->expectedIcmpId) + return false; + + // if ICMP ID is bigger than expected it probably means catcher missed one or + // more packets. Since a reliability mechanism isn't currently implemented in + // this program, the only thing left to do is to exit the program with an + // error + if (pcpp::netToHost16(icmpLayer->getEchoRequestData()->header->id) > + icmpData->expectedIcmpId) { + // close the file, remove it and exit the program with error + icmpData->file->close(); + EXIT_WITH_ERROR_AND_RUN_COMMAND( + "Didn't get expected ICMP message #" + << icmpData->expectedIcmpId << ", got #" + << pcpp::netToHost16(icmpLayer->getEchoRequestData()->header->id), + std::remove(icmpData->fileName.c_str())); + } + + // increment expected ICMP ID + icmpData->expectedIcmpId++; + + // write the data received from the pitcher to the local file + icmpData->file->write((char*)icmpLayer->getEchoRequestData()->data, + icmpLayer->getEchoRequestData()->dataLength); + + // add chunk size to the aggregated file size + icmpData->fileSize += icmpLayer->getEchoRequestData()->dataLength; + + // print a dot (".") for every 1MB received + icmpData->MBReceived += icmpLayer->getEchoRequestData()->dataLength; + if (icmpData->MBReceived > ONE_MBYTE) { + icmpData->MBReceived -= ONE_MBYTE; + std::cout << "."; + } + + // return and wait for the next data packet + return false; } - /** * Receive a file from the pitcher */ -void receiveFile(pcpp::IPv4Address pitcherIP, pcpp::IPv4Address catcherIP) -{ - // identify the interface to listen and send packets to - pcpp::PcapLiveDevice* dev = pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDeviceByIp(catcherIP); - if (dev == nullptr) - EXIT_WITH_ERROR("Cannot find network interface with IP '" << catcherIP << "'"); - - // try to open the interface (device) - if (!dev->open()) - EXIT_WITH_ERROR("Cannot open network interface"); - - // set an ICMP protocol filter so it'll capture only ICMP packets - pcpp::ProtoFilter protocolFilter(pcpp::ICMP); - if (!dev->setFilter(protocolFilter)) - EXIT_WITH_ERROR("Can't set ICMP filter on device"); - - std::cout << "Waiting for pitcher to send a file..." << std::endl; - - IcmpFileTransferStart icmpFTStart = { - pitcherIP, - catcherIP, - "", - 0 - }; - - // wait until the pitcher sends an ICMP request with the file name in its data - int res = dev->startCaptureBlockingMode(waitForFileTransferStart, &icmpFTStart, -1); - if (!res) - EXIT_WITH_ERROR("Cannot start capturing packets"); - - // create a new file with the name provided by the pitcher - std::ofstream file(icmpFTStart.fileName.c_str(), std::ios::out|std::ios::binary); - - if (file.is_open()) - { - std::cout << "Getting file from pitcher: '" << icmpFTStart.fileName << "' "; - - IcmpFileContentDataRecv icmpFileContentData = { - pitcherIP, - catcherIP, - &file, - icmpFTStart.fileName, - (uint16_t)(icmpFTStart.icmpId+1), - 0, - 0 - }; - - // get all file data from the pitcher. This method blocks until all file is received - res = dev->startCaptureBlockingMode(getFileContent, &icmpFileContentData, -1); - if (!res) - { - file.close(); - EXIT_WITH_ERROR_AND_RUN_COMMAND("Cannot start capturing packets", std::remove(icmpFTStart.fileName.c_str())); - } - - std::cout << std::endl << std::endl - << "Finished getting file '" << icmpFTStart.fileName << "' " - << "[received " << icmpFileContentData.fileSize << " bytes]" - << std::endl; - } - else - EXIT_WITH_ERROR("Cannot create file"); - - // remove the filter and close the device (interface) - dev->clearFilter(); - dev->close(); +void receiveFile(pcpp::IPv4Address pitcherIP, pcpp::IPv4Address catcherIP) { + // identify the interface to listen and send packets to + pcpp::PcapLiveDevice* dev = + pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDeviceByIp(catcherIP); + if (dev == nullptr) + EXIT_WITH_ERROR("Cannot find network interface with IP '" << catcherIP + << "'"); + + // try to open the interface (device) + if (!dev->open()) + EXIT_WITH_ERROR("Cannot open network interface"); + + // set an ICMP protocol filter so it'll capture only ICMP packets + pcpp::ProtoFilter protocolFilter(pcpp::ICMP); + if (!dev->setFilter(protocolFilter)) + EXIT_WITH_ERROR("Can't set ICMP filter on device"); + + std::cout << "Waiting for pitcher to send a file..." << std::endl; + + IcmpFileTransferStart icmpFTStart = {pitcherIP, catcherIP, "", 0}; + + // wait until the pitcher sends an ICMP request with the file name in its data + int res = + dev->startCaptureBlockingMode(waitForFileTransferStart, &icmpFTStart, -1); + if (!res) + EXIT_WITH_ERROR("Cannot start capturing packets"); + + // create a new file with the name provided by the pitcher + std::ofstream file(icmpFTStart.fileName.c_str(), + std::ios::out | std::ios::binary); + + if (file.is_open()) { + std::cout << "Getting file from pitcher: '" << icmpFTStart.fileName << "' "; + + IcmpFileContentDataRecv icmpFileContentData = { + pitcherIP, + catcherIP, + &file, + icmpFTStart.fileName, + (uint16_t)(icmpFTStart.icmpId + 1), + 0, + 0}; + + // get all file data from the pitcher. This method blocks until all file is + // received + res = + dev->startCaptureBlockingMode(getFileContent, &icmpFileContentData, -1); + if (!res) { + file.close(); + EXIT_WITH_ERROR_AND_RUN_COMMAND( + "Cannot start capturing packets", + std::remove(icmpFTStart.fileName.c_str())); + } + + std::cout << std::endl + << std::endl + << "Finished getting file '" << icmpFTStart.fileName << "' " + << "[received " << icmpFileContentData.fileSize << " bytes]" + << std::endl; + } else + EXIT_WITH_ERROR("Cannot create file"); + + // remove the filter and close the device (interface) + dev->clearFilter(); + dev->close(); } - /** - * A callback used in the sendFile() method and responsible to wait for the pitcher to send a keep-alive ICMP request. When such message - * arrives this callback takes care of sending an ICMP response to the pitcher which is data contains the file name to send + * A callback used in the sendFile() method and responsible to wait for the + * pitcher to send a keep-alive ICMP request. When such message arrives this + * callback takes care of sending an ICMP response to the pitcher which is data + * contains the file name to send */ -static bool startFileTransfer(pcpp::RawPacket* rawPacket, pcpp::PcapLiveDevice* dev, void* icmpVoidData) -{ - // first, parse the packet - pcpp::Packet parsedPacket(rawPacket); - - // verify it's ICMP and IPv4 (IPv6 and ICMPv6 are not supported) - if (!parsedPacket.isPacketOfType(pcpp::ICMP) || !parsedPacket.isPacketOfType(pcpp::IPv4)) - return false; - - if (icmpVoidData == nullptr) - return false; - - IcmpFileTransferStart* icmpFTStart = (IcmpFileTransferStart*)icmpVoidData; - - // extract the ICMP layer, verify it's an ICMP request - pcpp::IcmpLayer* icmpLayer = parsedPacket.getLayerOfType(); - if (icmpLayer->getEchoRequestData() == nullptr) - return false; - - // verify the source IP is the pitcher's IP and the dest IP is the catcher's IP - pcpp::IPv4Layer* ip4Layer = parsedPacket.getLayerOfType(); - if (ip4Layer->getSrcIPv4Address() != icmpFTStart->pitcherIPAddr || ip4Layer->getDstIPv4Address() != icmpFTStart->catcherIPAddr) - return false; - - // check the ICMP timestamp field which contains the type of message delivered between pitcher and catcher - // in this case the catcher is waiting for a keep-alive message from the pitcher which is of type ICMP_FT_WAITING_FT_START - uint64_t resMsg = icmpLayer->getEchoRequestData()->header->timestamp; - if (resMsg != ICMP_FT_WAITING_FT_START) - return false; - - // extract ethernet layer and ICMP ID to be able to respond to the pitcher - pcpp::EthLayer* ethLayer = parsedPacket.getLayerOfType(); - uint16_t icmpId = pcpp::netToHost16(icmpLayer->getEchoRequestData()->header->id); - - // send the ICMP response containing the file name back to the pitcher - if (!sendIcmpResponse(dev, - dev->getMacAddress(), ethLayer->getSourceMac(), - icmpFTStart->catcherIPAddr, icmpFTStart->pitcherIPAddr, - icmpId, ICMP_FT_START, - (uint8_t*)icmpFTStart->fileName.c_str(), icmpFTStart->fileName.length()+1)) - EXIT_WITH_ERROR("Cannot send file transfer start message to pitcher"); - - return true; +static bool startFileTransfer(pcpp::RawPacket* rawPacket, + pcpp::PcapLiveDevice* dev, void* icmpVoidData) { + // first, parse the packet + pcpp::Packet parsedPacket(rawPacket); + + // verify it's ICMP and IPv4 (IPv6 and ICMPv6 are not supported) + if (!parsedPacket.isPacketOfType(pcpp::ICMP) || + !parsedPacket.isPacketOfType(pcpp::IPv4)) + return false; + + if (icmpVoidData == nullptr) + return false; + + IcmpFileTransferStart* icmpFTStart = (IcmpFileTransferStart*)icmpVoidData; + + // extract the ICMP layer, verify it's an ICMP request + pcpp::IcmpLayer* icmpLayer = parsedPacket.getLayerOfType(); + if (icmpLayer->getEchoRequestData() == nullptr) + return false; + + // verify the source IP is the pitcher's IP and the dest IP is the catcher's + // IP + pcpp::IPv4Layer* ip4Layer = parsedPacket.getLayerOfType(); + if (ip4Layer->getSrcIPv4Address() != icmpFTStart->pitcherIPAddr || + ip4Layer->getDstIPv4Address() != icmpFTStart->catcherIPAddr) + return false; + + // check the ICMP timestamp field which contains the type of message delivered + // between pitcher and catcher in this case the catcher is waiting for a + // keep-alive message from the pitcher which is of type + // ICMP_FT_WAITING_FT_START + uint64_t resMsg = icmpLayer->getEchoRequestData()->header->timestamp; + if (resMsg != ICMP_FT_WAITING_FT_START) + return false; + + // extract ethernet layer and ICMP ID to be able to respond to the pitcher + pcpp::EthLayer* ethLayer = parsedPacket.getLayerOfType(); + uint16_t icmpId = + pcpp::netToHost16(icmpLayer->getEchoRequestData()->header->id); + + // send the ICMP response containing the file name back to the pitcher + if (!sendIcmpResponse(dev, dev->getMacAddress(), ethLayer->getSourceMac(), + icmpFTStart->catcherIPAddr, icmpFTStart->pitcherIPAddr, + icmpId, ICMP_FT_START, + (uint8_t*)icmpFTStart->fileName.c_str(), + icmpFTStart->fileName.length() + 1)) + EXIT_WITH_ERROR("Cannot send file transfer start message to pitcher"); + + return true; } - /** - * A callback used in the sendFile() method and responsible to wait for ICMP requests coming from the pitcher and send file data chunks as - * a reply in the ICMP response data + * A callback used in the sendFile() method and responsible to wait for ICMP + * requests coming from the pitcher and send file data chunks as a reply in the + * ICMP response data */ -static bool sendContent(pcpp::RawPacket* rawPacket, pcpp::PcapLiveDevice* dev, void* icmpVoidData) -{ - // first, parse the packet - pcpp::Packet parsedPacket(rawPacket); - - // verify it's ICMP and IPv4 (IPv6 and ICMPv6 are not supported) - if (!parsedPacket.isPacketOfType(pcpp::ICMP) || !parsedPacket.isPacketOfType(pcpp::IPv4)) - return false; - - if (icmpVoidData == nullptr) - return false; - - IcmpFileContentDataSend* icmpFileContentData = (IcmpFileContentDataSend*)icmpVoidData; - - // extract the ICMP layer, verify it's an ICMP request - pcpp::IcmpLayer* icmpLayer = parsedPacket.getLayerOfType(); - if (icmpLayer->getEchoRequestData() == nullptr) - return false; - - // verify the source IP is the pitcher's IP and the dest IP is the catcher's IP - pcpp::IPv4Layer* ip4Layer = parsedPacket.getLayerOfType(); - if (ip4Layer->getSrcIPv4Address() != icmpFileContentData->pitcherIPAddr || ip4Layer->getDstIPv4Address() != icmpFileContentData->catcherIPAddr) - return false; - - // check the ICMP timestamp field which contains the type of message delivered between pitcher and catcher - uint64_t resMsg = icmpLayer->getEchoRequestData()->header->timestamp; - - // if the pitcher sent an abort message, exit the program - if (resMsg == ICMP_FT_ABORT) - EXIT_WITH_ERROR("Got an abort message from pitcher. Exiting..."); - - // if it's not an abort message, catcher is only waiting for data messages which are of type ICMP_FT_WAITING_DATA - if (resMsg != ICMP_FT_WAITING_DATA) - return false; - - // extract ethernet layer and ICMP ID to be able to respond to the pitcher - pcpp::EthLayer* ethLayer = parsedPacket.getLayerOfType(); - uint16_t icmpId = pcpp::netToHost16(icmpLayer->getEchoRequestData()->header->id); - - // if all file was already sent to the pitcher - if (!icmpFileContentData->readingFromFile) - { - // send an ICMP response with ICMP_FT_END value in the timestamp field, indicating the pitcher all file was sent to it - if (!sendIcmpResponse(dev, - dev->getMacAddress(), ethLayer->getSourceMac(), - icmpFileContentData->catcherIPAddr, icmpFileContentData->pitcherIPAddr, - icmpId, ICMP_FT_END, - nullptr, 0)) - EXIT_WITH_ERROR("Cannot send file transfer end message to pitcher"); - - // then return true so the sendFile() will stop blocking - return true; - } - - // try to read another block of data from the file - if (icmpFileContentData->file->read(icmpFileContentData->memblock, icmpFileContentData->blockSize)) - { - // if managed to read a full block, send it via the ICMP response to the pitcher. The data chunk will be sent in the response data - if (!sendIcmpResponse(dev, - dev->getMacAddress(), ethLayer->getSourceMac(), - icmpFileContentData->catcherIPAddr, icmpFileContentData->pitcherIPAddr, - icmpId, ICMP_FT_DATA, - (uint8_t*)icmpFileContentData->memblock, icmpFileContentData->blockSize)) - EXIT_WITH_ERROR("Cannot send file transfer data message to pitcher"); - - // print a dot ('.') on every 1MB sent - icmpFileContentData->MBSent += icmpFileContentData->blockSize; - if (icmpFileContentData->MBSent > ONE_MBYTE) - { - icmpFileContentData->MBSent -= ONE_MBYTE; - std::cout << "."; - } - } - // if read only partial block, it means it's the last block - else if (icmpFileContentData->file->gcount() > 0) - { - // send the remaining bytes of data to the pitcher via the ICMP response. The data chunk will be sent in the response data - if (!sendIcmpResponse(dev, - dev->getMacAddress(), ethLayer->getSourceMac(), - icmpFileContentData->catcherIPAddr, icmpFileContentData->pitcherIPAddr, - icmpId, ICMP_FT_DATA, - (uint8_t*)icmpFileContentData->memblock, icmpFileContentData->file->gcount())) - EXIT_WITH_ERROR("Cannot send file transfer last data message to pitcher"); - - // set an indication that all file was delivered to the pitcher - icmpFileContentData->readingFromFile = false; - - std::cout << "."; - } - // if couldn't read anything, it means the previous block was the last block of the file - else - { - // nothing to send to the pitcher - - // set an indication that all file was delivered to the pitcher - icmpFileContentData->readingFromFile = false; - } - - return false; +static bool sendContent(pcpp::RawPacket* rawPacket, pcpp::PcapLiveDevice* dev, + void* icmpVoidData) { + // first, parse the packet + pcpp::Packet parsedPacket(rawPacket); + + // verify it's ICMP and IPv4 (IPv6 and ICMPv6 are not supported) + if (!parsedPacket.isPacketOfType(pcpp::ICMP) || + !parsedPacket.isPacketOfType(pcpp::IPv4)) + return false; + + if (icmpVoidData == nullptr) + return false; + + IcmpFileContentDataSend* icmpFileContentData = + (IcmpFileContentDataSend*)icmpVoidData; + + // extract the ICMP layer, verify it's an ICMP request + pcpp::IcmpLayer* icmpLayer = parsedPacket.getLayerOfType(); + if (icmpLayer->getEchoRequestData() == nullptr) + return false; + + // verify the source IP is the pitcher's IP and the dest IP is the catcher's + // IP + pcpp::IPv4Layer* ip4Layer = parsedPacket.getLayerOfType(); + if (ip4Layer->getSrcIPv4Address() != icmpFileContentData->pitcherIPAddr || + ip4Layer->getDstIPv4Address() != icmpFileContentData->catcherIPAddr) + return false; + + // check the ICMP timestamp field which contains the type of message delivered + // between pitcher and catcher + uint64_t resMsg = icmpLayer->getEchoRequestData()->header->timestamp; + + // if the pitcher sent an abort message, exit the program + if (resMsg == ICMP_FT_ABORT) + EXIT_WITH_ERROR("Got an abort message from pitcher. Exiting..."); + + // if it's not an abort message, catcher is only waiting for data messages + // which are of type ICMP_FT_WAITING_DATA + if (resMsg != ICMP_FT_WAITING_DATA) + return false; + + // extract ethernet layer and ICMP ID to be able to respond to the pitcher + pcpp::EthLayer* ethLayer = parsedPacket.getLayerOfType(); + uint16_t icmpId = + pcpp::netToHost16(icmpLayer->getEchoRequestData()->header->id); + + // if all file was already sent to the pitcher + if (!icmpFileContentData->readingFromFile) { + // send an ICMP response with ICMP_FT_END value in the timestamp field, + // indicating the pitcher all file was sent to it + if (!sendIcmpResponse(dev, dev->getMacAddress(), ethLayer->getSourceMac(), + icmpFileContentData->catcherIPAddr, + icmpFileContentData->pitcherIPAddr, icmpId, + ICMP_FT_END, nullptr, 0)) + EXIT_WITH_ERROR("Cannot send file transfer end message to pitcher"); + + // then return true so the sendFile() will stop blocking + return true; + } + + // try to read another block of data from the file + if (icmpFileContentData->file->read(icmpFileContentData->memblock, + icmpFileContentData->blockSize)) { + // if managed to read a full block, send it via the ICMP response to the + // pitcher. The data chunk will be sent in the response data + if (!sendIcmpResponse(dev, dev->getMacAddress(), ethLayer->getSourceMac(), + icmpFileContentData->catcherIPAddr, + icmpFileContentData->pitcherIPAddr, icmpId, + ICMP_FT_DATA, + (uint8_t*)icmpFileContentData->memblock, + icmpFileContentData->blockSize)) + EXIT_WITH_ERROR("Cannot send file transfer data message to pitcher"); + + // print a dot ('.') on every 1MB sent + icmpFileContentData->MBSent += icmpFileContentData->blockSize; + if (icmpFileContentData->MBSent > ONE_MBYTE) { + icmpFileContentData->MBSent -= ONE_MBYTE; + std::cout << "."; + } + } + // if read only partial block, it means it's the last block + else if (icmpFileContentData->file->gcount() > 0) { + // send the remaining bytes of data to the pitcher via the ICMP response. + // The data chunk will be sent in the response data + if (!sendIcmpResponse(dev, dev->getMacAddress(), ethLayer->getSourceMac(), + icmpFileContentData->catcherIPAddr, + icmpFileContentData->pitcherIPAddr, icmpId, + ICMP_FT_DATA, + (uint8_t*)icmpFileContentData->memblock, + icmpFileContentData->file->gcount())) + EXIT_WITH_ERROR("Cannot send file transfer last data message to pitcher"); + + // set an indication that all file was delivered to the pitcher + icmpFileContentData->readingFromFile = false; + + std::cout << "."; + } + // if couldn't read anything, it means the previous block was the last block + // of the file + else { + // nothing to send to the pitcher + + // set an indication that all file was delivered to the pitcher + icmpFileContentData->readingFromFile = false; + } + + return false; } - /** * Send a file to the pitcher */ -void sendFile(const std::string &filePath, pcpp::IPv4Address pitcherIP, pcpp::IPv4Address catcherIP, size_t blockSize) -{ - // identify the interface to listen and send packets to - pcpp::PcapLiveDevice* dev = pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDeviceByIp(catcherIP); - if (dev == nullptr) - EXIT_WITH_ERROR("Cannot find network interface with IP '" << catcherIP << "'"); - - // try to open the interface (device) - if (!dev->open()) - EXIT_WITH_ERROR("Cannot open network interface"); - - // set an ICMP protocol filter so it'll capture only ICMP packets - pcpp::ProtoFilter protocolFilter(pcpp::ICMP); - if (!dev->setFilter(protocolFilter)) - EXIT_WITH_ERROR("Can't set ICMP filter on device"); - - // try the open the file for reading - std::ifstream file(filePath.c_str(), std::ios::in|std::ios::binary); - - if (file.is_open()) - { - // extract file size - file.seekg(0, std::ios_base::end); - uint32_t fileSize = file.tellg(); - - // go back to the beginning of the file - file.seekg(0, std::ios::beg); - - // remove the path and keep just the file name. This is the name that will be delivered to the pitcher - std::string fileName = getFileNameFromPath(filePath); - - std::cout << "Waiting for pitcher to send a keep-alive signal..." << std::endl; - - IcmpFileTransferStart icmpFTStart = { - pitcherIP, - catcherIP, - fileName, - 0 - }; - - // first, establish a connection with the pitcher and send it the file name. This method waits for the pitcher to send an ICMP - // request which indicates it's alive. The response to the request will contain the file name in the ICMP response data - int res = dev->startCaptureBlockingMode(startFileTransfer, &icmpFTStart, -1); - // if an error occurred - if (!res) - EXIT_WITH_ERROR("Cannot start capturing packets"); - - std::cout << "Sending file '" << fileName << "' "; - - - IcmpFileContentDataSend icmpFileContentData = { - pitcherIP, - catcherIP, - &file, - true, - 0, - blockSize, - nullptr - }; - - // create the memory block that will contain the file data chunks that will be transferred to the pitcher - icmpFileContentData.memblock = new char[blockSize]; - - // wait for ICMP requests coming from the pitcher and send file data chunks as a reply in the ICMP response data - // this method returns when all file was transferred to the pitcher - res = dev->startCaptureBlockingMode(sendContent, &icmpFileContentData, -1); - - // free the memory block data and close the file - delete [] icmpFileContentData.memblock; - file.close(); - - // if capture failed, exit the program - if (!res) - EXIT_WITH_ERROR("Cannot start capturing packets"); - - std::cout << std::endl << std::endl - << "Finished sending '" << fileName << "' " - << "[sent " << fileSize << " bytes]" - << std::endl; - } - else // if file couldn't be opened - EXIT_WITH_ERROR("Cannot open file '" << filePath << "'"); - - // close the device - dev->close(); +void sendFile(const std::string& filePath, pcpp::IPv4Address pitcherIP, + pcpp::IPv4Address catcherIP, size_t blockSize) { + // identify the interface to listen and send packets to + pcpp::PcapLiveDevice* dev = + pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDeviceByIp(catcherIP); + if (dev == nullptr) + EXIT_WITH_ERROR("Cannot find network interface with IP '" << catcherIP + << "'"); + + // try to open the interface (device) + if (!dev->open()) + EXIT_WITH_ERROR("Cannot open network interface"); + + // set an ICMP protocol filter so it'll capture only ICMP packets + pcpp::ProtoFilter protocolFilter(pcpp::ICMP); + if (!dev->setFilter(protocolFilter)) + EXIT_WITH_ERROR("Can't set ICMP filter on device"); + + // try the open the file for reading + std::ifstream file(filePath.c_str(), std::ios::in | std::ios::binary); + + if (file.is_open()) { + // extract file size + file.seekg(0, std::ios_base::end); + uint32_t fileSize = file.tellg(); + + // go back to the beginning of the file + file.seekg(0, std::ios::beg); + + // remove the path and keep just the file name. This is the name that will + // be delivered to the pitcher + std::string fileName = getFileNameFromPath(filePath); + + std::cout << "Waiting for pitcher to send a keep-alive signal..." + << std::endl; + + IcmpFileTransferStart icmpFTStart = {pitcherIP, catcherIP, fileName, 0}; + + // first, establish a connection with the pitcher and send it the file name. + // This method waits for the pitcher to send an ICMP request which indicates + // it's alive. The response to the request will contain the file name in the + // ICMP response data + int res = + dev->startCaptureBlockingMode(startFileTransfer, &icmpFTStart, -1); + // if an error occurred + if (!res) + EXIT_WITH_ERROR("Cannot start capturing packets"); + + std::cout << "Sending file '" << fileName << "' "; + + IcmpFileContentDataSend icmpFileContentData = { + pitcherIP, catcherIP, &file, true, 0, blockSize, nullptr}; + + // create the memory block that will contain the file data chunks that will + // be transferred to the pitcher + icmpFileContentData.memblock = new char[blockSize]; + + // wait for ICMP requests coming from the pitcher and send file data chunks + // as a reply in the ICMP response data this method returns when all file + // was transferred to the pitcher + res = dev->startCaptureBlockingMode(sendContent, &icmpFileContentData, -1); + + // free the memory block data and close the file + delete[] icmpFileContentData.memblock; + file.close(); + + // if capture failed, exit the program + if (!res) + EXIT_WITH_ERROR("Cannot start capturing packets"); + + std::cout << std::endl + << std::endl + << "Finished sending '" << fileName << "' " + << "[sent " << fileSize << " bytes]" << std::endl; + } else // if file couldn't be opened + EXIT_WITH_ERROR("Cannot open file '" << filePath << "'"); + + // close the device + dev->close(); } /** * main method of this ICMP catcher */ -int main(int argc, char* argv[]) -{ - pcpp::AppName::init(argc, argv); - - bool sender, receiver; - pcpp::IPv4Address pitcherIP; - pcpp::IPv4Address catcherIP; - std::string fileNameToSend; - int packetsPerSec = 0; - size_t blockSize = 0; - - // disable stdout buffering so all std::cout command will be printed immediately - setbuf(stdout, nullptr); - - // read and parse command line arguments. This method also takes care of arguments correctness. If they're not correct, it'll exit the program - readCommandLineArguments(argc, argv, "catcher", "pitcher", sender, receiver, catcherIP, pitcherIP, fileNameToSend, packetsPerSec, blockSize); - - // send a file to the pitcher - if (sender) - sendFile(fileNameToSend, pitcherIP, catcherIP, blockSize); - // receive a file from the pitcher - else if (receiver) - receiveFile(pitcherIP, catcherIP); +int main(int argc, char* argv[]) { + pcpp::AppName::init(argc, argv); + + bool sender, receiver; + pcpp::IPv4Address pitcherIP; + pcpp::IPv4Address catcherIP; + std::string fileNameToSend; + int packetsPerSec = 0; + size_t blockSize = 0; + + // disable stdout buffering so all std::cout command will be printed + // immediately + setbuf(stdout, nullptr); + + // read and parse command line arguments. This method also takes care of + // arguments correctness. If they're not correct, it'll exit the program + readCommandLineArguments(argc, argv, "catcher", "pitcher", sender, receiver, + catcherIP, pitcherIP, fileNameToSend, packetsPerSec, + blockSize); + + // send a file to the pitcher + if (sender) + sendFile(fileNameToSend, pitcherIP, catcherIP, blockSize); + // receive a file from the pitcher + else if (receiver) + receiveFile(pitcherIP, catcherIP); } diff --git a/Examples/IcmpFileTransfer/IcmpFileTransfer-pitcher.cpp b/Examples/IcmpFileTransfer/IcmpFileTransfer-pitcher.cpp index 82d1b5f73a..f0080d07a2 100644 --- a/Examples/IcmpFileTransfer/IcmpFileTransfer-pitcher.cpp +++ b/Examples/IcmpFileTransfer/IcmpFileTransfer-pitcher.cpp @@ -1,580 +1,614 @@ /** * ICMP file transfer utility - pitcher * ======================================== - * This utility demonstrates how to transfer files between 2 machines using only ICMP messages. - * This is the pitcher part of the utility - * For more information please refer to README.md + * This utility demonstrates how to transfer files between 2 machines using only + * ICMP messages. This is the pitcher part of the utility For more information + * please refer to README.md */ -#include -#include #include +#include +#include #ifndef _MSC_VER #include "unistd.h" #endif +#include "Common.h" #include "EthLayer.h" #include "IPv4Layer.h" #include "IcmpLayer.h" +#include "NetworkUtils.h" #include "Packet.h" #include "PcapLiveDeviceList.h" -#include "NetworkUtils.h" -#include "Common.h" #include "SystemUtils.h" - #define SEND_TIMEOUT_BEFORE_FT_START 3 -#define SLEEP_BETWEEN_ABORT_MESSAGES 100000 // 100 msec +#define SLEEP_BETWEEN_ABORT_MESSAGES 100000 // 100 msec #define NUM_OF_ABORT_MESSAGES_TO_SEND 5 #ifdef _MSC_VER #include -void usleep(__int64 usec) -{ - HANDLE timer; - LARGE_INTEGER ft; +void usleep(__int64 usec) { + HANDLE timer; + LARGE_INTEGER ft; - ft.QuadPart = -(10 * usec); // Convert to 100 nanosecond interval, negative value indicates relative time + ft.QuadPart = -(10 * usec); // Convert to 100 nanosecond interval, negative + // value indicates relative time - timer = CreateWaitableTimer(NULL, TRUE, NULL); - SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0); - WaitForSingleObject(timer, INFINITE); - CloseHandle(timer); + timer = CreateWaitableTimer(NULL, TRUE, NULL); + SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0); + WaitForSingleObject(timer, INFINITE); + CloseHandle(timer); } #endif /** * A struct used for start sending a file to the catcher */ -struct IcmpFileTransferStartSend -{ - uint16_t icmpMsgId; - pcpp::IPv4Address pitcherIPAddr; - pcpp::IPv4Address catcherIPAddr; +struct IcmpFileTransferStartSend { + uint16_t icmpMsgId; + pcpp::IPv4Address pitcherIPAddr; + pcpp::IPv4Address catcherIPAddr; }; /** * A struct used for start receiving a file from the catcher */ -struct IcmpFileTransferStartRecv -{ - pcpp::IPv4Address pitcherIPAddr; - pcpp::IPv4Address catcherIPAddr; - bool gotFileTransferStartMsg; - std::string fileName; +struct IcmpFileTransferStartRecv { + pcpp::IPv4Address pitcherIPAddr; + pcpp::IPv4Address catcherIPAddr; + bool gotFileTransferStartMsg; + std::string fileName; }; /** * A struct used for receiving file content from the catcher */ -struct IcmpFileContentData -{ - pcpp::IPv4Address pitcherIPAddr; - pcpp::IPv4Address catcherIPAddr; - std::ofstream* file; - uint16_t expectedIcmpId; - uint32_t fileSize; - uint32_t MBReceived; - bool fileTransferCompleted; - bool fileTransferError; +struct IcmpFileContentData { + pcpp::IPv4Address pitcherIPAddr; + pcpp::IPv4Address catcherIPAddr; + std::ofstream* file; + uint16_t expectedIcmpId; + uint32_t fileSize; + uint32_t MBReceived; + bool fileTransferCompleted; + bool fileTransferError; }; - /** - * A callback used in the receiveFile() method and responsible to wait for the catcher to send an ICMP response containing the file name - * to be received + * A callback used in the receiveFile() method and responsible to wait for the + * catcher to send an ICMP response containing the file name to be received */ -static void waitForFileTransferStart(pcpp::RawPacket* rawPacket, pcpp::PcapLiveDevice* dev, void* icmpVoidData) -{ - // first, parse the packet - pcpp::Packet parsedPacket(rawPacket); - - // verify it's ICMP and IPv4 (IPv6 and ICMPv6 are not supported) - if (!parsedPacket.isPacketOfType(pcpp::ICMP) || !parsedPacket.isPacketOfType(pcpp::IPv4)) - return; - - if (icmpVoidData == nullptr) - return; - - IcmpFileTransferStartRecv* icmpFTStart = (IcmpFileTransferStartRecv*)icmpVoidData; - - // extract the ICMP layer, verify it's an ICMP reply - pcpp::IcmpLayer* icmpLayer = parsedPacket.getLayerOfType(); - if (icmpLayer->getEchoReplyData() == nullptr) - return; - - // verify the source IP is the catcher's IP and the dest IP is the pitcher's IP - pcpp::IPv4Layer* ip4Layer = parsedPacket.getLayerOfType(); - if (ip4Layer->getSrcIPv4Address() != icmpFTStart->catcherIPAddr || ip4Layer->getDstIPv4Address() != icmpFTStart->pitcherIPAddr) - return; - - // extract the message type in the ICMP reply timestamp field and check if it's ICMP_FT_START - uint64_t resMsg = icmpLayer->getEchoReplyData()->header->timestamp; - if (resMsg != ICMP_FT_START) - return; - - // verify there is data in the ICMP reply - if (icmpLayer->getEchoReplyData()->data == nullptr) - return; - - // extract the file name from the ICMP reply data - icmpFTStart->fileName = std::string((char*)icmpLayer->getEchoReplyData()->data); - - // signal the receiveFile() file name was extracted and it can stop capturing packets - icmpFTStart->gotFileTransferStartMsg = true; +static void waitForFileTransferStart(pcpp::RawPacket* rawPacket, + pcpp::PcapLiveDevice* dev, + void* icmpVoidData) { + // first, parse the packet + pcpp::Packet parsedPacket(rawPacket); + + // verify it's ICMP and IPv4 (IPv6 and ICMPv6 are not supported) + if (!parsedPacket.isPacketOfType(pcpp::ICMP) || + !parsedPacket.isPacketOfType(pcpp::IPv4)) + return; + + if (icmpVoidData == nullptr) + return; + + IcmpFileTransferStartRecv* icmpFTStart = + (IcmpFileTransferStartRecv*)icmpVoidData; + + // extract the ICMP layer, verify it's an ICMP reply + pcpp::IcmpLayer* icmpLayer = parsedPacket.getLayerOfType(); + if (icmpLayer->getEchoReplyData() == nullptr) + return; + + // verify the source IP is the catcher's IP and the dest IP is the pitcher's + // IP + pcpp::IPv4Layer* ip4Layer = parsedPacket.getLayerOfType(); + if (ip4Layer->getSrcIPv4Address() != icmpFTStart->catcherIPAddr || + ip4Layer->getDstIPv4Address() != icmpFTStart->pitcherIPAddr) + return; + + // extract the message type in the ICMP reply timestamp field and check if + // it's ICMP_FT_START + uint64_t resMsg = icmpLayer->getEchoReplyData()->header->timestamp; + if (resMsg != ICMP_FT_START) + return; + + // verify there is data in the ICMP reply + if (icmpLayer->getEchoReplyData()->data == nullptr) + return; + + // extract the file name from the ICMP reply data + icmpFTStart->fileName = + std::string((char*)icmpLayer->getEchoReplyData()->data); + + // signal the receiveFile() file name was extracted and it can stop capturing + // packets + icmpFTStart->gotFileTransferStartMsg = true; } - /** - * A callback used in the receiveFile() method and responsible to receive file data chunks arriving from the catcher and write them to the - * local file + * A callback used in the receiveFile() method and responsible to receive file + * data chunks arriving from the catcher and write them to the local file */ -static void getFileContent(pcpp::RawPacket* rawPacket, pcpp::PcapLiveDevice* dev, void* icmpVoidData) -{ - // first, parse the packet - pcpp::Packet parsedPacket(rawPacket); - - // verify it's ICMP and IPv4 (IPv6 and ICMPv6 are not supported) - if (!parsedPacket.isPacketOfType(pcpp::ICMP) || !parsedPacket.isPacketOfType(pcpp::IPv4)) - return; - - if (icmpVoidData == nullptr) - return; - - IcmpFileContentData* icmpFileContentData = (IcmpFileContentData*)icmpVoidData; - - // extract the ICMP layer, verify it's an ICMP reply - pcpp::IcmpLayer* icmpLayer = parsedPacket.getLayerOfType(); - if (icmpLayer->getEchoReplyData() == nullptr) - return; - - // verify the source IP is the catcher's IP and the dest IP is the pitcher's IP - pcpp::IPv4Layer* ip4Layer = parsedPacket.getLayerOfType(); - if (ip4Layer->getSrcIPv4Address() != icmpFileContentData->catcherIPAddr || ip4Layer->getDstIPv4Address() != icmpFileContentData->pitcherIPAddr) - return; - - // extract the message type from the ICMP reply timestamp field - uint64_t resMsg = icmpLayer->getEchoReplyData()->header->timestamp; - - // if message type is ICMP_FT_END it means all file was sent by the catcher. In that case set the icmpFileContentData->fileTransferCompleted to true - // the receiveFile() method checks that flag periodically and will stop capture packets - if (resMsg == ICMP_FT_END) - { - icmpFileContentData->fileTransferCompleted = true; - std::cout << "."; - return; - } - - // if message type isn't ICMP_FT_END and ICMP_FT_DATA, ignore it - if (resMsg != ICMP_FT_DATA) - return; - - // if got to here it means it's an ICMP_FT_DATA message - - // verify we're not missing any message by checking the ICMP ID of the reply and compare it to the expected ICMP ID. If one or more - // message were missed, set fileTransferError flag so the main thread could abort the catcher and exit the program - if (pcpp::netToHost16(icmpLayer->getEchoReplyData()->header->id) != icmpFileContentData->expectedIcmpId) - { - icmpFileContentData->fileTransferError = true; - std::cout << std::endl << std::endl - << "Didn't get expected ICMP message #" << icmpFileContentData->expectedIcmpId - << ", got #" << pcpp::netToHost16(icmpLayer->getEchoReplyData()->header->id) - << std::endl; - return; - } - - // verify the ICMP reply has data - if (icmpLayer->getEchoReplyData()->data == nullptr) - return; - - // increment the expected ICMP ID - icmpFileContentData->expectedIcmpId++; - - // write the file data chunk in the ICMP reply data to the output file - icmpFileContentData->file->write((char*)icmpLayer->getEchoReplyData()->data, icmpLayer->getEchoReplyData()->dataLength); - - // count the bytes received - icmpFileContentData->fileSize += icmpLayer->getEchoReplyData()->dataLength; - - // print a dot (".") for every 1MB received - icmpFileContentData->MBReceived += icmpLayer->getEchoReplyData()->dataLength; - if (icmpFileContentData->MBReceived > ONE_MBYTE) - { - icmpFileContentData->MBReceived -= ONE_MBYTE; - std::cout << "."; - } +static void getFileContent(pcpp::RawPacket* rawPacket, + pcpp::PcapLiveDevice* dev, void* icmpVoidData) { + // first, parse the packet + pcpp::Packet parsedPacket(rawPacket); + + // verify it's ICMP and IPv4 (IPv6 and ICMPv6 are not supported) + if (!parsedPacket.isPacketOfType(pcpp::ICMP) || + !parsedPacket.isPacketOfType(pcpp::IPv4)) + return; + + if (icmpVoidData == nullptr) + return; + + IcmpFileContentData* icmpFileContentData = + (IcmpFileContentData*)icmpVoidData; + + // extract the ICMP layer, verify it's an ICMP reply + pcpp::IcmpLayer* icmpLayer = parsedPacket.getLayerOfType(); + if (icmpLayer->getEchoReplyData() == nullptr) + return; + + // verify the source IP is the catcher's IP and the dest IP is the pitcher's + // IP + pcpp::IPv4Layer* ip4Layer = parsedPacket.getLayerOfType(); + if (ip4Layer->getSrcIPv4Address() != icmpFileContentData->catcherIPAddr || + ip4Layer->getDstIPv4Address() != icmpFileContentData->pitcherIPAddr) + return; + + // extract the message type from the ICMP reply timestamp field + uint64_t resMsg = icmpLayer->getEchoReplyData()->header->timestamp; + + // if message type is ICMP_FT_END it means all file was sent by the catcher. + // In that case set the icmpFileContentData->fileTransferCompleted to true the + // receiveFile() method checks that flag periodically and will stop capture + // packets + if (resMsg == ICMP_FT_END) { + icmpFileContentData->fileTransferCompleted = true; + std::cout << "."; + return; + } + + // if message type isn't ICMP_FT_END and ICMP_FT_DATA, ignore it + if (resMsg != ICMP_FT_DATA) + return; + + // if got to here it means it's an ICMP_FT_DATA message + + // verify we're not missing any message by checking the ICMP ID of the reply + // and compare it to the expected ICMP ID. If one or more message were missed, + // set fileTransferError flag so the main thread could abort the catcher and + // exit the program + if (pcpp::netToHost16(icmpLayer->getEchoReplyData()->header->id) != + icmpFileContentData->expectedIcmpId) { + icmpFileContentData->fileTransferError = true; + std::cout << std::endl + << std::endl + << "Didn't get expected ICMP message #" + << icmpFileContentData->expectedIcmpId << ", got #" + << pcpp::netToHost16(icmpLayer->getEchoReplyData()->header->id) + << std::endl; + return; + } + + // verify the ICMP reply has data + if (icmpLayer->getEchoReplyData()->data == nullptr) + return; + + // increment the expected ICMP ID + icmpFileContentData->expectedIcmpId++; + + // write the file data chunk in the ICMP reply data to the output file + icmpFileContentData->file->write((char*)icmpLayer->getEchoReplyData()->data, + icmpLayer->getEchoReplyData()->dataLength); + + // count the bytes received + icmpFileContentData->fileSize += icmpLayer->getEchoReplyData()->dataLength; + + // print a dot (".") for every 1MB received + icmpFileContentData->MBReceived += icmpLayer->getEchoReplyData()->dataLength; + if (icmpFileContentData->MBReceived > ONE_MBYTE) { + icmpFileContentData->MBReceived -= ONE_MBYTE; + std::cout << "."; + } } - /** * Receive a file from the catcher */ -void receiveFile(pcpp::IPv4Address pitcherIP, pcpp::IPv4Address catcherIP, int packetPerSec) -{ - // identify the interface to listen and send packets to - pcpp::PcapLiveDevice* dev = pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDeviceByIp(pitcherIP); - if (dev == nullptr) - EXIT_WITH_ERROR("Cannot find network interface with IP '" << pitcherIP << "'"); - - // try to open the interface (device) - if (!dev->open()) - EXIT_WITH_ERROR("Cannot open network interface "); - - // get the MAC address of the interface - pcpp::MacAddress pitcherMacAddr = dev->getMacAddress(); - if (pitcherMacAddr == pcpp::MacAddress::Zero) - EXIT_WITH_ERROR("Cannot find pitcher MAC address"); - - // discover the MAC address of the catcher by sending an ARP ping to it - double arpResTO = 0; - pcpp::MacAddress catcherMacAddr = pcpp::NetworkUtils::getInstance().getMacAddress(catcherIP, dev, arpResTO, pitcherMacAddr, pitcherIP, 10); - if (catcherMacAddr == pcpp::MacAddress::Zero) - EXIT_WITH_ERROR("Cannot find catcher MAC address"); - - uint16_t icmpId = 1; - - IcmpFileTransferStartRecv icmpFTStart = { - pitcherIP, - catcherIP, - false, - "" - }; - - std::cout << "Waiting for catcher to start sending a file..." << std::endl; - - // set an ICMP protocol filter so it'll capture only ICMP packets - pcpp::ProtoFilter protocolFilter(pcpp::ICMP); - if (!dev->setFilter(protocolFilter)) - EXIT_WITH_ERROR("Can't set ICMP filter on device"); - - // since it's the pitcher's job to send ICMP requests and the catcher's job to get them and send ICMP replies, - // sending a file from the catcher to the pitcher is a bit more complicated - // so for start the pitcher needs the file name. It sends an ICMP request with ICMP_FT_WAITING_FT_START message in the timestamp field - // and awaits for catcher response that should include the file name - - // start capturing ICMP packets. The waitForFileTransferStart callback should look for the catcher reply and set icmpFTStart.gotFileTransferStartMsg - // to true - if (!dev->startCapture(waitForFileTransferStart, &icmpFTStart)) - EXIT_WITH_ERROR("Cannot start capturing packets"); - - // while didn't receive response from the catcher, keep sending the ICMP_FT_WAITING_FT_START message - while (!icmpFTStart.gotFileTransferStartMsg) - { - sendIcmpRequest(dev, pitcherMacAddr, catcherMacAddr, pitcherIP, catcherIP, icmpId, ICMP_FT_WAITING_FT_START, nullptr, 0); - icmpId++; - // sleep for a few seconds between sending the message - pcpp::multiPlatformSleep(SEND_TIMEOUT_BEFORE_FT_START); - } - - // stop capturing packets - dev->stopCapture(); - - - // create a new file with the name provided by the catcher - std::ofstream file(icmpFTStart.fileName.c_str(), std::ios::out|std::ios::binary); - - if (file.is_open()) - { - std::cout << "Getting file from catcher: '" << icmpFTStart.fileName << "' "; - - IcmpFileContentData icmpFileContentData = { - pitcherIP, - catcherIP, - &file, - icmpId, - 0, - 0, - false, - false - }; - - // the next thing to do is start getting the file data. For doing that the pitcher sends the catcher ICMP requests with message type - // ICMP_FT_WAITING_DATA in the timestamp field. The catcher should send an ICMP response for each such request with data chunk of the - // file - - // calculate how many microseconds (usec) the pitcher needs to sleep between sending the ICMP_FT_WAITING_DATA message - // (calculated from user defined packetPerSec parameter). - // The calculation is done in usec as in most cases the pitcher needs to sleep less than 1 second between chunks. However if packetPerSec - // equals to 1 it means sleeping for 1 second and in this case we can't use usleep (as it's not working for 1 sec or more) and we use - // sleep instead - uint32_t sleepBetweenPackets = 0; - if (packetPerSec > 1) - sleepBetweenPackets = (uint32_t)(1000000UL / packetPerSec); - - // start capturing ICMP packets. The getFileContent callback should look for the catcher replies containing data chunks of the file - // and write them to the opened file. When catcher signals the end of the file transfer, the callback will set the - // icmpFileContentData.fileTransferCompleted flag to true - if (!dev->startCapture(getFileContent, &icmpFileContentData)) - { - file.close(); - EXIT_WITH_ERROR_AND_RUN_COMMAND("Cannot start capturing packets", std::remove(icmpFTStart.fileName.c_str())); - } - - // keep sending ICMP requests with ICMP_FT_WAITING_DATA message in the timestamp field until all file was received or until an error occurred - while (!icmpFileContentData.fileTransferCompleted && !icmpFileContentData.fileTransferError) - { - sendIcmpRequest(dev, pitcherMacAddr, catcherMacAddr, pitcherIP, catcherIP, icmpId, ICMP_FT_WAITING_DATA, nullptr, 0); - - // if rate limit was set by the user, sleep between sending packets - if (packetPerSec > 1) - usleep(sleepBetweenPackets); - else if (packetPerSec == 1) - pcpp::multiPlatformSleep(1); - - icmpId++; - } - - // stop capturing packets - dev->stopCapture(); - - // if an error occurred (for example: pitcher missed some of the file content packets), send several abort message to the catcher - // so it'll stop waiting for packets, and exit the program - if (icmpFileContentData.fileTransferError) - { - for (int i = 0; i < NUM_OF_ABORT_MESSAGES_TO_SEND; i++) - { - sendIcmpRequest(dev, pitcherMacAddr, catcherMacAddr, pitcherIP, catcherIP, icmpId, ICMP_FT_ABORT, nullptr, 0); - usleep(SLEEP_BETWEEN_ABORT_MESSAGES); - } - - file.close(); - EXIT_WITH_ERROR_AND_RUN_COMMAND("Sent abort message to catcher. Exiting...", std::remove(icmpFTStart.fileName.c_str())); - } - - // file transfer was completed successfully - std::cout << std::endl << std::endl - << "Finished getting file '" << icmpFTStart.fileName << "' " - << "[received " << icmpFileContentData.fileSize << " bytes]" - << std::endl; - } - else - EXIT_WITH_ERROR("Cannot create file"); - - // close the device - dev->close(); +void receiveFile(pcpp::IPv4Address pitcherIP, pcpp::IPv4Address catcherIP, + int packetPerSec) { + // identify the interface to listen and send packets to + pcpp::PcapLiveDevice* dev = + pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDeviceByIp(pitcherIP); + if (dev == nullptr) + EXIT_WITH_ERROR("Cannot find network interface with IP '" << pitcherIP + << "'"); + + // try to open the interface (device) + if (!dev->open()) + EXIT_WITH_ERROR("Cannot open network interface "); + + // get the MAC address of the interface + pcpp::MacAddress pitcherMacAddr = dev->getMacAddress(); + if (pitcherMacAddr == pcpp::MacAddress::Zero) + EXIT_WITH_ERROR("Cannot find pitcher MAC address"); + + // discover the MAC address of the catcher by sending an ARP ping to it + double arpResTO = 0; + pcpp::MacAddress catcherMacAddr = + pcpp::NetworkUtils::getInstance().getMacAddress( + catcherIP, dev, arpResTO, pitcherMacAddr, pitcherIP, 10); + if (catcherMacAddr == pcpp::MacAddress::Zero) + EXIT_WITH_ERROR("Cannot find catcher MAC address"); + + uint16_t icmpId = 1; + + IcmpFileTransferStartRecv icmpFTStart = {pitcherIP, catcherIP, false, ""}; + + std::cout << "Waiting for catcher to start sending a file..." << std::endl; + + // set an ICMP protocol filter so it'll capture only ICMP packets + pcpp::ProtoFilter protocolFilter(pcpp::ICMP); + if (!dev->setFilter(protocolFilter)) + EXIT_WITH_ERROR("Can't set ICMP filter on device"); + + // since it's the pitcher's job to send ICMP requests and the catcher's job to + // get them and send ICMP replies, sending a file from the catcher to the + // pitcher is a bit more complicated so for start the pitcher needs the file + // name. It sends an ICMP request with ICMP_FT_WAITING_FT_START message in the + // timestamp field and awaits for catcher response that should include the + // file name + + // start capturing ICMP packets. The waitForFileTransferStart callback should + // look for the catcher reply and set icmpFTStart.gotFileTransferStartMsg to + // true + if (!dev->startCapture(waitForFileTransferStart, &icmpFTStart)) + EXIT_WITH_ERROR("Cannot start capturing packets"); + + // while didn't receive response from the catcher, keep sending the + // ICMP_FT_WAITING_FT_START message + while (!icmpFTStart.gotFileTransferStartMsg) { + sendIcmpRequest(dev, pitcherMacAddr, catcherMacAddr, pitcherIP, catcherIP, + icmpId, ICMP_FT_WAITING_FT_START, nullptr, 0); + icmpId++; + // sleep for a few seconds between sending the message + pcpp::multiPlatformSleep(SEND_TIMEOUT_BEFORE_FT_START); + } + + // stop capturing packets + dev->stopCapture(); + + // create a new file with the name provided by the catcher + std::ofstream file(icmpFTStart.fileName.c_str(), + std::ios::out | std::ios::binary); + + if (file.is_open()) { + std::cout << "Getting file from catcher: '" << icmpFTStart.fileName << "' "; + + IcmpFileContentData icmpFileContentData = { + pitcherIP, catcherIP, &file, icmpId, 0, 0, false, false}; + + // the next thing to do is start getting the file data. For doing that the + // pitcher sends the catcher ICMP requests with message type + // ICMP_FT_WAITING_DATA in the timestamp field. The catcher should send an + // ICMP response for each such request with data chunk of the file + + // calculate how many microseconds (usec) the pitcher needs to sleep between + // sending the ICMP_FT_WAITING_DATA message (calculated from user defined + // packetPerSec parameter). The calculation is done in usec as in most cases + // the pitcher needs to sleep less than 1 second between chunks. However if + // packetPerSec equals to 1 it means sleeping for 1 second and in this case + // we can't use usleep (as it's not working for 1 sec or more) and we use + // sleep instead + uint32_t sleepBetweenPackets = 0; + if (packetPerSec > 1) + sleepBetweenPackets = (uint32_t)(1000000UL / packetPerSec); + + // start capturing ICMP packets. The getFileContent callback should look for + // the catcher replies containing data chunks of the file and write them to + // the opened file. When catcher signals the end of the file transfer, the + // callback will set the icmpFileContentData.fileTransferCompleted flag to + // true + if (!dev->startCapture(getFileContent, &icmpFileContentData)) { + file.close(); + EXIT_WITH_ERROR_AND_RUN_COMMAND( + "Cannot start capturing packets", + std::remove(icmpFTStart.fileName.c_str())); + } + + // keep sending ICMP requests with ICMP_FT_WAITING_DATA message in the + // timestamp field until all file was received or until an error occurred + while (!icmpFileContentData.fileTransferCompleted && + !icmpFileContentData.fileTransferError) { + sendIcmpRequest(dev, pitcherMacAddr, catcherMacAddr, pitcherIP, catcherIP, + icmpId, ICMP_FT_WAITING_DATA, nullptr, 0); + + // if rate limit was set by the user, sleep between sending packets + if (packetPerSec > 1) + usleep(sleepBetweenPackets); + else if (packetPerSec == 1) + pcpp::multiPlatformSleep(1); + + icmpId++; + } + + // stop capturing packets + dev->stopCapture(); + + // if an error occurred (for example: pitcher missed some of the file + // content packets), send several abort message to the catcher so it'll stop + // waiting for packets, and exit the program + if (icmpFileContentData.fileTransferError) { + for (int i = 0; i < NUM_OF_ABORT_MESSAGES_TO_SEND; i++) { + sendIcmpRequest(dev, pitcherMacAddr, catcherMacAddr, pitcherIP, + catcherIP, icmpId, ICMP_FT_ABORT, nullptr, 0); + usleep(SLEEP_BETWEEN_ABORT_MESSAGES); + } + + file.close(); + EXIT_WITH_ERROR_AND_RUN_COMMAND( + "Sent abort message to catcher. Exiting...", + std::remove(icmpFTStart.fileName.c_str())); + } + + // file transfer was completed successfully + std::cout << std::endl + << std::endl + << "Finished getting file '" << icmpFTStart.fileName << "' " + << "[received " << icmpFileContentData.fileSize << " bytes]" + << std::endl; + } else + EXIT_WITH_ERROR("Cannot create file"); + + // close the device + dev->close(); } - /** - * A callback used in the sendFile() method and responsible to wait for ICMP responses coming from the catcher indicating it's alive - * and ready for file transfer to start + * A callback used in the sendFile() method and responsible to wait for ICMP + * responses coming from the catcher indicating it's alive and ready for file + * transfer to start */ -static bool waitForFileTransferStartAck(pcpp::RawPacket* rawPacket, pcpp::PcapLiveDevice* dev, void* icmpVoidData) -{ - // first, parse the packet - pcpp::Packet parsedPacket(rawPacket); - - // verify it's ICMP and IPv4 (IPv6 and ICMPv6 are not supported) - if (!parsedPacket.isPacketOfType(pcpp::ICMP) || !parsedPacket.isPacketOfType(pcpp::IPv4)) - return false; - - if (icmpVoidData == nullptr) - return false; - - IcmpFileTransferStartSend* icmpData = (IcmpFileTransferStartSend*)icmpVoidData; - - // extract the ICMP layer, verify it's an ICMP reply - pcpp::IcmpLayer* icmpLayer = parsedPacket.getLayerOfType(); - if (icmpLayer->getEchoReplyData() == nullptr) - return false; - - // verify the ICMP ID of the reply matched the ICMP ID the pitcher sent in the request - if (icmpLayer->getEchoReplyData()->header->id != pcpp::hostToNet16(icmpData->icmpMsgId)) - return false; - - // verify the source IP is the catcher's IP and the dest IP is the pitcher's IP - pcpp::IPv4Layer* ip4Layer = parsedPacket.getLayerOfType(); - if (ip4Layer->getSrcIPv4Address() != icmpData->catcherIPAddr || ip4Layer->getDstIPv4Address() != icmpData->pitcherIPAddr) - return false; - - // verify the message type is ICMP_FT_ACK - uint64_t resMsg = icmpLayer->getEchoReplyData()->header->timestamp; - if (resMsg != ICMP_FT_ACK) - return false; - - // if arrived to here it means we got a response from the catcher and it's ready for file transfer to start - return true; +static bool waitForFileTransferStartAck(pcpp::RawPacket* rawPacket, + pcpp::PcapLiveDevice* dev, + void* icmpVoidData) { + // first, parse the packet + pcpp::Packet parsedPacket(rawPacket); + + // verify it's ICMP and IPv4 (IPv6 and ICMPv6 are not supported) + if (!parsedPacket.isPacketOfType(pcpp::ICMP) || + !parsedPacket.isPacketOfType(pcpp::IPv4)) + return false; + + if (icmpVoidData == nullptr) + return false; + + IcmpFileTransferStartSend* icmpData = + (IcmpFileTransferStartSend*)icmpVoidData; + + // extract the ICMP layer, verify it's an ICMP reply + pcpp::IcmpLayer* icmpLayer = parsedPacket.getLayerOfType(); + if (icmpLayer->getEchoReplyData() == nullptr) + return false; + + // verify the ICMP ID of the reply matched the ICMP ID the pitcher sent in the + // request + if (icmpLayer->getEchoReplyData()->header->id != + pcpp::hostToNet16(icmpData->icmpMsgId)) + return false; + + // verify the source IP is the catcher's IP and the dest IP is the pitcher's + // IP + pcpp::IPv4Layer* ip4Layer = parsedPacket.getLayerOfType(); + if (ip4Layer->getSrcIPv4Address() != icmpData->catcherIPAddr || + ip4Layer->getDstIPv4Address() != icmpData->pitcherIPAddr) + return false; + + // verify the message type is ICMP_FT_ACK + uint64_t resMsg = icmpLayer->getEchoReplyData()->header->timestamp; + if (resMsg != ICMP_FT_ACK) + return false; + + // if arrived to here it means we got a response from the catcher and it's + // ready for file transfer to start + return true; } - /** * Send a file to the catcher */ -void sendFile(const std::string &filePath, pcpp::IPv4Address pitcherIP, pcpp::IPv4Address catcherIP, size_t blockSize, int packetPerSec) -{ - // identify the interface to listen and send packets to - pcpp::PcapLiveDevice* dev = pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDeviceByIp(pitcherIP); - if (dev == nullptr) - EXIT_WITH_ERROR("Cannot find network interface with IP '" << pitcherIP << "'"); - - // try to open the interface (device) - if (!dev->open()) - EXIT_WITH_ERROR("Cannot open network interface "); - - // get the MAC address of the interface - pcpp::MacAddress pitcherMacAddr = dev->getMacAddress(); - if (pitcherMacAddr == pcpp::MacAddress::Zero) - EXIT_WITH_ERROR("Cannot find pitcher MAC address"); - - // discover the MAC address of the catcher by sending an ARP ping to it - double arpResTO = 0; - pcpp::MacAddress catcherMacAddr = pcpp::NetworkUtils::getInstance().getMacAddress(catcherIP, dev, arpResTO, pitcherMacAddr, pitcherIP, 10); - if (catcherMacAddr == pcpp::MacAddress::Zero) - EXIT_WITH_ERROR("Cannot find catcher MAC address"); - - // create a buffer that will be used to send data chunks of the file - uint8_t* memblock = new uint8_t[blockSize]; - memset(memblock, 0, blockSize); - - // try the open the file for reading - std::ifstream file(filePath.c_str(), std::ios::in|std::ios::binary); - - if (file.is_open()) - { - // remove the path and keep just the file name. This is the name that will be delivered to the catcher - std::string fileName = getFileNameFromPath(filePath); - - // go back to the beginning of the file - file.seekg(0, std::ios::beg); - - uint16_t icmpId = 1; - - // copy the file name to the buffer - strcpy((char*)memblock, fileName.c_str()); - - IcmpFileTransferStartSend ftStartData = { - icmpId, - pitcherIP, - catcherIP - }; - - std::cout << "Waiting for catcher..." << std::endl; - - // establish connection with the catcher by sending it ICMP requests that contains the file name and wait for a response - // keep sending these requests until the catcher answers or until the program is stopped - while (1) - { - // send the catcher an ICMP request that includes an special ICMP_FT_START message in the timestamp field and the filename - // in the request data. The catcher should intercept this message and send an ICMP response with an ICMP_FT_ACK message in - // the timestamp field - if (!sendIcmpRequest(dev, - pitcherMacAddr, catcherMacAddr, - pitcherIP, catcherIP, - icmpId, ICMP_FT_START, - memblock, fileName.length() + 1)) - EXIT_WITH_ERROR("Cannot send file transfer start message"); - - // now wait for the catcher to answer. The timeout is SEND_TIMEOUT_BEFORE_FT_START. After that another ICMP request will be sent - int res = dev->startCaptureBlockingMode(waitForFileTransferStartAck, &ftStartData, SEND_TIMEOUT_BEFORE_FT_START); - if (!res) - EXIT_WITH_ERROR("Cannot start capturing packets"); - - // res == 1 means we got the catcher response so we can break the endless loop - if (res == 1) - break; - - // increase ICMP ID so we won't send the same ICMP ID again - icmpId++; - ftStartData.icmpMsgId++; - } - - std::cout << "Sending file '" << fileName << "' "; - - icmpId++; - uint32_t bytesSentSoFar = 0; - uint32_t MBSent = 0; - - uint32_t sleepBetweenPackets = 0; - // calculate how many microseconds (usec) the pitcher needs to sleep between sending each file data chunk (calculated from user defined - // packetPerSec parameter). - // The calculation is done in usec as in most cases the pitcher needs to sleep less than 1 second between chunks. However if packetPerSec - // equals to 1 it means sleeping for 1 second and in this case we can't use usleep (as it's not working for 1 sec or more) and we use - // sleep instead - if (packetPerSec > 1) - sleepBetweenPackets = (uint32_t)(1000000UL / packetPerSec); - - // read one chunk of the file and send it to catcher. This loop breaks when it is reaching the end of the file and can't read a block - // of size blockSize from the file - while (file.read((char*)memblock, blockSize)) - { - // send an ICMP request to the catcher containing the data chunk.The message type (set in the timestamp field) is ICMP_FT_DATA - // so the catcher knows it's a data chunk - if (!sendIcmpRequest(dev, pitcherMacAddr, catcherMacAddr, pitcherIP, catcherIP, icmpId, ICMP_FT_DATA, memblock, blockSize)) - EXIT_WITH_ERROR("Cannot send file data message"); - - // use usleep or sleep (see comment a few lines below) - if (packetPerSec > 1) - usleep(sleepBetweenPackets); - else if (packetPerSec == 1) - pcpp::multiPlatformSleep(1); - - bytesSentSoFar += blockSize; - - // print a dot ('.') on every 1MB sent - MBSent += blockSize; - if (MBSent > ONE_MBYTE) - { - MBSent -= ONE_MBYTE; - std::cout << "."; - } - - icmpId++; - } - - // after the loop above breaks there may be one more block to read (of size less than blockSize). Read it and send it to the catcher - if (file.gcount() > 0) - { - if (!sendIcmpRequest(dev, pitcherMacAddr, catcherMacAddr, pitcherIP, catcherIP, icmpId, ICMP_FT_DATA, memblock, file.gcount())) - EXIT_WITH_ERROR("Cannot send file data message"); - - bytesSentSoFar += file.gcount(); - std::cout << "."; - } - - // done sending the file to the catcher, send an ICMP request with message type ICMP_FT_END (in the timestamp field) to the catcher - // to indicate all file was sent - if (!sendIcmpRequest(dev, pitcherMacAddr, catcherMacAddr, pitcherIP, catcherIP, icmpId, ICMP_FT_END, nullptr, 0)) - EXIT_WITH_ERROR("Cannot send file transfer end message"); - - std::cout << std::endl << std::endl - << "Finished sending '" << fileName << "' " - << "[sent " << bytesSentSoFar << " bytes]" - << std::endl; - } - else - EXIT_WITH_ERROR("Cannot open file '" << filePath << "'"); - - // close the file and the device. Free the memory for memblock - file.close(); - dev->close(); - delete [] memblock; +void sendFile(const std::string& filePath, pcpp::IPv4Address pitcherIP, + pcpp::IPv4Address catcherIP, size_t blockSize, int packetPerSec) { + // identify the interface to listen and send packets to + pcpp::PcapLiveDevice* dev = + pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDeviceByIp(pitcherIP); + if (dev == nullptr) + EXIT_WITH_ERROR("Cannot find network interface with IP '" << pitcherIP + << "'"); + + // try to open the interface (device) + if (!dev->open()) + EXIT_WITH_ERROR("Cannot open network interface "); + + // get the MAC address of the interface + pcpp::MacAddress pitcherMacAddr = dev->getMacAddress(); + if (pitcherMacAddr == pcpp::MacAddress::Zero) + EXIT_WITH_ERROR("Cannot find pitcher MAC address"); + + // discover the MAC address of the catcher by sending an ARP ping to it + double arpResTO = 0; + pcpp::MacAddress catcherMacAddr = + pcpp::NetworkUtils::getInstance().getMacAddress( + catcherIP, dev, arpResTO, pitcherMacAddr, pitcherIP, 10); + if (catcherMacAddr == pcpp::MacAddress::Zero) + EXIT_WITH_ERROR("Cannot find catcher MAC address"); + + // create a buffer that will be used to send data chunks of the file + uint8_t* memblock = new uint8_t[blockSize]; + memset(memblock, 0, blockSize); + + // try the open the file for reading + std::ifstream file(filePath.c_str(), std::ios::in | std::ios::binary); + + if (file.is_open()) { + // remove the path and keep just the file name. This is the name that will + // be delivered to the catcher + std::string fileName = getFileNameFromPath(filePath); + + // go back to the beginning of the file + file.seekg(0, std::ios::beg); + + uint16_t icmpId = 1; + + // copy the file name to the buffer + strcpy((char*)memblock, fileName.c_str()); + + IcmpFileTransferStartSend ftStartData = {icmpId, pitcherIP, catcherIP}; + + std::cout << "Waiting for catcher..." << std::endl; + + // establish connection with the catcher by sending it ICMP requests that + // contains the file name and wait for a response keep sending these + // requests until the catcher answers or until the program is stopped + while (1) { + // send the catcher an ICMP request that includes an special ICMP_FT_START + // message in the timestamp field and the filename in the request data. + // The catcher should intercept this message and send an ICMP response + // with an ICMP_FT_ACK message in the timestamp field + if (!sendIcmpRequest(dev, pitcherMacAddr, catcherMacAddr, pitcherIP, + catcherIP, icmpId, ICMP_FT_START, memblock, + fileName.length() + 1)) + EXIT_WITH_ERROR("Cannot send file transfer start message"); + + // now wait for the catcher to answer. The timeout is + // SEND_TIMEOUT_BEFORE_FT_START. After that another ICMP request will be + // sent + int res = dev->startCaptureBlockingMode(waitForFileTransferStartAck, + &ftStartData, + SEND_TIMEOUT_BEFORE_FT_START); + if (!res) + EXIT_WITH_ERROR("Cannot start capturing packets"); + + // res == 1 means we got the catcher response so we can break the endless + // loop + if (res == 1) + break; + + // increase ICMP ID so we won't send the same ICMP ID again + icmpId++; + ftStartData.icmpMsgId++; + } + + std::cout << "Sending file '" << fileName << "' "; + + icmpId++; + uint32_t bytesSentSoFar = 0; + uint32_t MBSent = 0; + + uint32_t sleepBetweenPackets = 0; + // calculate how many microseconds (usec) the pitcher needs to sleep between + // sending each file data chunk (calculated from user defined packetPerSec + // parameter). The calculation is done in usec as in most cases the pitcher + // needs to sleep less than 1 second between chunks. However if packetPerSec + // equals to 1 it means sleeping for 1 second and in this case we can't use + // usleep (as it's not working for 1 sec or more) and we use sleep instead + if (packetPerSec > 1) + sleepBetweenPackets = (uint32_t)(1000000UL / packetPerSec); + + // read one chunk of the file and send it to catcher. This loop breaks when + // it is reaching the end of the file and can't read a block of size + // blockSize from the file + while (file.read((char*)memblock, blockSize)) { + // send an ICMP request to the catcher containing the data chunk.The + // message type (set in the timestamp field) is ICMP_FT_DATA so the + // catcher knows it's a data chunk + if (!sendIcmpRequest(dev, pitcherMacAddr, catcherMacAddr, pitcherIP, + catcherIP, icmpId, ICMP_FT_DATA, memblock, + blockSize)) + EXIT_WITH_ERROR("Cannot send file data message"); + + // use usleep or sleep (see comment a few lines below) + if (packetPerSec > 1) + usleep(sleepBetweenPackets); + else if (packetPerSec == 1) + pcpp::multiPlatformSleep(1); + + bytesSentSoFar += blockSize; + + // print a dot ('.') on every 1MB sent + MBSent += blockSize; + if (MBSent > ONE_MBYTE) { + MBSent -= ONE_MBYTE; + std::cout << "."; + } + + icmpId++; + } + + // after the loop above breaks there may be one more block to read (of size + // less than blockSize). Read it and send it to the catcher + if (file.gcount() > 0) { + if (!sendIcmpRequest(dev, pitcherMacAddr, catcherMacAddr, pitcherIP, + catcherIP, icmpId, ICMP_FT_DATA, memblock, + file.gcount())) + EXIT_WITH_ERROR("Cannot send file data message"); + + bytesSentSoFar += file.gcount(); + std::cout << "."; + } + + // done sending the file to the catcher, send an ICMP request with message + // type ICMP_FT_END (in the timestamp field) to the catcher to indicate all + // file was sent + if (!sendIcmpRequest(dev, pitcherMacAddr, catcherMacAddr, pitcherIP, + catcherIP, icmpId, ICMP_FT_END, nullptr, 0)) + EXIT_WITH_ERROR("Cannot send file transfer end message"); + + std::cout << std::endl + << std::endl + << "Finished sending '" << fileName << "' " + << "[sent " << bytesSentSoFar << " bytes]" << std::endl; + } else + EXIT_WITH_ERROR("Cannot open file '" << filePath << "'"); + + // close the file and the device. Free the memory for memblock + file.close(); + dev->close(); + delete[] memblock; } /** * main method of this ICMP pitcher */ -int main(int argc, char* argv[]) -{ - pcpp::AppName::init(argc, argv); - - bool sender, receiver; - pcpp::IPv4Address pitcherIP; - pcpp::IPv4Address catcherIP; - std::string fileNameToSend; - int packetsPerSec = 0; - size_t blockSize = 0; - - // disable stdout buffering so all std::cout command will be printed immediately - setbuf(stdout, nullptr); - - // read and parse command line arguments. This method also takes care of arguments correctness. If they're not correct, it'll exit the program - readCommandLineArguments(argc, argv, "pitcher", "catcher", sender, receiver, pitcherIP, catcherIP, fileNameToSend, packetsPerSec, blockSize); - - // send a file to the catcher - if (sender) - sendFile(fileNameToSend, pitcherIP, catcherIP, blockSize, packetsPerSec); - // receive a file from the catcher - else if (receiver) - receiveFile(pitcherIP, catcherIP, packetsPerSec); +int main(int argc, char* argv[]) { + pcpp::AppName::init(argc, argv); + + bool sender, receiver; + pcpp::IPv4Address pitcherIP; + pcpp::IPv4Address catcherIP; + std::string fileNameToSend; + int packetsPerSec = 0; + size_t blockSize = 0; + + // disable stdout buffering so all std::cout command will be printed + // immediately + setbuf(stdout, nullptr); + + // read and parse command line arguments. This method also takes care of + // arguments correctness. If they're not correct, it'll exit the program + readCommandLineArguments(argc, argv, "pitcher", "catcher", sender, receiver, + pitcherIP, catcherIP, fileNameToSend, packetsPerSec, + blockSize); + + // send a file to the catcher + if (sender) + sendFile(fileNameToSend, pitcherIP, catcherIP, blockSize, packetsPerSec); + // receive a file from the catcher + else if (receiver) + receiveFile(pitcherIP, catcherIP, packetsPerSec); } diff --git a/Examples/KniPong/main.cpp b/Examples/KniPong/main.cpp index 3b71c9c8a5..91a9fb0240 100644 --- a/Examples/KniPong/main.cpp +++ b/Examples/KniPong/main.cpp @@ -1,41 +1,42 @@ +#include #include -#include #include -#include +#include #include #include -#include +#include #include -#include -#include +#include #include #include -#include #include -#include +#include +#include +#include #include #include -#include -#include #include +#include #include -#include +#include #include +#include #include #include #include - -#define EXIT_WITH_ERROR(reason) do { \ - std::cout << std::endl << "ERROR: " << reason << std::endl << std::endl; \ - exit(1); \ - } while(0) - +#define EXIT_WITH_ERROR(reason) \ + do { \ + std::cout << std::endl \ + << "ERROR: " << reason << std::endl \ + << std::endl; \ + exit(1); \ + } while (0) #define IO_BUFF_SIZE (1 << 14) #define WANT_POLLIN (-2) @@ -43,463 +44,436 @@ #define DEFAULT_KNI_NAME "pcppkni0" #define DEFAULT_PORT 62604 +namespace { -namespace -{ - -struct KniPongArgs -{ - std::string kniIp; - std::string outIp; - std::string kniName; - uint16_t kniPort; +struct KniPongArgs { + std::string kniIp; + std::string outIp; + std::string kniName; + uint16_t kniPort; }; - typedef int linuxFd; - -struct LinuxSocket -{ - inline operator int() const { return m_Socket; } - linuxFd m_Socket; +struct LinuxSocket { + inline operator int() const { return m_Socket; } + linuxFd m_Socket; }; - -struct PacketStats -{ - unsigned long totalPackets; - unsigned long udpPacketsIn; - unsigned long udpPacketsOutFail; - unsigned long arpPacketsIn; - unsigned long arpPacketsOutFail; +struct PacketStats { + unsigned long totalPackets; + unsigned long udpPacketsIn; + unsigned long udpPacketsOutFail; + unsigned long arpPacketsIn; + unsigned long arpPacketsOutFail; }; - static bool doContinue = true; - /** * Print application version */ -void printAppVersion() -{ - std::cout - << pcpp::AppName::get() << " " << pcpp::getPcapPlusPlusVersionFull() << std::endl - << "Built: " << pcpp::getBuildDateTime() << std::endl - << "Built from: " << pcpp::getGitInfo() << std::endl; - exit(0); +void printAppVersion() { + std::cout << pcpp::AppName::get() << " " << pcpp::getPcapPlusPlusVersionFull() + << std::endl + << "Built: " << pcpp::getBuildDateTime() << std::endl + << "Built from: " << pcpp::getGitInfo() << std::endl; + exit(0); } - /** * Print application usage */ -inline void printUsage() -{ - std::cout << std::endl - << "Usage:" << std::endl - << "------" << std::endl - << pcpp::AppName::get() << " [-hv] [-n KNI_DEVICE_NAME] [-p PORT] -s SRC_IPV4 -d DST_IPV4" << std::endl - << std::endl - << "Options:" << std::endl - << " -s --src SRC_IPV4 : IPv4 address to assign to the created KNI device" << std::endl - << " -d --dst DST_IPV4 : Virtual IPv4 address to communicate with. Must be in /24 subnet with SRC_IPV4" << std::endl - << " -n --name KNI_DEVICE_NAME : Name for KNI device. Default: \"" << DEFAULT_KNI_NAME << "\"" << std::endl - << " -p --port PORT : Port for communication. Default: " << DEFAULT_PORT << std::endl - << " -v --version : Displays the current version and exits" << std::endl - << " -h --help : Displays this help message and exits" << std::endl - << std::endl; +inline void printUsage() { + std::cout + << std::endl + << "Usage:" << std::endl + << "------" << std::endl + << pcpp::AppName::get() + << " [-hv] [-n KNI_DEVICE_NAME] [-p PORT] -s SRC_IPV4 -d DST_IPV4" + << std::endl + << std::endl + << "Options:" << std::endl + << " -s --src SRC_IPV4 : IPv4 address to assign to the " + "created KNI device" + << std::endl + << " -d --dst DST_IPV4 : Virtual IPv4 address to " + "communicate with. Must be in /24 subnet with SRC_IPV4" + << std::endl + << " -n --name KNI_DEVICE_NAME : Name for KNI device. Default: \"" + << DEFAULT_KNI_NAME << "\"" << std::endl + << " -p --port PORT : Port for communication. Default: " + << DEFAULT_PORT << std::endl + << " -v --version : Displays the current version and " + "exits" + << std::endl + << " -h --help : Displays this help message and " + "exits" + << std::endl + << std::endl; } - -inline void parseArgs(int argc, char* argv[], KniPongArgs& args) -{ - struct option KniPongOptions[] = - { - {"src", required_argument, NULL, 's'}, - {"dst", required_argument, NULL, 'd'}, - {"name", optional_argument, NULL, 'n'}, - {"port", optional_argument, NULL, 'p'}, - {"help", no_argument, NULL, 'h'}, - {"version", no_argument, NULL, 'v'}, - {NULL, 0, NULL, 0} - }; - // Default port: - args.kniPort = DEFAULT_PORT; - int optionIndex = 0; - int opt = 0; - while ((opt = getopt_long(argc, argv, "s:d:n:p:hv", KniPongOptions, &optionIndex)) != -1) - { - switch (opt) - { - case 0: - break; - case 's': - args.kniIp = optarg; - break; - case 'd': - args.outIp = optarg; - break; - case 'n': - args.kniName = optarg; - break; - case 'p': - args.kniPort = std::strtoul(optarg, NULL, 10) & 0xFFFF; - break; - case 'v': - printAppVersion(); - break; - case 'h': - { - printUsage(); - std::exit(0); - } break; - default: - { - printUsage(); - exit(1); - } break; - } - } - // Default name for KNI device: - if (args.kniName.empty()) - args.kniName = DEFAULT_KNI_NAME; - if (args.kniIp.empty()) - { - printUsage(); - EXIT_WITH_ERROR("IP for KNI device not provided"); - } - if (args.outIp.empty()) - { - printUsage(); - EXIT_WITH_ERROR("Virtual IP for communication not provided"); - } - pcpp::IPv4Address kniIp = args.kniIp; - pcpp::IPv4Address outIp = args.outIp; - if (!(kniIp.isValid() && outIp.isValid())) - { - EXIT_WITH_ERROR("One of provided IPs is not valid"); - } - if (!outIp.matchNetwork(pcpp::IPv4Network(kniIp, "255.255.255.0"))) - { - EXIT_WITH_ERROR( - "Provided Virtual IP '" << outIp << "' is not in same required subnet '255.255.255.0' as KNI IP '" << kniIp << "'" - ); - } +inline void parseArgs(int argc, char* argv[], KniPongArgs& args) { + struct option KniPongOptions[] = {{"src", required_argument, NULL, 's'}, + {"dst", required_argument, NULL, 'd'}, + {"name", optional_argument, NULL, 'n'}, + {"port", optional_argument, NULL, 'p'}, + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'v'}, + {NULL, 0, NULL, 0}}; + // Default port: + args.kniPort = DEFAULT_PORT; + int optionIndex = 0; + int opt = 0; + while ((opt = getopt_long(argc, argv, "s:d:n:p:hv", KniPongOptions, + &optionIndex)) != -1) { + switch (opt) { + case 0: + break; + case 's': + args.kniIp = optarg; + break; + case 'd': + args.outIp = optarg; + break; + case 'n': + args.kniName = optarg; + break; + case 'p': + args.kniPort = std::strtoul(optarg, NULL, 10) & 0xFFFF; + break; + case 'v': + printAppVersion(); + break; + case 'h': { + printUsage(); + std::exit(0); + } break; + default: { + printUsage(); + exit(1); + } break; + } + } + // Default name for KNI device: + if (args.kniName.empty()) + args.kniName = DEFAULT_KNI_NAME; + if (args.kniIp.empty()) { + printUsage(); + EXIT_WITH_ERROR("IP for KNI device not provided"); + } + if (args.outIp.empty()) { + printUsage(); + EXIT_WITH_ERROR("Virtual IP for communication not provided"); + } + pcpp::IPv4Address kniIp = args.kniIp; + pcpp::IPv4Address outIp = args.outIp; + if (!(kniIp.isValid() && outIp.isValid())) { + EXIT_WITH_ERROR("One of provided IPs is not valid"); + } + if (!outIp.matchNetwork(pcpp::IPv4Network(kniIp, "255.255.255.0"))) { + EXIT_WITH_ERROR( + "Provided Virtual IP '" + << outIp + << "' is not in same required subnet '255.255.255.0' as KNI IP '" + << kniIp << "'"); + } } - /** * Simple dummy callbacks that always yields success for Linux Kernel */ -struct KniDummyCallbacks -{ - static int changeMtuNew(uint16_t, unsigned int) { return 0; } - static int changeMtuOld(uint8_t, unsigned int) { return 0; } - static int configNetworkIfNew(uint16_t, uint8_t) { return 0; } - static int configNetworkIfOld(uint8_t, uint8_t) { return 0; } - static int configMacAddress(uint16_t, uint8_t[]) { return 0; } - static int configPromiscusity(uint16_t, uint8_t) { return 0; } - - static pcpp::KniDevice::KniIoctlCallbacks cbNew; - static pcpp::KniDevice::KniOldIoctlCallbacks cbOld; - - static void setCallbacks() - { - cbNew.change_mtu = changeMtuNew; - cbNew.config_network_if = configNetworkIfNew; - cbNew.config_mac_address = configMacAddress; - cbNew.config_promiscusity = configPromiscusity; - cbOld.change_mtu = changeMtuOld; - cbOld.config_network_if = configNetworkIfOld; - } +struct KniDummyCallbacks { + static int changeMtuNew(uint16_t, unsigned int) { return 0; } + static int changeMtuOld(uint8_t, unsigned int) { return 0; } + static int configNetworkIfNew(uint16_t, uint8_t) { return 0; } + static int configNetworkIfOld(uint8_t, uint8_t) { return 0; } + static int configMacAddress(uint16_t, uint8_t[]) { return 0; } + static int configPromiscusity(uint16_t, uint8_t) { return 0; } + + static pcpp::KniDevice::KniIoctlCallbacks cbNew; + static pcpp::KniDevice::KniOldIoctlCallbacks cbOld; + + static void setCallbacks() { + cbNew.change_mtu = changeMtuNew; + cbNew.config_network_if = configNetworkIfNew; + cbNew.config_mac_address = configMacAddress; + cbNew.config_promiscusity = configPromiscusity; + cbOld.change_mtu = changeMtuOld; + cbOld.config_network_if = configNetworkIfOld; + } }; pcpp::KniDevice::KniIoctlCallbacks KniDummyCallbacks::cbNew; pcpp::KniDevice::KniOldIoctlCallbacks KniDummyCallbacks::cbOld; - /** * Setup IP of net device by calling the ip unix utility */ -inline bool setKniIp(const pcpp::IPv4Address& ip, const std::string& kniName) -{ - std::ostringstream command; - command << "ip a add " << ip << "/24 dev " << kniName; - pcpp::executeShellCommand(command.str()); - command.str(""); - command << "ip a | grep " << ip; - std::string result = pcpp::executeShellCommand(command.str()); - return result != "" && result != "ERROR"; +inline bool setKniIp(const pcpp::IPv4Address& ip, const std::string& kniName) { + std::ostringstream command; + command << "ip a add " << ip << "/24 dev " << kniName; + pcpp::executeShellCommand(command.str()); + command.str(""); + command << "ip a | grep " << ip; + std::string result = pcpp::executeShellCommand(command.str()); + return result != "" && result != "ERROR"; } - /** * KNI device setup routine */ -inline pcpp::KniDevice* setupKniDevice(const KniPongArgs& args) -{ - { - // Setup DPDK - pcpp::CoreMask cm = 0x3; - bool dpdkInitSuccess = pcpp::DpdkDeviceList::initDpdk(cm, 1023); - if (!dpdkInitSuccess) - EXIT_WITH_ERROR("Failed to init DPDK"); - } - pcpp::IPv4Address kniIp = args.kniIp; - // Setup device config - pcpp::KniDevice* device = NULL; - pcpp::KniDevice::KniDeviceConfiguration devConfig; - devConfig.name = args.kniName; - KniDummyCallbacks::setCallbacks(); - if (pcpp::KniDeviceList::callbackVersion() == pcpp::KniDeviceList::CALLBACKS_NEW) - { - devConfig.callbacks = &KniDummyCallbacks::cbNew; - } - else - { - devConfig.oldCallbacks = &KniDummyCallbacks::cbOld; - } - devConfig.bindKthread = false; - pcpp::KniDeviceList& kniDeviceList = pcpp::KniDeviceList::getInstance(); - if (!kniDeviceList.isInitialized()) - EXIT_WITH_ERROR("Can't initialize KNI device list"); - device = kniDeviceList.createDevice(devConfig, 1024); - if (device == NULL) - EXIT_WITH_ERROR("Can't create KNI device"); - // Check KNI device and start request thread - if (!device->isInitialized()) - EXIT_WITH_ERROR("KNI device was not initialized correctly"); - if (!device->open()) - EXIT_WITH_ERROR("Could not open KNI device"); - if (!device->startRequestHandlerThread(0, 500000000)) - EXIT_WITH_ERROR("Could not start KNI device request handler thread"); - // Assign IP - if (!setKniIp(kniIp, args.kniName)) - EXIT_WITH_ERROR("Can't set KNI device IP"); - // Turn device on for Linux Kernel - if (!device->setLinkState(pcpp::KniDevice::LINK_UP)) - EXIT_WITH_ERROR("Can't set KNI device link state to UP"); - return device; +inline pcpp::KniDevice* setupKniDevice(const KniPongArgs& args) { + { + // Setup DPDK + pcpp::CoreMask cm = 0x3; + bool dpdkInitSuccess = pcpp::DpdkDeviceList::initDpdk(cm, 1023); + if (!dpdkInitSuccess) + EXIT_WITH_ERROR("Failed to init DPDK"); + } + pcpp::IPv4Address kniIp = args.kniIp; + // Setup device config + pcpp::KniDevice* device = NULL; + pcpp::KniDevice::KniDeviceConfiguration devConfig; + devConfig.name = args.kniName; + KniDummyCallbacks::setCallbacks(); + if (pcpp::KniDeviceList::callbackVersion() == + pcpp::KniDeviceList::CALLBACKS_NEW) { + devConfig.callbacks = &KniDummyCallbacks::cbNew; + } else { + devConfig.oldCallbacks = &KniDummyCallbacks::cbOld; + } + devConfig.bindKthread = false; + pcpp::KniDeviceList& kniDeviceList = pcpp::KniDeviceList::getInstance(); + if (!kniDeviceList.isInitialized()) + EXIT_WITH_ERROR("Can't initialize KNI device list"); + device = kniDeviceList.createDevice(devConfig, 1024); + if (device == NULL) + EXIT_WITH_ERROR("Can't create KNI device"); + // Check KNI device and start request thread + if (!device->isInitialized()) + EXIT_WITH_ERROR("KNI device was not initialized correctly"); + if (!device->open()) + EXIT_WITH_ERROR("Could not open KNI device"); + if (!device->startRequestHandlerThread(0, 500000000)) + EXIT_WITH_ERROR("Could not start KNI device request handler thread"); + // Assign IP + if (!setKniIp(kniIp, args.kniName)) + EXIT_WITH_ERROR("Can't set KNI device IP"); + // Turn device on for Linux Kernel + if (!device->setLinkState(pcpp::KniDevice::LINK_UP)) + EXIT_WITH_ERROR("Can't set KNI device link state to UP"); + return device; } - /** * Open UDP socket for communication with KNI device */ -inline LinuxSocket setupLinuxSocket(const KniPongArgs& args) -{ // Open socket - enum { INVALID_FD = -1 }; - LinuxSocket sock; - if ((sock.m_Socket = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_FD) - { - int old_errno = errno; - EXIT_WITH_ERROR("Could not open socket" << std::endl << "Errno: " << std::strerror(old_errno)); - } - // Bind socket to KNI device IP - struct sockaddr_in egress; - std::memset(&egress, 0, sizeof(egress)); - egress.sin_family = AF_INET; - egress.sin_addr.s_addr = inet_addr(args.kniIp.c_str()); - egress.sin_port = pcpp::hostToNet16(args.kniPort); - if (bind(sock, (struct sockaddr*)&egress, sizeof(egress)) == -1) - { - int old_errno = errno; - close(sock); - EXIT_WITH_ERROR("Could not bind socket" << std::endl << "Errno: " << std::strerror(old_errno)); - } - - return sock; +inline LinuxSocket setupLinuxSocket(const KniPongArgs& args) { // Open socket + enum { INVALID_FD = -1 }; + LinuxSocket sock; + if ((sock.m_Socket = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_FD) { + int old_errno = errno; + EXIT_WITH_ERROR("Could not open socket" + << std::endl + << "Errno: " << std::strerror(old_errno)); + } + // Bind socket to KNI device IP + struct sockaddr_in egress; + std::memset(&egress, 0, sizeof(egress)); + egress.sin_family = AF_INET; + egress.sin_addr.s_addr = inet_addr(args.kniIp.c_str()); + egress.sin_port = pcpp::hostToNet16(args.kniPort); + if (bind(sock, (struct sockaddr*)&egress, sizeof(egress)) == -1) { + int old_errno = errno; + close(sock); + EXIT_WITH_ERROR("Could not bind socket" + << std::endl + << "Errno: " << std::strerror(old_errno)); + } + + return sock; } - /** * Handle all ARP requests on KNI interface: map all IPs to same MAC */ -inline void processArp(pcpp::Packet& packet, pcpp::ArpLayer* arpLayer) -{ - pcpp::MacAddress rndMac("00:42:43:74:11:54"); - pcpp::EthLayer* ethernetLayer = NULL; - pcpp::arphdr arpHdr; - pcpp::arphdr* origArpHdr = arpLayer->getArpHeader(); - // Copy ARP request - std::memcpy(&arpHdr, origArpHdr, sizeof(arpHdr)); - // Fill fields - arpHdr.hardwareType = pcpp::hostToNet16(0x0001); // ETHERNET - arpHdr.hardwareSize = sizeof(((pcpp::arphdr*)0)->senderMacAddr); // sizeof(MAC) - arpHdr.protocolSize = sizeof(((pcpp::arphdr*)0)->senderIpAddr); // sizeof(IPv4) - arpHdr.opcode = pcpp::hostToNet16(pcpp::ARP_REPLY); - std::memcpy(arpHdr.targetMacAddr, origArpHdr->senderMacAddr, sizeof(((pcpp::arphdr*)0)->senderMacAddr)); - std::memcpy(&arpHdr.targetIpAddr, &origArpHdr->senderIpAddr, sizeof(((pcpp::arphdr*)0)->senderIpAddr)); - std::memcpy(&arpHdr.senderIpAddr, &origArpHdr->targetIpAddr, sizeof(((pcpp::arphdr*)0)->senderIpAddr)); - // Set rnd MAC in response - rndMac.copyTo(arpHdr.senderMacAddr); - // Copy ready ARP response to packet - std::memcpy(origArpHdr, &arpHdr, sizeof(arpHdr)); - - // Setup Ethernet addresses in Ethernet layer - ethernetLayer = packet.getLayerOfType(); - pcpp::ether_header ethHdr; - pcpp::ether_header* origEthHdr = ethernetLayer->getEthHeader(); - std::memcpy(ðHdr, origEthHdr, sizeof(ethHdr)); - std::memcpy(ethHdr.dstMac, origEthHdr->srcMac, sizeof(ethHdr.dstMac)); - rndMac.copyTo(ethHdr.srcMac); - // Copy ready Ethernet layer to packet - std::memcpy(origEthHdr, ðHdr, sizeof(ethHdr)); +inline void processArp(pcpp::Packet& packet, pcpp::ArpLayer* arpLayer) { + pcpp::MacAddress rndMac("00:42:43:74:11:54"); + pcpp::EthLayer* ethernetLayer = NULL; + pcpp::arphdr arpHdr; + pcpp::arphdr* origArpHdr = arpLayer->getArpHeader(); + // Copy ARP request + std::memcpy(&arpHdr, origArpHdr, sizeof(arpHdr)); + // Fill fields + arpHdr.hardwareType = pcpp::hostToNet16(0x0001); // ETHERNET + arpHdr.hardwareSize = + sizeof(((pcpp::arphdr*)0)->senderMacAddr); // sizeof(MAC) + arpHdr.protocolSize = + sizeof(((pcpp::arphdr*)0)->senderIpAddr); // sizeof(IPv4) + arpHdr.opcode = pcpp::hostToNet16(pcpp::ARP_REPLY); + std::memcpy(arpHdr.targetMacAddr, origArpHdr->senderMacAddr, + sizeof(((pcpp::arphdr*)0)->senderMacAddr)); + std::memcpy(&arpHdr.targetIpAddr, &origArpHdr->senderIpAddr, + sizeof(((pcpp::arphdr*)0)->senderIpAddr)); + std::memcpy(&arpHdr.senderIpAddr, &origArpHdr->targetIpAddr, + sizeof(((pcpp::arphdr*)0)->senderIpAddr)); + // Set rnd MAC in response + rndMac.copyTo(arpHdr.senderMacAddr); + // Copy ready ARP response to packet + std::memcpy(origArpHdr, &arpHdr, sizeof(arpHdr)); + + // Setup Ethernet addresses in Ethernet layer + ethernetLayer = packet.getLayerOfType(); + pcpp::ether_header ethHdr; + pcpp::ether_header* origEthHdr = ethernetLayer->getEthHeader(); + std::memcpy(ðHdr, origEthHdr, sizeof(ethHdr)); + std::memcpy(ethHdr.dstMac, origEthHdr->srcMac, sizeof(ethHdr.dstMac)); + rndMac.copyTo(ethHdr.srcMac); + // Copy ready Ethernet layer to packet + std::memcpy(origEthHdr, ðHdr, sizeof(ethHdr)); } - /** - * Handle all UDP packets as a packet carying a "ping" string to "pong" to with same string. - * Handle only packets that are of type: Eth / Ip / Udp / Payload. + * Handle all UDP packets as a packet carying a "ping" string to "pong" to with + * same string. Handle only packets that are of type: Eth / Ip / Udp / Payload. */ -inline bool processUdp(pcpp::Packet& packet, pcpp::UdpLayer* udpLayer) -{ - pcpp::EthLayer* ethernetLayer = NULL; - pcpp::IPv4Layer* ipLayer = NULL; - - ethernetLayer = packet.getLayerOfType(); - pcpp::ether_header ethHdr; - pcpp::ether_header* origEthHdr = ethernetLayer->getEthHeader(); - std::memcpy(ðHdr, origEthHdr, sizeof(ethHdr)); - // Swap MACs for Ethernet layer - std::memcpy(ethHdr.dstMac, origEthHdr->srcMac, sizeof(ethHdr.dstMac)); - std::memcpy(ethHdr.srcMac, origEthHdr->dstMac, sizeof(ethHdr.srcMac)); - std::memcpy(origEthHdr, ðHdr, sizeof(ethHdr)); - - ipLayer = packet.getLayerOfType(); - if (ipLayer == NULL) // Some invalid packet - return false; - pcpp::iphdr ipHdr; - pcpp::iphdr* origIpHdr = ipLayer->getIPv4Header(); - std::memcpy(&ipHdr, origIpHdr, sizeof(ipHdr)); - if (pcpp::netToHost16(ipHdr.fragmentOffset) & 0x1FFF) // Fragmented packet - return false; - // Swap src and dst IPs - std::memcpy(&ipHdr.ipSrc, &origIpHdr->ipDst, sizeof(ipHdr.ipSrc)); - std::memcpy(&ipHdr.ipDst, &origIpHdr->ipSrc, sizeof(ipHdr.ipDst)); - // Randomize IP id - ipHdr.ipId = std::rand() & 0xFFFF; - // Set by RFC791 - ipHdr.timeToLive = 64; - std::memcpy(origIpHdr, &ipHdr, sizeof(ipHdr)); - - pcpp::udphdr udpHdr; - pcpp::udphdr* origUdpHdr = udpLayer->getUdpHeader(); - std::memcpy(&udpHdr, origUdpHdr, sizeof(udpHdr)); - // Swap src and dst ports - std::memcpy(&udpHdr.portSrc, &origUdpHdr->portDst, sizeof(udpHdr.portSrc)); - std::memcpy(&udpHdr.portDst, &origUdpHdr->portSrc, sizeof(udpHdr.portDst)); - std::memcpy(origUdpHdr, &udpHdr, sizeof(udpHdr)); - - // Calculate checksums of IP and UDP layers - packet.computeCalculateFields(); - // Packet is ready to be sent - return true; +inline bool processUdp(pcpp::Packet& packet, pcpp::UdpLayer* udpLayer) { + pcpp::EthLayer* ethernetLayer = NULL; + pcpp::IPv4Layer* ipLayer = NULL; + + ethernetLayer = packet.getLayerOfType(); + pcpp::ether_header ethHdr; + pcpp::ether_header* origEthHdr = ethernetLayer->getEthHeader(); + std::memcpy(ðHdr, origEthHdr, sizeof(ethHdr)); + // Swap MACs for Ethernet layer + std::memcpy(ethHdr.dstMac, origEthHdr->srcMac, sizeof(ethHdr.dstMac)); + std::memcpy(ethHdr.srcMac, origEthHdr->dstMac, sizeof(ethHdr.srcMac)); + std::memcpy(origEthHdr, ðHdr, sizeof(ethHdr)); + + ipLayer = packet.getLayerOfType(); + if (ipLayer == NULL) // Some invalid packet + return false; + pcpp::iphdr ipHdr; + pcpp::iphdr* origIpHdr = ipLayer->getIPv4Header(); + std::memcpy(&ipHdr, origIpHdr, sizeof(ipHdr)); + if (pcpp::netToHost16(ipHdr.fragmentOffset) & 0x1FFF) // Fragmented packet + return false; + // Swap src and dst IPs + std::memcpy(&ipHdr.ipSrc, &origIpHdr->ipDst, sizeof(ipHdr.ipSrc)); + std::memcpy(&ipHdr.ipDst, &origIpHdr->ipSrc, sizeof(ipHdr.ipDst)); + // Randomize IP id + ipHdr.ipId = std::rand() & 0xFFFF; + // Set by RFC791 + ipHdr.timeToLive = 64; + std::memcpy(origIpHdr, &ipHdr, sizeof(ipHdr)); + + pcpp::udphdr udpHdr; + pcpp::udphdr* origUdpHdr = udpLayer->getUdpHeader(); + std::memcpy(&udpHdr, origUdpHdr, sizeof(udpHdr)); + // Swap src and dst ports + std::memcpy(&udpHdr.portSrc, &origUdpHdr->portDst, sizeof(udpHdr.portSrc)); + std::memcpy(&udpHdr.portDst, &origUdpHdr->portSrc, sizeof(udpHdr.portDst)); + std::memcpy(origUdpHdr, &udpHdr, sizeof(udpHdr)); + + // Calculate checksums of IP and UDP layers + packet.computeCalculateFields(); + // Packet is ready to be sent + return true; } - /** * Process burst of packets */ -bool processBurst(pcpp::MBufRawPacket packets[], uint32_t numOfPackets, pcpp::KniDevice* kni, void* cookie) -{ - PacketStats* packetStats = (PacketStats*)cookie; - pcpp::Packet packet; - pcpp::ArpLayer* arpLayer = NULL; - pcpp::UdpLayer* udpLayer = NULL; - - packetStats->totalPackets += numOfPackets; - for (uint32_t i = 0; i < numOfPackets; ++i) - { - packet.setRawPacket(packets + i, false); - if ((arpLayer = packet.getLayerOfType()) != NULL) - { - ++packetStats->arpPacketsIn; - processArp(packet, arpLayer); - // Packet is ready to be sent -> have no fields to recalculate - if (!kni->sendPacket(packet)) - ++packetStats->arpPacketsOutFail; - arpLayer = NULL; - continue; - } - - if ((udpLayer = packet.getLayerOfType()) != NULL) - { - ++packetStats->udpPacketsIn; - //! Warning (echo-Mike): DO NOT normalize next logic statement it relays on short circuiting - if (!processUdp(packet, udpLayer) || !kni->sendPacket(packet)) - ++packetStats->udpPacketsOutFail; - udpLayer = NULL; - continue; - } - - // Other packets are just ignored - } - - return true; +bool processBurst(pcpp::MBufRawPacket packets[], uint32_t numOfPackets, + pcpp::KniDevice* kni, void* cookie) { + PacketStats* packetStats = (PacketStats*)cookie; + pcpp::Packet packet; + pcpp::ArpLayer* arpLayer = NULL; + pcpp::UdpLayer* udpLayer = NULL; + + packetStats->totalPackets += numOfPackets; + for (uint32_t i = 0; i < numOfPackets; ++i) { + packet.setRawPacket(packets + i, false); + if ((arpLayer = packet.getLayerOfType()) != NULL) { + ++packetStats->arpPacketsIn; + processArp(packet, arpLayer); + // Packet is ready to be sent -> have no fields to recalculate + if (!kni->sendPacket(packet)) + ++packetStats->arpPacketsOutFail; + arpLayer = NULL; + continue; + } + + if ((udpLayer = packet.getLayerOfType()) != NULL) { + ++packetStats->udpPacketsIn; + //! Warning (echo-Mike): DO NOT normalize next logic statement it relays + //! on short circuiting + if (!processUdp(packet, udpLayer) || !kni->sendPacket(packet)) + ++packetStats->udpPacketsOutFail; + udpLayer = NULL; + continue; + } + + // Other packets are just ignored + } + + return true; } - /** * Connect UDP socket to other IP:port pair derived from our args */ -void connectUDPSocket(const LinuxSocket& sock, const KniPongArgs& args) -{ - struct sockaddr_in ingress; - std::memset(&ingress, 0, sizeof(ingress)); - ingress.sin_family = AF_INET; - ingress.sin_addr.s_addr = inet_addr(args.outIp.c_str()); - ingress.sin_port = pcpp::hostToNet16(args.kniPort); - if (connect(sock, (struct sockaddr*)&ingress, sizeof(ingress)) == -1) - { - int old_errno = errno; - close(sock); - EXIT_WITH_ERROR("Could not connect socket" << std::endl << "Errno: " << std::strerror(old_errno)); - } +void connectUDPSocket(const LinuxSocket& sock, const KniPongArgs& args) { + struct sockaddr_in ingress; + std::memset(&ingress, 0, sizeof(ingress)); + ingress.sin_family = AF_INET; + ingress.sin_addr.s_addr = inet_addr(args.outIp.c_str()); + ingress.sin_port = pcpp::hostToNet16(args.kniPort); + if (connect(sock, (struct sockaddr*)&ingress, sizeof(ingress)) == -1) { + int old_errno = errno; + close(sock); + EXIT_WITH_ERROR("Could not connect socket" + << std::endl + << "Errno: " << std::strerror(old_errno)); + } } - /** * Reworked fillbuf from netcat. See description in pingPongProcess */ -ssize_t fillbuf(linuxFd fd, unsigned char buff[], size_t& buffPos) -{ - size_t num = IO_BUFF_SIZE - buffPos; - ssize_t n; - - n = read(fd, buff + buffPos, num); - if (n == -1 && (errno == EAGAIN || errno == EINTR)) - n = WANT_POLLIN; - if (n <= 0) - return n; - buffPos += n; - return n; +ssize_t fillbuf(linuxFd fd, unsigned char buff[], size_t& buffPos) { + size_t num = IO_BUFF_SIZE - buffPos; + ssize_t n; + + n = read(fd, buff + buffPos, num); + if (n == -1 && (errno == EAGAIN || errno == EINTR)) + n = WANT_POLLIN; + if (n <= 0) + return n; + buffPos += n; + return n; } - /** * Reworked drainbuf from netcat. See description in pingPongProcess */ -ssize_t drainbuf(linuxFd fd, unsigned char buff[], size_t& buffPos) -{ - ssize_t n; - ssize_t adjust; - - n = write(fd, buff, buffPos); - if (n == -1 && (errno == EAGAIN || errno == EINTR)) - n = WANT_POLLOUT; - if (n <= 0) - return n; - /* adjust buffer */ - adjust = buffPos - n; - if (adjust > 0) - std::memmove(buff, buff + n, adjust); - buffPos -= n; - return n; +ssize_t drainbuf(linuxFd fd, unsigned char buff[], size_t& buffPos) { + ssize_t n; + ssize_t adjust; + + n = write(fd, buff, buffPos); + if (n == -1 && (errno == EAGAIN || errno == EINTR)) + n = WANT_POLLOUT; + if (n <= 0) + return n; + /* adjust buffer */ + adjust = buffPos - n; + if (adjust > 0) + std::memmove(buff, buff + n, adjust); + buffPos -= n; + return n; } - /** * Reworked readwrite from netcat. * @@ -511,233 +485,208 @@ ssize_t drainbuf(linuxFd fd, unsigned char buff[], size_t& buffPos) * - *Hobbit* * See: http://man7.org/linux/man-pages/man1/ncat.1.html */ -void pingPongProcess(const LinuxSocket& sock) -{ - - struct pollfd pfd[4]; - const int POLL_STDIN = 0, POLL_NETOUT = 1, POLL_NETIN = 2, POLL_STDOUT = 3; - const int DEFAULT_POLL_TIMEOUT = 3000;//milisec - unsigned char netbuff[IO_BUFF_SIZE]; - size_t netbuffPos = 0; - unsigned char ttybuff[IO_BUFF_SIZE]; - size_t ttybuffPos = 0; - int n; - ssize_t ret; - - /* stdin */ - pfd[POLL_STDIN].fd = STDIN_FILENO; - pfd[POLL_STDIN].events = POLLIN; - /* network out */ - pfd[POLL_NETOUT].fd = sock; - pfd[POLL_NETOUT].events = 0; - /* network in */ - pfd[POLL_NETIN].fd = sock; - pfd[POLL_NETIN].events = POLLIN; - /* stdout */ - pfd[POLL_STDOUT].fd = STDOUT_FILENO; - pfd[POLL_STDOUT].events = 0; - - while (doContinue) - { - /* both inputs are gone, buffers are empty, we are done */ - if (pfd[POLL_STDIN].fd == -1 && - pfd[POLL_NETIN].fd == -1 && - ttybuffPos == 0 && - netbuffPos == 0 - ) - { - return; - } - /* both outputs are gone, we can't continue */ - if (pfd[POLL_NETOUT].fd == -1 && pfd[POLL_STDOUT].fd == -1) - return; - - /* poll */ - int num_fds = poll(pfd, 4, DEFAULT_POLL_TIMEOUT); - - /* treat poll errors */ - if (num_fds == -1) - { - int old_errno = errno; - if (old_errno != EINTR) - { - close(sock); - EXIT_WITH_ERROR("poll returned an error" << std::endl << "Errno: " << std::strerror(old_errno)); - } - continue; - } - - if (num_fds == 0) - { - continue; - } - - /* treat socket error conditions */ - for (n = 0; n < 4; ++n) - { - if (pfd[n].revents & (POLLERR|POLLNVAL)) - { - pfd[n].fd = -1; - } - } - /* reading is possible after HUP */ - if (pfd[POLL_STDIN].events & POLLIN && - pfd[POLL_STDIN].revents & POLLHUP && - !(pfd[POLL_STDIN].revents & POLLIN) - ) - { - pfd[POLL_STDIN].fd = -1; - } - - if (pfd[POLL_NETIN].events & POLLIN && - pfd[POLL_NETIN].revents & POLLHUP && - !(pfd[POLL_NETIN].revents & POLLIN) - ) - { - pfd[POLL_NETIN].fd = -1; - } - - if (pfd[POLL_NETOUT].revents & POLLHUP) - { - pfd[POLL_NETOUT].fd = -1; - } - /* if HUP, stop watching stdout */ - if (pfd[POLL_STDOUT].revents & POLLHUP) - pfd[POLL_STDOUT].fd = -1; - /* if no net out, stop watching stdin */ - if (pfd[POLL_NETOUT].fd == -1) - pfd[POLL_STDIN].fd = -1; - /* if no stdout, stop watching net in */ - if (pfd[POLL_STDOUT].fd == -1) - { - if (pfd[POLL_NETIN].fd != -1) - shutdown(pfd[POLL_NETIN].fd, SHUT_RD); - pfd[POLL_NETIN].fd = -1; - } - - /* try to read from stdin */ - if (pfd[POLL_STDIN].revents & POLLIN && ttybuffPos < IO_BUFF_SIZE) - { - ret = fillbuf(pfd[POLL_STDIN].fd, ttybuff, ttybuffPos); - if (ret == WANT_POLLIN) - pfd[POLL_STDIN].events = POLLIN; - else if (ret == 0 || ret == -1) - pfd[POLL_STDIN].fd = -1; - /* read something - poll net out */ - if (ttybuffPos > 0) - pfd[POLL_NETOUT].events = POLLOUT; - /* filled buffer - remove self from polling */ - if (ttybuffPos == IO_BUFF_SIZE) - pfd[POLL_STDIN].events = 0; - } - /* try to write to network */ - if (pfd[POLL_NETOUT].revents & POLLOUT && ttybuffPos > 0) - { - ret = drainbuf(pfd[POLL_NETOUT].fd, ttybuff, ttybuffPos); - if (ret == WANT_POLLOUT) - pfd[POLL_NETOUT].events = POLLOUT; - else if (ret == -1) - pfd[POLL_NETOUT].fd = -1; - /* buffer empty - remove self from polling */ - if (ttybuffPos == 0) - pfd[POLL_NETOUT].events = 0; - /* buffer no longer full - poll stdin again */ - if (ttybuffPos < IO_BUFF_SIZE) - pfd[POLL_STDIN].events = POLLIN; - } - /* try to read from network */ - if (pfd[POLL_NETIN].revents & POLLIN && netbuffPos < IO_BUFF_SIZE) - { - ret = fillbuf(pfd[POLL_NETIN].fd, netbuff, netbuffPos); - if (ret == WANT_POLLIN) - pfd[POLL_NETIN].events = POLLIN; - else if (ret == -1) - pfd[POLL_NETIN].fd = -1; - /* eof on net in - remove from pfd */ - if (ret == 0) - { - shutdown(pfd[POLL_NETIN].fd, SHUT_RD); - pfd[POLL_NETIN].fd = -1; - } - /* read something - poll stdout */ - if (netbuffPos > 0) - pfd[POLL_STDOUT].events = POLLOUT; - /* filled buffer - remove self from polling */ - if (netbuffPos == IO_BUFF_SIZE) - pfd[POLL_NETIN].events = 0; - } - /* try to write to stdout */ - if (pfd[POLL_STDOUT].revents & POLLOUT && netbuffPos > 0) - { - ret = drainbuf(pfd[POLL_STDOUT].fd, netbuff, netbuffPos); - if (ret == WANT_POLLOUT) - pfd[POLL_STDOUT].events = POLLOUT; - else if (ret == -1) - pfd[POLL_STDOUT].fd = -1; - /* buffer empty - remove self from polling */ - if (netbuffPos == 0) - pfd[POLL_STDOUT].events = 0; - /* buffer no longer full - poll net in again */ - if (netbuffPos < IO_BUFF_SIZE) - pfd[POLL_NETIN].events = POLLIN; - } - - /* stdin gone and queue empty? */ - if (pfd[POLL_STDIN].fd == -1 && ttybuffPos == 0) - { - pfd[POLL_NETOUT].fd = -1; - } - /* net in gone and queue empty? */ - if (pfd[POLL_NETIN].fd == -1 && netbuffPos == 0) - { - pfd[POLL_STDOUT].fd = -1; - } - } +void pingPongProcess(const LinuxSocket& sock) { + + struct pollfd pfd[4]; + const int POLL_STDIN = 0, POLL_NETOUT = 1, POLL_NETIN = 2, POLL_STDOUT = 3; + const int DEFAULT_POLL_TIMEOUT = 3000; // milisec + unsigned char netbuff[IO_BUFF_SIZE]; + size_t netbuffPos = 0; + unsigned char ttybuff[IO_BUFF_SIZE]; + size_t ttybuffPos = 0; + int n; + ssize_t ret; + + /* stdin */ + pfd[POLL_STDIN].fd = STDIN_FILENO; + pfd[POLL_STDIN].events = POLLIN; + /* network out */ + pfd[POLL_NETOUT].fd = sock; + pfd[POLL_NETOUT].events = 0; + /* network in */ + pfd[POLL_NETIN].fd = sock; + pfd[POLL_NETIN].events = POLLIN; + /* stdout */ + pfd[POLL_STDOUT].fd = STDOUT_FILENO; + pfd[POLL_STDOUT].events = 0; + + while (doContinue) { + /* both inputs are gone, buffers are empty, we are done */ + if (pfd[POLL_STDIN].fd == -1 && pfd[POLL_NETIN].fd == -1 && + ttybuffPos == 0 && netbuffPos == 0) { + return; + } + /* both outputs are gone, we can't continue */ + if (pfd[POLL_NETOUT].fd == -1 && pfd[POLL_STDOUT].fd == -1) + return; + + /* poll */ + int num_fds = poll(pfd, 4, DEFAULT_POLL_TIMEOUT); + + /* treat poll errors */ + if (num_fds == -1) { + int old_errno = errno; + if (old_errno != EINTR) { + close(sock); + EXIT_WITH_ERROR("poll returned an error" + << std::endl + << "Errno: " << std::strerror(old_errno)); + } + continue; + } + + if (num_fds == 0) { + continue; + } + + /* treat socket error conditions */ + for (n = 0; n < 4; ++n) { + if (pfd[n].revents & (POLLERR | POLLNVAL)) { + pfd[n].fd = -1; + } + } + /* reading is possible after HUP */ + if (pfd[POLL_STDIN].events & POLLIN && pfd[POLL_STDIN].revents & POLLHUP && + !(pfd[POLL_STDIN].revents & POLLIN)) { + pfd[POLL_STDIN].fd = -1; + } + + if (pfd[POLL_NETIN].events & POLLIN && pfd[POLL_NETIN].revents & POLLHUP && + !(pfd[POLL_NETIN].revents & POLLIN)) { + pfd[POLL_NETIN].fd = -1; + } + + if (pfd[POLL_NETOUT].revents & POLLHUP) { + pfd[POLL_NETOUT].fd = -1; + } + /* if HUP, stop watching stdout */ + if (pfd[POLL_STDOUT].revents & POLLHUP) + pfd[POLL_STDOUT].fd = -1; + /* if no net out, stop watching stdin */ + if (pfd[POLL_NETOUT].fd == -1) + pfd[POLL_STDIN].fd = -1; + /* if no stdout, stop watching net in */ + if (pfd[POLL_STDOUT].fd == -1) { + if (pfd[POLL_NETIN].fd != -1) + shutdown(pfd[POLL_NETIN].fd, SHUT_RD); + pfd[POLL_NETIN].fd = -1; + } + + /* try to read from stdin */ + if (pfd[POLL_STDIN].revents & POLLIN && ttybuffPos < IO_BUFF_SIZE) { + ret = fillbuf(pfd[POLL_STDIN].fd, ttybuff, ttybuffPos); + if (ret == WANT_POLLIN) + pfd[POLL_STDIN].events = POLLIN; + else if (ret == 0 || ret == -1) + pfd[POLL_STDIN].fd = -1; + /* read something - poll net out */ + if (ttybuffPos > 0) + pfd[POLL_NETOUT].events = POLLOUT; + /* filled buffer - remove self from polling */ + if (ttybuffPos == IO_BUFF_SIZE) + pfd[POLL_STDIN].events = 0; + } + /* try to write to network */ + if (pfd[POLL_NETOUT].revents & POLLOUT && ttybuffPos > 0) { + ret = drainbuf(pfd[POLL_NETOUT].fd, ttybuff, ttybuffPos); + if (ret == WANT_POLLOUT) + pfd[POLL_NETOUT].events = POLLOUT; + else if (ret == -1) + pfd[POLL_NETOUT].fd = -1; + /* buffer empty - remove self from polling */ + if (ttybuffPos == 0) + pfd[POLL_NETOUT].events = 0; + /* buffer no longer full - poll stdin again */ + if (ttybuffPos < IO_BUFF_SIZE) + pfd[POLL_STDIN].events = POLLIN; + } + /* try to read from network */ + if (pfd[POLL_NETIN].revents & POLLIN && netbuffPos < IO_BUFF_SIZE) { + ret = fillbuf(pfd[POLL_NETIN].fd, netbuff, netbuffPos); + if (ret == WANT_POLLIN) + pfd[POLL_NETIN].events = POLLIN; + else if (ret == -1) + pfd[POLL_NETIN].fd = -1; + /* eof on net in - remove from pfd */ + if (ret == 0) { + shutdown(pfd[POLL_NETIN].fd, SHUT_RD); + pfd[POLL_NETIN].fd = -1; + } + /* read something - poll stdout */ + if (netbuffPos > 0) + pfd[POLL_STDOUT].events = POLLOUT; + /* filled buffer - remove self from polling */ + if (netbuffPos == IO_BUFF_SIZE) + pfd[POLL_NETIN].events = 0; + } + /* try to write to stdout */ + if (pfd[POLL_STDOUT].revents & POLLOUT && netbuffPos > 0) { + ret = drainbuf(pfd[POLL_STDOUT].fd, netbuff, netbuffPos); + if (ret == WANT_POLLOUT) + pfd[POLL_STDOUT].events = POLLOUT; + else if (ret == -1) + pfd[POLL_STDOUT].fd = -1; + /* buffer empty - remove self from polling */ + if (netbuffPos == 0) + pfd[POLL_STDOUT].events = 0; + /* buffer no longer full - poll net in again */ + if (netbuffPos < IO_BUFF_SIZE) + pfd[POLL_NETIN].events = POLLIN; + } + + /* stdin gone and queue empty? */ + if (pfd[POLL_STDIN].fd == -1 && ttybuffPos == 0) { + pfd[POLL_NETOUT].fd = -1; + } + /* net in gone and queue empty? */ + if (pfd[POLL_NETIN].fd == -1 && netbuffPos == 0) { + pfd[POLL_STDOUT].fd = -1; + } + } } } // namespace - -extern "C" void signal_handler(int) -{ - doContinue = false; -} - +extern "C" void signal_handler(int) { doContinue = false; } /** * main method of the application */ -int main(int argc, char* argv[]) -{ - PacketStats packetStats; - std::memset(&packetStats, 0, sizeof(packetStats)); - KniPongArgs args; - std::srand(std::time(NULL)); - pcpp::AppName::init(argc, argv); - parseArgs(argc, argv, args); - pcpp::KniDevice* device = setupKniDevice(args); - LinuxSocket sock = setupLinuxSocket(args); - if (!device->startCapture(processBurst, &packetStats)) - { - close(sock); - EXIT_WITH_ERROR("Could not start capture thread on KNI device"); - } - connectUDPSocket(sock, args); - std::signal(SIGINT, signal_handler); - std::cout << "Ready for input:" << std::endl; - pingPongProcess(sock); - //! Close socket before device - close(sock); - device->stopCapture(); - device->close(); - device->stopRequestHandlerThread(); - std::cout << std::endl << std::endl - << "Packet statistics from KNI thread:" << std::endl - << " Total packets met: " << packetStats.totalPackets << std::endl - << " UDP packets met: " << packetStats.udpPacketsIn << std::endl - << " Failed PONG packets: " << packetStats.udpPacketsOutFail << std::endl - << " ARP packets met: " << packetStats.arpPacketsIn << std::endl - << " Failed ARP replay packets: " << packetStats.arpPacketsOutFail << std::endl - << std::endl; - return 0; +int main(int argc, char* argv[]) { + PacketStats packetStats; + std::memset(&packetStats, 0, sizeof(packetStats)); + KniPongArgs args; + std::srand(std::time(NULL)); + pcpp::AppName::init(argc, argv); + parseArgs(argc, argv, args); + pcpp::KniDevice* device = setupKniDevice(args); + LinuxSocket sock = setupLinuxSocket(args); + if (!device->startCapture(processBurst, &packetStats)) { + close(sock); + EXIT_WITH_ERROR("Could not start capture thread on KNI device"); + } + connectUDPSocket(sock, args); + std::signal(SIGINT, signal_handler); + std::cout << "Ready for input:" << std::endl; + pingPongProcess(sock); + //! Close socket before device + close(sock); + device->stopCapture(); + device->close(); + device->stopRequestHandlerThread(); + std::cout << std::endl + << std::endl + << "Packet statistics from KNI thread:" << std::endl + << " Total packets met: " << packetStats.totalPackets + << std::endl + << " UDP packets met: " << packetStats.udpPacketsIn + << std::endl + << " Failed PONG packets: " << packetStats.udpPacketsOutFail + << std::endl + << " ARP packets met: " << packetStats.arpPacketsIn + << std::endl + << " Failed ARP replay packets: " << packetStats.arpPacketsOutFail + << std::endl + << std::endl; + return 0; } diff --git a/Examples/PcapPlusPlus-benchmark/benchmark.cpp b/Examples/PcapPlusPlus-benchmark/benchmark.cpp index 51708e4789..13eb252e8c 100644 --- a/Examples/PcapPlusPlus-benchmark/benchmark.cpp +++ b/Examples/PcapPlusPlus-benchmark/benchmark.cpp @@ -1,111 +1,105 @@ /** * PcapPlusPlus benchmark application * ================================== - * This application is meant to run a benchmark for PcapPlusPlus as part of the "packet-capture-benchmarks" project created by - * Matias Fontanini: https://github.com/mfontanini/packet-capture-benchmarks - * The application follows the project's convention so the benchmark code is very similar to other existing benchmarks in this project - * with minor changes necessary to test and run PcapPlusPlus. - * This application currently compiles and runs on Linux only, I didn't manage to compile it on Windows with MinGW (issues related to - * to compiling a C++11 application together with WinPcap. There's probably a solution but I didn't find it yet) - * In order to run this benchmark please download packet-capture-benchmarks and compile the existing benchmarks . Then build PcapPlusPlus - * which will also build the benchmark in `/examples_bin/benchmark`. Copy this executable to `packet-capture-benchmarks/pcapplusplus` - * Then run the `benchmark.sh` script provided in `packet-capture-benchmarks` with all benchmarks you want to run. For example: + * This application is meant to run a benchmark for PcapPlusPlus as part of the + * "packet-capture-benchmarks" project created by Matias Fontanini: + * https://github.com/mfontanini/packet-capture-benchmarks The application + * follows the project's convention so the benchmark code is very similar to + * other existing benchmarks in this project with minor changes necessary to + * test and run PcapPlusPlus. This application currently compiles and runs on + * Linux only, I didn't manage to compile it on Windows with MinGW (issues + * related to to compiling a C++11 application together with WinPcap. There's + * probably a solution but I didn't find it yet) In order to run this benchmark + * please download packet-capture-benchmarks and compile the existing benchmarks + * . Then build PcapPlusPlus which will also build the benchmark in + * `/examples_bin/benchmark`. Copy this executable to + * `packet-capture-benchmarks/pcapplusplus` Then run the `benchmark.sh` script + * provided in `packet-capture-benchmarks` with all benchmarks you want to run. + * For example: * `./benchmark.sh libpcap PcapPlusPlus libtins libcrafter` */ -#include #include +#include #include -#include #include +#include +#include #include #include -#include using namespace pcpp; size_t count = 0; -bool handle_dns(Packet& packet) -{ - if (!packet.isPacketOfType(DNS)) - return true; +bool handle_dns(Packet& packet) { + if (!packet.isPacketOfType(DNS)) + return true; - DnsLayer* dnsLayer = packet.getLayerOfType(); + DnsLayer* dnsLayer = packet.getLayerOfType(); - DnsQuery* query = dnsLayer->getFirstQuery(); - while (query != nullptr) - { - count++; - query = dnsLayer->getNextQuery(query); - } + DnsQuery* query = dnsLayer->getFirstQuery(); + while (query != nullptr) { + count++; + query = dnsLayer->getNextQuery(query); + } - DnsResource* answer = dnsLayer->getFirstAnswer(); - while (answer != nullptr) - { - count++; - answer = dnsLayer->getNextAnswer(answer); - } + DnsResource* answer = dnsLayer->getFirstAnswer(); + while (answer != nullptr) { + count++; + answer = dnsLayer->getNextAnswer(answer); + } - return true; + return true; } -bool handle_packet(Packet& packet) -{ - count++; - return true; +bool handle_packet(Packet& packet) { + count++; + return true; } -int main(int argc, char *argv[]) -{ - if(argc != 4) - { - std::cout << "Usage: " << *argv << " \n"; - return 1; - } - std::string input_type(argv[2]); - int total_runs = std::stoi(argv[3]); - size_t total_packets = 0; - std::vector durations; - for(int i = 0; i < total_runs; ++i) - { - count = 0; - PcapFileReaderDevice reader(argv[1]); - reader.open(); - std::chrono::high_resolution_clock::time_point start; - if(input_type == "dns") - { - start = std::chrono::high_resolution_clock::now(); - RawPacket rawPacket; - while (reader.getNextPacket(rawPacket)) - { - Packet packet(&rawPacket); - handle_dns(packet); - } - } - else - { - start = std::chrono::high_resolution_clock::now(); - RawPacket rawPacket; - while (reader.getNextPacket(rawPacket)) - { - Packet packet(&rawPacket, pcpp::TCP); - handle_packet(packet); - } - } - auto end = std::chrono::high_resolution_clock::now(); - durations.push_back(end - start); - total_packets += count; - reader.close(); - } - auto total_time = std::accumulate( - durations.begin(), - durations.end(), - std::chrono::high_resolution_clock::duration(0) - ); +int main(int argc, char* argv[]) { + if (argc != 4) { + std::cout << "Usage: " << *argv + << " \n"; + return 1; + } + std::string input_type(argv[2]); + int total_runs = std::stoi(argv[3]); + size_t total_packets = 0; + std::vector durations; + for (int i = 0; i < total_runs; ++i) { + count = 0; + PcapFileReaderDevice reader(argv[1]); + reader.open(); + std::chrono::high_resolution_clock::time_point start; + if (input_type == "dns") { + start = std::chrono::high_resolution_clock::now(); + RawPacket rawPacket; + while (reader.getNextPacket(rawPacket)) { + Packet packet(&rawPacket); + handle_dns(packet); + } + } else { + start = std::chrono::high_resolution_clock::now(); + RawPacket rawPacket; + while (reader.getNextPacket(rawPacket)) { + Packet packet(&rawPacket, pcpp::TCP); + handle_packet(packet); + } + } + auto end = std::chrono::high_resolution_clock::now(); + durations.push_back(end - start); + total_packets += count; + reader.close(); + } + auto total_time = + std::accumulate(durations.begin(), durations.end(), + std::chrono::high_resolution_clock::duration(0)); - using std::chrono::duration_cast; - using std::chrono::milliseconds; - auto total_time_in_ms = duration_cast(total_time).count(); - std::cout << (total_packets / total_runs) << " " << (total_time_in_ms / durations.size()) << std::endl; + using std::chrono::duration_cast; + using std::chrono::milliseconds; + auto total_time_in_ms = duration_cast(total_time).count(); + std::cout << (total_packets / total_runs) << " " + << (total_time_in_ms / durations.size()) << std::endl; } diff --git a/Examples/PcapPrinter/main.cpp b/Examples/PcapPrinter/main.cpp index d263228474..72b5fabfde 100644 --- a/Examples/PcapPrinter/main.cpp +++ b/Examples/PcapPrinter/main.cpp @@ -1,338 +1,334 @@ /** * PcapPrinter application * ======================= - * This application takes a pcap or pcapng file, parses its packets using Packet++ and output each layer in each packet - * as a readable string (quite similar to the way Wireshark shows packets). - * In addition it prints a short summary of the file (with details such as file name, size, etc.) - * The result is printed to stdout (by default) or to a file (if specified). It can also print only the - * first X packets of a file + * This application takes a pcap or pcapng file, parses its packets using + * Packet++ and output each layer in each packet as a readable string (quite + * similar to the way Wireshark shows packets). In addition it prints a short + * summary of the file (with details such as file name, size, etc.) The result + * is printed to stdout (by default) or to a file (if specified). It can also + * print only the first X packets of a file * * For more details about modes of operation and parameters run PcapPrinter -h */ -#include -#include -#include -#include -#include #include #include #include +#include #include +#include #include +#include +#include +#include - -static struct option PcapPrinterOptions[] = -{ - {"output-file", required_argument, nullptr, 'o'}, - {"packet-count", required_argument, nullptr, 'c'}, - {"filter", required_argument, nullptr, 'i'}, - {"summary", no_argument, nullptr, 's'}, - {"help", no_argument, nullptr, 'h'}, - {"version", no_argument, nullptr, 'v'}, - {nullptr, 0, nullptr, 0} -}; - - -#define EXIT_WITH_ERROR(reason) do { \ - printUsage(); \ - std::cout << std::endl << "ERROR: " << reason << std::endl << std::endl; \ - exit(1); \ - } while(0) - +static struct option PcapPrinterOptions[] = { + {"output-file", required_argument, nullptr, 'o'}, + {"packet-count", required_argument, nullptr, 'c'}, + {"filter", required_argument, nullptr, 'i'}, + {"summary", no_argument, nullptr, 's'}, + {"help", no_argument, nullptr, 'h'}, + {"version", no_argument, nullptr, 'v'}, + {nullptr, 0, nullptr, 0}}; + +#define EXIT_WITH_ERROR(reason) \ + do { \ + printUsage(); \ + std::cout << std::endl \ + << "ERROR: " << reason << std::endl \ + << std::endl; \ + exit(1); \ + } while (0) /** * Print application usage */ -void printUsage() -{ - std::cout << std::endl - << "Usage:" << std::endl - << "------" << std::endl - << pcpp::AppName::get() << " pcap_file [-h] [-v] [-o output_file] [-c packet_count] [-i filter] [-s]" << std::endl - << std::endl - << "Options:" << std::endl - << std::endl - << " pcap_file : Input pcap/pcapng file name" << std::endl - << " -o output_file : Save output to text file (default output is stdout)" << std::endl - << " -c packet_count: Print only first packet_count number of packet" << std::endl - << " -i filter : Apply a BPF filter, meaning only filtered packets will be printed" << std::endl - << " -s : Print only file summary and exit" << std::endl - << " -v : Display the current version and exit" << std::endl - << " -h : Display this help message and exit" << std::endl - << std::endl; +void printUsage() { + std::cout + << std::endl + << "Usage:" << std::endl + << "------" << std::endl + << pcpp::AppName::get() + << " pcap_file [-h] [-v] [-o output_file] [-c packet_count] [-i filter] " + "[-s]" + << std::endl + << std::endl + << "Options:" << std::endl + << std::endl + << " pcap_file : Input pcap/pcapng file name" << std::endl + << " -o output_file : Save output to text file (default output is " + "stdout)" + << std::endl + << " -c packet_count: Print only first packet_count number of packet" + << std::endl + << " -i filter : Apply a BPF filter, meaning only filtered " + "packets will be printed" + << std::endl + << " -s : Print only file summary and exit" << std::endl + << " -v : Display the current version and exit" + << std::endl + << " -h : Display this help message and exit" << std::endl + << std::endl; } - /** * Print application version */ -void printAppVersion() -{ - std::cout - << pcpp::AppName::get() << " " << pcpp::getPcapPlusPlusVersionFull() << std::endl - << "Built: " << pcpp::getBuildDateTime() << std::endl - << "Built from: " << pcpp::getGitInfo() << std::endl; - exit(0); +void printAppVersion() { + std::cout << pcpp::AppName::get() << " " << pcpp::getPcapPlusPlusVersionFull() + << std::endl + << "Built: " << pcpp::getBuildDateTime() << std::endl + << "Built from: " << pcpp::getGitInfo() << std::endl; + exit(0); } - -std::string linkLayerToString(pcpp::LinkLayerType linkLayer) -{ - - if (linkLayer == pcpp::LINKTYPE_ETHERNET) - return "Ethernet"; - if (linkLayer == pcpp::LINKTYPE_IEEE802_5) - return "IEEE 802.5 Token Ring"; - else if (linkLayer == pcpp::LINKTYPE_LINUX_SLL) - return "Linux cooked capture"; - else if (linkLayer == pcpp::LINKTYPE_LINUX_SLL2) - return "Linux cooked capture v2"; - else if (linkLayer == pcpp::LINKTYPE_NULL) - return "Null/Loopback"; - else if (linkLayer == pcpp::LINKTYPE_RAW || linkLayer == pcpp::LINKTYPE_DLT_RAW1 || linkLayer == pcpp::LINKTYPE_DLT_RAW2) - { - std::ostringstream stream; - stream << "Raw IP (" << linkLayer << ")"; - return stream.str(); - } - - std::ostringstream stream; - stream << (int)linkLayer; - return stream.str(); +std::string linkLayerToString(pcpp::LinkLayerType linkLayer) { + + if (linkLayer == pcpp::LINKTYPE_ETHERNET) + return "Ethernet"; + if (linkLayer == pcpp::LINKTYPE_IEEE802_5) + return "IEEE 802.5 Token Ring"; + else if (linkLayer == pcpp::LINKTYPE_LINUX_SLL) + return "Linux cooked capture"; + else if (linkLayer == pcpp::LINKTYPE_LINUX_SLL2) + return "Linux cooked capture v2"; + else if (linkLayer == pcpp::LINKTYPE_NULL) + return "Null/Loopback"; + else if (linkLayer == pcpp::LINKTYPE_RAW || + linkLayer == pcpp::LINKTYPE_DLT_RAW1 || + linkLayer == pcpp::LINKTYPE_DLT_RAW2) { + std::ostringstream stream; + stream << "Raw IP (" << linkLayer << ")"; + return stream.str(); + } + + std::ostringstream stream; + stream << (int)linkLayer; + return stream.str(); } - /** -* print file summary based on the reader type -*/ -std::string printFileSummary(pcpp::IFileReaderDevice* reader) -{ - std::ostringstream stream; - stream << "File summary:" << std::endl; - stream << "~~~~~~~~~~~~~" << std::endl; - stream << " File name: " << reader->getFileName() << std::endl; - stream << " File size: " << reader->getFileSize() << " bytes" << std::endl; - - if (dynamic_cast(reader) != nullptr) - { - pcpp::PcapFileReaderDevice* pcapReader = dynamic_cast(reader); - pcpp::LinkLayerType linkLayer = pcapReader->getLinkLayerType(); - stream << " Link layer type: " << linkLayerToString(linkLayer) << std::endl; - } - else if (dynamic_cast(reader) != nullptr) - { - pcpp::SnoopFileReaderDevice* snoopReader = dynamic_cast(reader); - pcpp::LinkLayerType linkLayer = snoopReader->getLinkLayerType(); - stream << " Link layer type: " << linkLayerToString(linkLayer) << std::endl; - } - else if (dynamic_cast(reader) != nullptr) - { - pcpp::PcapNgFileReaderDevice* pcapNgReader = dynamic_cast(reader); - if (pcapNgReader->getOS() != "") - stream << " OS: " << pcapNgReader->getOS() << std::endl; - - if (pcapNgReader->getCaptureApplication() != "") - stream << " Capture application: " << pcapNgReader->getCaptureApplication() << std::endl; - - if (pcapNgReader->getCaptureFileComment() != "") - stream << " File comment: " << pcapNgReader->getCaptureFileComment() << std::endl; - - if (pcapNgReader->getHardware() != "") - stream << " Capture hardware: " << pcapNgReader->getHardware() << std::endl; - } - - stream << std::endl; - - return stream.str(); + * print file summary based on the reader type + */ +std::string printFileSummary(pcpp::IFileReaderDevice* reader) { + std::ostringstream stream; + stream << "File summary:" << std::endl; + stream << "~~~~~~~~~~~~~" << std::endl; + stream << " File name: " << reader->getFileName() << std::endl; + stream << " File size: " << reader->getFileSize() << " bytes" << std::endl; + + if (dynamic_cast(reader) != nullptr) { + pcpp::PcapFileReaderDevice* pcapReader = + dynamic_cast(reader); + pcpp::LinkLayerType linkLayer = pcapReader->getLinkLayerType(); + stream << " Link layer type: " << linkLayerToString(linkLayer) + << std::endl; + } else if (dynamic_cast(reader) != nullptr) { + pcpp::SnoopFileReaderDevice* snoopReader = + dynamic_cast(reader); + pcpp::LinkLayerType linkLayer = snoopReader->getLinkLayerType(); + stream << " Link layer type: " << linkLayerToString(linkLayer) + << std::endl; + } else if (dynamic_cast(reader) != nullptr) { + pcpp::PcapNgFileReaderDevice* pcapNgReader = + dynamic_cast(reader); + if (pcapNgReader->getOS() != "") + stream << " OS: " << pcapNgReader->getOS() << std::endl; + + if (pcapNgReader->getCaptureApplication() != "") + stream << " Capture application: " + << pcapNgReader->getCaptureApplication() << std::endl; + + if (pcapNgReader->getCaptureFileComment() != "") + stream << " File comment: " << pcapNgReader->getCaptureFileComment() + << std::endl; + + if (pcapNgReader->getHardware() != "") + stream << " Capture hardware: " << pcapNgReader->getHardware() + << std::endl; + } + + stream << std::endl; + + return stream.str(); } - /** -* print all requested packets in a pcap/snoop file -*/ -int printPcapPackets(pcpp::IFileReaderDevice* reader, std::ostream* out, int packetCount) -{ - // read packets from the file until end-of-file or until reached user requested packet count - int packetCountSoFar = 0; - pcpp::RawPacket rawPacket; - while (reader->getNextPacket(rawPacket) && packetCountSoFar != packetCount) - { - // parse the raw packet into a parsed packet - pcpp::Packet parsedPacket(&rawPacket); - - // print packet to string - (*out) << parsedPacket.toString() << std::endl; - - packetCountSoFar++; - } - - // return the number of packets that were printed - return packetCountSoFar; + * print all requested packets in a pcap/snoop file + */ +int printPcapPackets(pcpp::IFileReaderDevice* reader, std::ostream* out, + int packetCount) { + // read packets from the file until end-of-file or until reached user + // requested packet count + int packetCountSoFar = 0; + pcpp::RawPacket rawPacket; + while (reader->getNextPacket(rawPacket) && packetCountSoFar != packetCount) { + // parse the raw packet into a parsed packet + pcpp::Packet parsedPacket(&rawPacket); + + // print packet to string + (*out) << parsedPacket.toString() << std::endl; + + packetCountSoFar++; + } + + // return the number of packets that were printed + return packetCountSoFar; } - /** -* print all requested packets in a pcap-ng file -*/ -int printPcapNgPackets(pcpp::PcapNgFileReaderDevice* reader, std::ostream* out, int packetCount) -{ - // read packets from the file until end-of-file or until reached user requested packet count - int packetCountSoFar = 0; - pcpp::RawPacket rawPacket; - std::string packetComment = ""; - while (reader->getNextPacket(rawPacket, packetComment) && packetCountSoFar != packetCount) - { - // print packet comment if exists - if (packetComment != "") - (*out) << "Packet Comment: " << packetComment << std::endl; - - // parse the raw packet into a parsed packet - pcpp::Packet parsedPacket(&rawPacket); - - // print packet to string - (*out) << "Link layer type: " << linkLayerToString(rawPacket.getLinkLayerType()) << std::endl; - (*out) << parsedPacket.toString() << std::endl; - - packetCountSoFar++; - } - - // return the number of packets that were printed - return packetCountSoFar; + * print all requested packets in a pcap-ng file + */ +int printPcapNgPackets(pcpp::PcapNgFileReaderDevice* reader, std::ostream* out, + int packetCount) { + // read packets from the file until end-of-file or until reached user + // requested packet count + int packetCountSoFar = 0; + pcpp::RawPacket rawPacket; + std::string packetComment = ""; + while (reader->getNextPacket(rawPacket, packetComment) && + packetCountSoFar != packetCount) { + // print packet comment if exists + if (packetComment != "") + (*out) << "Packet Comment: " << packetComment << std::endl; + + // parse the raw packet into a parsed packet + pcpp::Packet parsedPacket(&rawPacket); + + // print packet to string + (*out) << "Link layer type: " + << linkLayerToString(rawPacket.getLinkLayerType()) << std::endl; + (*out) << parsedPacket.toString() << std::endl; + + packetCountSoFar++; + } + + // return the number of packets that were printed + return packetCountSoFar; } - /** * main method of this utility */ -int main(int argc, char* argv[]) -{ - pcpp::AppName::init(argc, argv); - - std::string inputPcapFileName = ""; - std::string outputPcapFileName = ""; - - std::string filter = ""; - - bool printOnlySummary = false; - - int packetCount = -1; - - int optionIndex = 0; - int opt = 0; - - while((opt = getopt_long(argc, argv, "o:c:i:svh", PcapPrinterOptions, &optionIndex)) != -1) - { - switch (opt) - { - case 0: - break; - case 'o': - outputPcapFileName = optarg; - break; - case 'c': - packetCount = atoi(optarg); - break; - case 'i': - filter = optarg; - break; - case 's': - printOnlySummary = true; - break; - case 'h': - printUsage(); - exit(0); - break; - case 'v': - printAppVersion(); - break; - default: - printUsage(); - exit(-1); - } - } - - if (optind < argc) - { - inputPcapFileName = argv[optind]; - } - - if (inputPcapFileName == "") - { - EXIT_WITH_ERROR("Input file name was not given"); - } - - // write to output file if provided, otherwise output to cout - - std::ofstream of; - std::ostream* out = &std::cout; - - if (outputPcapFileName != "") - { - of.open(outputPcapFileName.c_str()); - out = &of; - } - - // open a pcap/pcapng file for reading - pcpp::IFileReaderDevice* reader = pcpp::IFileReaderDevice::getReader(inputPcapFileName); - - if (!reader->open()) - { - delete reader; - EXIT_WITH_ERROR("Error opening input pcap file\n"); - } - - // set a filter if provided - if (filter != "") - { - if (!reader->setFilter(filter)) - { - delete reader; - EXIT_WITH_ERROR("Couldn't set filter '" << filter << "'"); - } - - } - - // print file summary - (*out) << printFileSummary(reader); - - // if requested to print only file summary - exit - if (printOnlySummary) - { - delete reader; - exit(0); - } - - int printedPacketCount = 0; - - // if the file is a pcap file - if (dynamic_cast(reader) != nullptr) - { - // print all requested packets in the pcap file - pcpp::PcapFileReaderDevice* pcapReader = dynamic_cast(reader); - printedPacketCount = printPcapPackets(pcapReader, out, packetCount); - } - else if (dynamic_cast(reader) != nullptr) - { - // print all requested packets in the pcap file - pcpp::SnoopFileReaderDevice* snoopReader = dynamic_cast(reader); - printedPacketCount = printPcapPackets(snoopReader, out, packetCount); - } - // if the file is a pcap-ng file - else if (dynamic_cast(reader) != nullptr) - { - // print all requested packets in the pcap-ng file - pcpp::PcapNgFileReaderDevice* pcapNgReader = dynamic_cast(reader); - printedPacketCount = printPcapNgPackets(pcapNgReader, out, packetCount); - } - - (*out) << "Finished. Printed " << printedPacketCount << " packets" << std::endl; - - // close the file - reader->close(); - - // free reader memory - delete reader; - - return 0; +int main(int argc, char* argv[]) { + pcpp::AppName::init(argc, argv); + + std::string inputPcapFileName = ""; + std::string outputPcapFileName = ""; + + std::string filter = ""; + + bool printOnlySummary = false; + + int packetCount = -1; + + int optionIndex = 0; + int opt = 0; + + while ((opt = getopt_long(argc, argv, "o:c:i:svh", PcapPrinterOptions, + &optionIndex)) != -1) { + switch (opt) { + case 0: + break; + case 'o': + outputPcapFileName = optarg; + break; + case 'c': + packetCount = atoi(optarg); + break; + case 'i': + filter = optarg; + break; + case 's': + printOnlySummary = true; + break; + case 'h': + printUsage(); + exit(0); + break; + case 'v': + printAppVersion(); + break; + default: + printUsage(); + exit(-1); + } + } + + if (optind < argc) { + inputPcapFileName = argv[optind]; + } + + if (inputPcapFileName == "") { + EXIT_WITH_ERROR("Input file name was not given"); + } + + // write to output file if provided, otherwise output to cout + + std::ofstream of; + std::ostream* out = &std::cout; + + if (outputPcapFileName != "") { + of.open(outputPcapFileName.c_str()); + out = &of; + } + + // open a pcap/pcapng file for reading + pcpp::IFileReaderDevice* reader = + pcpp::IFileReaderDevice::getReader(inputPcapFileName); + + if (!reader->open()) { + delete reader; + EXIT_WITH_ERROR("Error opening input pcap file\n"); + } + + // set a filter if provided + if (filter != "") { + if (!reader->setFilter(filter)) { + delete reader; + EXIT_WITH_ERROR("Couldn't set filter '" << filter << "'"); + } + } + + // print file summary + (*out) << printFileSummary(reader); + + // if requested to print only file summary - exit + if (printOnlySummary) { + delete reader; + exit(0); + } + + int printedPacketCount = 0; + + // if the file is a pcap file + if (dynamic_cast(reader) != nullptr) { + // print all requested packets in the pcap file + pcpp::PcapFileReaderDevice* pcapReader = + dynamic_cast(reader); + printedPacketCount = printPcapPackets(pcapReader, out, packetCount); + } else if (dynamic_cast(reader) != nullptr) { + // print all requested packets in the pcap file + pcpp::SnoopFileReaderDevice* snoopReader = + dynamic_cast(reader); + printedPacketCount = printPcapPackets(snoopReader, out, packetCount); + } + // if the file is a pcap-ng file + else if (dynamic_cast(reader) != nullptr) { + // print all requested packets in the pcap-ng file + pcpp::PcapNgFileReaderDevice* pcapNgReader = + dynamic_cast(reader); + printedPacketCount = printPcapNgPackets(pcapNgReader, out, packetCount); + } + + (*out) << "Finished. Printed " << printedPacketCount << " packets" + << std::endl; + + // close the file + reader->close(); + + // free reader memory + delete reader; + + return 0; } diff --git a/Examples/PcapSearch/dirent-for-Visual-Studio/include/dirent.h b/Examples/PcapSearch/dirent-for-Visual-Studio/include/dirent.h index e785d394f9..5a45532a11 100644 --- a/Examples/PcapSearch/dirent-for-Visual-Studio/include/dirent.h +++ b/Examples/PcapSearch/dirent-for-Visual-Studio/include/dirent.h @@ -15,19 +15,19 @@ * Windows Sockets 2.0. */ #ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN #endif #include -#include +#include +#include #include -#include -#include +#include #include -#include -#include +#include #include -#include +#include +#include /* Indicates that d_type field is available in dirent structure */ #define _DIRENT_HAVE_D_TYPE @@ -37,123 +37,123 @@ /* Entries missing from MSVC 6.0 */ #if !defined(FILE_ATTRIBUTE_DEVICE) -# define FILE_ATTRIBUTE_DEVICE 0x40 +#define FILE_ATTRIBUTE_DEVICE 0x40 #endif /* File type and permission flags for stat(), general mask */ #if !defined(S_IFMT) -# define S_IFMT _S_IFMT +#define S_IFMT _S_IFMT #endif /* Directory bit */ #if !defined(S_IFDIR) -# define S_IFDIR _S_IFDIR +#define S_IFDIR _S_IFDIR #endif /* Character device bit */ #if !defined(S_IFCHR) -# define S_IFCHR _S_IFCHR +#define S_IFCHR _S_IFCHR #endif /* Pipe bit */ #if !defined(S_IFFIFO) -# define S_IFFIFO _S_IFFIFO +#define S_IFFIFO _S_IFFIFO #endif /* Regular file bit */ #if !defined(S_IFREG) -# define S_IFREG _S_IFREG +#define S_IFREG _S_IFREG #endif /* Read permission */ #if !defined(S_IREAD) -# define S_IREAD _S_IREAD +#define S_IREAD _S_IREAD #endif /* Write permission */ #if !defined(S_IWRITE) -# define S_IWRITE _S_IWRITE +#define S_IWRITE _S_IWRITE #endif /* Execute permission */ #if !defined(S_IEXEC) -# define S_IEXEC _S_IEXEC +#define S_IEXEC _S_IEXEC #endif /* Pipe */ #if !defined(S_IFIFO) -# define S_IFIFO _S_IFIFO +#define S_IFIFO _S_IFIFO #endif /* Block device */ #if !defined(S_IFBLK) -# define S_IFBLK 0 +#define S_IFBLK 0 #endif /* Link */ #if !defined(S_IFLNK) -# define S_IFLNK 0 +#define S_IFLNK 0 #endif /* Socket */ #if !defined(S_IFSOCK) -# define S_IFSOCK 0 +#define S_IFSOCK 0 #endif /* Read user permission */ #if !defined(S_IRUSR) -# define S_IRUSR S_IREAD +#define S_IRUSR S_IREAD #endif /* Write user permission */ #if !defined(S_IWUSR) -# define S_IWUSR S_IWRITE +#define S_IWUSR S_IWRITE #endif /* Execute user permission */ #if !defined(S_IXUSR) -# define S_IXUSR 0 +#define S_IXUSR 0 #endif /* Read group permission */ #if !defined(S_IRGRP) -# define S_IRGRP 0 +#define S_IRGRP 0 #endif /* Write group permission */ #if !defined(S_IWGRP) -# define S_IWGRP 0 +#define S_IWGRP 0 #endif /* Execute group permission */ #if !defined(S_IXGRP) -# define S_IXGRP 0 +#define S_IXGRP 0 #endif /* Read others permission */ #if !defined(S_IROTH) -# define S_IROTH 0 +#define S_IROTH 0 #endif /* Write others permission */ #if !defined(S_IWOTH) -# define S_IWOTH 0 +#define S_IWOTH 0 #endif /* Execute others permission */ #if !defined(S_IXOTH) -# define S_IXOTH 0 +#define S_IXOTH 0 #endif /* Maximum length of file name */ #if !defined(PATH_MAX) -# define PATH_MAX MAX_PATH +#define PATH_MAX MAX_PATH #endif #if !defined(FILENAME_MAX) -# define FILENAME_MAX MAX_PATH +#define FILENAME_MAX MAX_PATH #endif #if !defined(NAME_MAX) -# define NAME_MAX FILENAME_MAX +#define NAME_MAX FILENAME_MAX #endif /* File type flags for d_type */ @@ -167,7 +167,7 @@ #define DT_LNK S_IFLNK /* Macros for converting between st_mode and d_type */ -#define IFTODT(mode) ((mode) & S_IFMT) +#define IFTODT(mode) ((mode)&S_IFMT) #define DTTOIF(type) (type) /* @@ -177,25 +177,25 @@ * on Windows. */ #if !defined(S_ISFIFO) -# define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) +#define S_ISFIFO(mode) (((mode)&S_IFMT) == S_IFIFO) #endif #if !defined(S_ISDIR) -# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#define S_ISDIR(mode) (((mode)&S_IFMT) == S_IFDIR) #endif #if !defined(S_ISREG) -# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#define S_ISREG(mode) (((mode)&S_IFMT) == S_IFREG) #endif #if !defined(S_ISLNK) -# define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) +#define S_ISLNK(mode) (((mode)&S_IFMT) == S_IFLNK) #endif #if !defined(S_ISSOCK) -# define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) +#define S_ISSOCK(mode) (((mode)&S_IFMT) == S_IFSOCK) #endif #if !defined(S_ISCHR) -# define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) +#define S_ISCHR(mode) (((mode)&S_IFMT) == S_IFCHR) #endif #if !defined(S_ISBLK) -# define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) +#define S_ISBLK(mode) (((mode)&S_IFMT) == S_IFBLK) #endif /* Return the exact length of d_namlen without zero terminator */ @@ -204,12 +204,10 @@ /* Return number of bytes needed to store d_namlen */ #define _D_ALLOC_NAMLEN(p) (PATH_MAX) - #ifdef __cplusplus extern "C" { #endif - /* Wide-character version */ struct _wdirent { /* Always zero */ @@ -243,15 +241,14 @@ struct _WDIR { HANDLE handle; /* Initial directory name */ - wchar_t *patt; + wchar_t* patt; }; typedef struct _WDIR _WDIR; -static _WDIR *_wopendir (const wchar_t *dirname); -static struct _wdirent *_wreaddir (_WDIR *dirp); -static int _wclosedir (_WDIR *dirp); -static void _wrewinddir (_WDIR* dirp); - +static _WDIR* _wopendir(const wchar_t* dirname); +static struct _wdirent* _wreaddir(_WDIR* dirp); +static int _wclosedir(_WDIR* dirp); +static void _wrewinddir(_WDIR* dirp); /* For compatibility with Symbian */ #define wdirent _wdirent @@ -261,7 +258,6 @@ static void _wrewinddir (_WDIR* dirp); #define wclosedir _wclosedir #define wrewinddir _wrewinddir - /* Multi-byte character versions */ struct dirent { /* Always zero */ @@ -283,56 +279,46 @@ typedef struct dirent dirent; struct DIR { struct dirent ent; - struct _WDIR *wdirp; + struct _WDIR* wdirp; }; typedef struct DIR DIR; -static DIR *opendir (const char *dirname); -static struct dirent *readdir (DIR *dirp); -static int closedir (DIR *dirp); -static void rewinddir (DIR* dirp); - +static DIR* opendir(const char* dirname); +static struct dirent* readdir(DIR* dirp); +static int closedir(DIR* dirp); +static void rewinddir(DIR* dirp); /* Internal utility functions */ -static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp); -static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp); +static WIN32_FIND_DATAW* dirent_first(_WDIR* dirp); +static WIN32_FIND_DATAW* dirent_next(_WDIR* dirp); -static int dirent_mbstowcs_s( - size_t *pReturnValue, - wchar_t *wcstr, - size_t sizeInWords, - const char *mbstr, - size_t count); +static int dirent_mbstowcs_s(size_t* pReturnValue, wchar_t* wcstr, + size_t sizeInWords, const char* mbstr, + size_t count); -static int dirent_wcstombs_s( - size_t *pReturnValue, - char *mbstr, - size_t sizeInBytes, - const wchar_t *wcstr, - size_t count); +static int dirent_wcstombs_s(size_t* pReturnValue, char* mbstr, + size_t sizeInBytes, const wchar_t* wcstr, + size_t count); -static void dirent_set_errno (int error); +static void dirent_set_errno(int error); /* * Open directory stream DIRNAME for read and return a pointer to the * internal working area that is used to retrieve individual directory * entries. */ -static _WDIR* -_wopendir( - const wchar_t *dirname) -{ - _WDIR *dirp = NULL; +static _WDIR* _wopendir(const wchar_t* dirname) { + _WDIR* dirp = NULL; int error; /* Must have directory name */ - if (dirname == NULL || dirname[0] == '\0') { - dirent_set_errno (ENOENT); + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno(ENOENT); return NULL; } /* Allocate new _WDIR structure */ - dirp = (_WDIR*) malloc (sizeof (struct _WDIR)); + dirp = (_WDIR*)malloc(sizeof(struct _WDIR)); if (dirp != NULL) { DWORD n; @@ -342,35 +328,35 @@ _wopendir( dirp->cached = 0; /* Compute the length of full path plus zero terminator - * - * Note that on WinRT there's no way to convert relative paths - * into absolute paths, so just assume its an absolute path. - */ -# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) - n = wcslen(dirname); -# else - n = GetFullPathNameW (dirname, 0, NULL, NULL); -# endif + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume its an absolute path. + */ +#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) + n = wcslen(dirname); +#else + n = GetFullPathNameW(dirname, 0, NULL, NULL); +#endif /* Allocate room for absolute directory name and search pattern */ - dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16); + dirp->patt = (wchar_t*)malloc(sizeof(wchar_t) * n + 16); if (dirp->patt) { /* - * Convert relative directory name to an absolute one. This - * allows rewinddir() to function correctly even when current - * working directory is changed between opendir() and rewinddir(). - * - * Note that on WinRT there's no way to convert relative paths - * into absolute paths, so just assume its an absolute path. - */ -# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) - wcsncpy_s(dirp->patt, n+1, dirname, n); -# else - n = GetFullPathNameW (dirname, n, dirp->patt, NULL); -# endif + * Convert relative directory name to an absolute one. This + * allows rewinddir() to function correctly even when current + * working directory is changed between opendir() and rewinddir(). + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume its an absolute path. + */ +#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) + wcsncpy_s(dirp->patt, n + 1, dirname, n); +#else + n = GetFullPathNameW(dirname, n, dirp->patt, NULL); +#endif if (n > 0) { - wchar_t *p; + wchar_t* p; /* Append search pattern \* to the directory name */ p = dirp->patt + n; @@ -392,18 +378,18 @@ _wopendir( *p = '\0'; /* Open directory stream and retrieve the first entry */ - if (dirent_first (dirp)) { + if (dirent_first(dirp)) { /* Directory stream opened successfully */ error = 0; } else { /* Cannot retrieve first entry */ error = 1; - dirent_set_errno (ENOENT); + dirent_set_errno(ENOENT); } } else { /* Cannot retrieve full path name */ - dirent_set_errno (ENOENT); + dirent_set_errno(ENOENT); error = 1; } @@ -418,8 +404,8 @@ _wopendir( } /* Clean up in case of error */ - if (error && dirp) { - _wclosedir (dirp); + if (error && dirp) { + _wclosedir(dirp); dirp = NULL; } @@ -432,15 +418,12 @@ _wopendir( * this function include regular files, sub-directories, pseudo-directories * "." and ".." as well as volume labels, hidden files and system files. */ -static struct _wdirent* -_wreaddir( - _WDIR *dirp) -{ - WIN32_FIND_DATAW *datap; - struct _wdirent *entp; +static struct _wdirent* _wreaddir(_WDIR* dirp) { + WIN32_FIND_DATAW* datap; + struct _wdirent* entp; /* Read next directory entry */ - datap = dirent_next (dirp); + datap = dirent_next(dirp); if (datap) { size_t n; DWORD attr; @@ -449,12 +432,12 @@ _wreaddir( entp = &dirp->ent; /* - * Copy file name as wide-character string. If the file name is too - * long to fit in to the destination buffer, then truncate file name - * to PATH_MAX characters and zero-terminate the buffer. - */ + * Copy file name as wide-character string. If the file name is too + * long to fit in to the destination buffer, then truncate file name + * to PATH_MAX characters and zero-terminate the buffer. + */ n = 0; - while (n + 1 < PATH_MAX && datap->cFileName[n] != 0) { + while (n + 1 < PATH_MAX && datap->cFileName[n] != 0) { entp->d_name[n] = datap->cFileName[n]; n++; } @@ -475,13 +458,12 @@ _wreaddir( /* Reset dummy fields */ entp->d_ino = 0; - entp->d_reclen = sizeof (struct _wdirent); + entp->d_reclen = sizeof(struct _wdirent); } else { /* Last directory entry read */ entp = NULL; - } return entp; @@ -492,33 +474,30 @@ _wreaddir( * DIR structure as well as any directory entry read previously by * _wreaddir(). */ -static int -_wclosedir( - _WDIR *dirp) -{ +static int _wclosedir(_WDIR* dirp) { int ok; if (dirp) { /* Release search handle */ if (dirp->handle != INVALID_HANDLE_VALUE) { - FindClose (dirp->handle); + FindClose(dirp->handle); dirp->handle = INVALID_HANDLE_VALUE; } /* Release search pattern */ if (dirp->patt) { - free (dirp->patt); + free(dirp->patt); dirp->patt = NULL; } /* Release directory structure */ - free (dirp); - ok = /*success*/0; + free(dirp); + ok = /*success*/ 0; } else { /* Invalid directory stream */ - dirent_set_errno (EBADF); - ok = /*failure*/-1; + dirent_set_errno(EBADF); + ok = /*failure*/ -1; } return ok; } @@ -527,32 +506,25 @@ _wclosedir( * Rewind directory stream such that _wreaddir() returns the very first * file name again. */ -static void -_wrewinddir( - _WDIR* dirp) -{ +static void _wrewinddir(_WDIR* dirp) { if (dirp) { /* Release existing search handle */ if (dirp->handle != INVALID_HANDLE_VALUE) { - FindClose (dirp->handle); + FindClose(dirp->handle); } /* Open new search handle */ - dirent_first (dirp); + dirent_first(dirp); } } /* Get first directory entry (internal) */ -static WIN32_FIND_DATAW* -dirent_first( - _WDIR *dirp) -{ - WIN32_FIND_DATAW *datap; +static WIN32_FIND_DATAW* dirent_first(_WDIR* dirp) { + WIN32_FIND_DATAW* datap; /* Open directory and retrieve the first entry */ - dirp->handle = FindFirstFileExW( - dirp->patt, FindExInfoStandard, &dirp->data, - FindExSearchNameMatch, NULL, 0); + dirp->handle = FindFirstFileExW(dirp->patt, FindExInfoStandard, &dirp->data, + FindExSearchNameMatch, NULL, 0); if (dirp->handle != INVALID_HANDLE_VALUE) { /* a directory entry is now waiting in memory */ @@ -564,17 +536,13 @@ dirent_first( /* Failed to re-open directory: no directory entry in memory */ dirp->cached = 0; datap = NULL; - } return datap; } /* Get next directory entry (internal) */ -static WIN32_FIND_DATAW* -dirent_next( - _WDIR *dirp) -{ - WIN32_FIND_DATAW *p; +static WIN32_FIND_DATAW* dirent_next(_WDIR* dirp) { + WIN32_FIND_DATAW* p; /* Get next directory entry */ if (dirp->cached != 0) { @@ -586,12 +554,12 @@ dirent_next( } else if (dirp->handle != INVALID_HANDLE_VALUE) { /* Get the next directory entry from stream */ - if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) { + if (FindNextFileW(dirp->handle, &dirp->data) != FALSE) { /* Got a file */ p = &dirp->data; } else { /* The very last entry has been processed or an error occurred */ - FindClose (dirp->handle); + FindClose(dirp->handle); dirp->handle = INVALID_HANDLE_VALUE; p = NULL; } @@ -600,7 +568,6 @@ dirent_next( /* End of directory stream reached */ p = NULL; - } return p; @@ -609,31 +576,28 @@ dirent_next( /* * Open directory stream using plain old C-string. */ -static DIR* -opendir( - const char *dirname) -{ - struct DIR *dirp; +static DIR* opendir(const char* dirname) { + struct DIR* dirp; int error; /* Must have directory name */ - if (dirname == NULL || dirname[0] == '\0') { - dirent_set_errno (ENOENT); + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno(ENOENT); return NULL; } /* Allocate memory for DIR structure */ - dirp = (DIR*) malloc (sizeof (struct DIR)); + dirp = (DIR*)malloc(sizeof(struct DIR)); if (dirp) { wchar_t wname[PATH_MAX]; size_t n; /* Convert directory name to wide-character string */ - error = dirent_mbstowcs_s (&n, wname, PATH_MAX, dirname, PATH_MAX); + error = dirent_mbstowcs_s(&n, wname, PATH_MAX, dirname, PATH_MAX); if (!error) { /* Open directory stream using wide-character name */ - dirp->wdirp = _wopendir (wname); + dirp->wdirp = _wopendir(wname); if (dirp->wdirp) { /* Directory stream opened */ error = 0; @@ -644,11 +608,11 @@ opendir( } else { /* - * Cannot convert file name to wide-character string. This - * occurs if the string contains invalid multi-byte sequences or - * the output buffer is too small to contain the resulting - * string. - */ + * Cannot convert file name to wide-character string. This + * occurs if the string contains invalid multi-byte sequences or + * the output buffer is too small to contain the resulting + * string. + */ error = 1; } @@ -658,8 +622,8 @@ opendir( } /* Clean up in case of error */ - if (error && dirp) { - free (dirp); + if (error && dirp) { + free(dirp); dirp = NULL; } @@ -679,37 +643,33 @@ opendir( * ANSI strings to the console code page so many non-ASCII characters will * display correctly. */ -static struct dirent* -readdir( - DIR *dirp) -{ - WIN32_FIND_DATAW *datap; - struct dirent *entp; +static struct dirent* readdir(DIR* dirp) { + WIN32_FIND_DATAW* datap; + struct dirent* entp; /* Read next directory entry */ - datap = dirent_next (dirp->wdirp); + datap = dirent_next(dirp->wdirp); if (datap) { size_t n; int error; /* Attempt to convert file name to multi-byte string */ - error = dirent_wcstombs_s( - &n, dirp->ent.d_name, PATH_MAX, datap->cFileName, PATH_MAX); + error = dirent_wcstombs_s(&n, dirp->ent.d_name, PATH_MAX, datap->cFileName, + PATH_MAX); /* - * If the file name cannot be represented by a multi-byte string, - * then attempt to use old 8+3 file name. This allows traditional - * Unix-code to access some file names despite of unicode - * characters, although file names may seem unfamiliar to the user. - * - * Be ware that the code below cannot come up with a short file - * name unless the file system provides one. At least - * VirtualBox shared folders fail to do this. - */ - if (error && datap->cAlternateFileName[0] != '\0') { - error = dirent_wcstombs_s( - &n, dirp->ent.d_name, PATH_MAX, - datap->cAlternateFileName, PATH_MAX); + * If the file name cannot be represented by a multi-byte string, + * then attempt to use old 8+3 file name. This allows traditional + * Unix-code to access some file names despite of unicode + * characters, although file names may seem unfamiliar to the user. + * + * Be ware that the code below cannot come up with a short file + * name unless the file system provides one. At least + * VirtualBox shared folders fail to do this. + */ + if (error && datap->cAlternateFileName[0] != '\0') { + error = dirent_wcstombs_s(&n, dirp->ent.d_name, PATH_MAX, + datap->cAlternateFileName, PATH_MAX); } if (!error) { @@ -733,15 +693,15 @@ readdir( /* Reset dummy fields */ entp->d_ino = 0; - entp->d_reclen = sizeof (struct dirent); + entp->d_reclen = sizeof(struct dirent); } else { /* - * Cannot convert file name to multi-byte string so construct - * an erroneous directory entry and return that. Note that - * we cannot return NULL as that would stop the processing - * of directory entries completely. - */ + * Cannot convert file name to multi-byte string so construct + * an erroneous directory entry and return that. Note that + * we cannot return NULL as that would stop the processing + * of directory entries completely. + */ entp = &dirp->ent; entp->d_name[0] = '?'; entp->d_name[1] = '\0'; @@ -762,26 +722,22 @@ readdir( /* * Close directory stream. */ -static int -closedir( - DIR *dirp) -{ +static int closedir(DIR* dirp) { int ok; if (dirp) { /* Close wide-character directory stream */ - ok = _wclosedir (dirp->wdirp); + ok = _wclosedir(dirp->wdirp); dirp->wdirp = NULL; /* Release multi-byte character version */ - free (dirp); + free(dirp); } else { /* Invalid directory stream */ - dirent_set_errno (EBADF); - ok = /*failure*/-1; - + dirent_set_errno(EBADF); + ok = /*failure*/ -1; } return ok; } @@ -789,29 +745,21 @@ closedir( /* * Rewind directory stream to beginning. */ -static void -rewinddir( - DIR* dirp) -{ +static void rewinddir(DIR* dirp) { /* Rewind wide-character string directory stream */ - _wrewinddir (dirp->wdirp); + _wrewinddir(dirp->wdirp); } /* Convert multi-byte string to wide character string */ -static int -dirent_mbstowcs_s( - size_t *pReturnValue, - wchar_t *wcstr, - size_t sizeInWords, - const char *mbstr, - size_t count) -{ +static int dirent_mbstowcs_s(size_t* pReturnValue, wchar_t* wcstr, + size_t sizeInWords, const char* mbstr, + size_t count) { int error; -#if defined(_MSC_VER) && _MSC_VER >= 1400 +#if defined(_MSC_VER) && _MSC_VER >= 1400 /* Microsoft Visual Studio 2005 or later */ - error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count); + error = mbstowcs_s(pReturnValue, wcstr, sizeInWords, mbstr, count); #else @@ -819,11 +767,11 @@ dirent_mbstowcs_s( size_t n; /* Convert to wide-character string (or count characters) */ - n = mbstowcs (wcstr, mbstr, sizeInWords); - if (!wcstr || n < count) { + n = mbstowcs(wcstr, mbstr, sizeInWords); + if (!wcstr || n < count) { /* Zero-terminate output buffer */ - if (wcstr && sizeInWords) { + if (wcstr && sizeInWords) { if (n >= sizeInWords) { n = sizeInWords - 1; } @@ -842,7 +790,6 @@ dirent_mbstowcs_s( /* Could not convert string */ error = 1; - } #endif @@ -851,20 +798,15 @@ dirent_mbstowcs_s( } /* Convert wide-character string to multi-byte string */ -static int -dirent_wcstombs_s( - size_t *pReturnValue, - char *mbstr, - size_t sizeInBytes, /* max size of mbstr */ - const wchar_t *wcstr, - size_t count) -{ +static int dirent_wcstombs_s(size_t* pReturnValue, char* mbstr, + size_t sizeInBytes, /* max size of mbstr */ + const wchar_t* wcstr, size_t count) { int error; -#if defined(_MSC_VER) && _MSC_VER >= 1400 +#if defined(_MSC_VER) && _MSC_VER >= 1400 /* Microsoft Visual Studio 2005 or later */ - error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count); + error = wcstombs_s(pReturnValue, mbstr, sizeInBytes, wcstr, count); #else @@ -872,11 +814,11 @@ dirent_wcstombs_s( size_t n; /* Convert to multi-byte string (or count the number of bytes needed) */ - n = wcstombs (mbstr, wcstr, sizeInBytes); - if (!mbstr || n < count) { + n = wcstombs(mbstr, wcstr, sizeInBytes); + if (!mbstr || n < count) { /* Zero-terminate output buffer */ - if (mbstr && sizeInBytes) { + if (mbstr && sizeInBytes) { if (n >= sizeInBytes) { n = sizeInBytes - 1; } @@ -895,7 +837,6 @@ dirent_wcstombs_s( /* Cannot convert string */ error = 1; - } #endif @@ -904,14 +845,11 @@ dirent_wcstombs_s( } /* Set errno variable */ -static void -dirent_set_errno( - int error) -{ -#if defined(_MSC_VER) && _MSC_VER >= 1400 +static void dirent_set_errno(int error) { +#if defined(_MSC_VER) && _MSC_VER >= 1400 /* Microsoft Visual Studio 2005 and later */ - _set_errno (error); + _set_errno(error); #else @@ -921,7 +859,6 @@ dirent_set_errno( #endif } - #ifdef __cplusplus } #endif diff --git a/Examples/PcapSearch/main.cpp b/Examples/PcapSearch/main.cpp index 1d7b7c0ed5..bef92dae3e 100644 --- a/Examples/PcapSearch/main.cpp +++ b/Examples/PcapSearch/main.cpp @@ -1,64 +1,67 @@ /** * PcapSearch application * ====================== - * This application searches all pcap and pcapng files in a given directory and all its sub-directories (unless stated otherwise) and outputs how many and which - * packets in those files match a certain pattern given by the user. The pattern is given in Berkeley Packet Filter (BPF) syntax - * (http://biot.com/capstats/bpf.html). For example: if running the application with the following parameters: - * PcapSearch.exe -d C:\ -s "ip net 1.1.1.1" -r C:\report.txt - * The application will search all '.pcap' files in all directories under C drive and try to match packets that matches IP 1.1.1.1. The result will be - * printed to stdout and a more detailed report will be printed to c:\report.txt - * Output example: - * 1 packets found in 'C:\\path\example\Dns.pcap' - * 5 packets found in 'C:\\path\example\bla1\my_pcap2.pcap' - * 7299 packets found in 'C:\\path2\example\example2\big_pcap.pcap' - * 7435 packets found in 'C:\\path3\dir1\dir2\dir3\dir4\another.pcap' - * 435 packets found in 'C:\\path3\dirx\diry\dirz\ok.pcap' - * 4662 packets found in 'C:\\path4\gotit.pcap' - * 7299 packets found in 'C:\\enough.pcap' + * This application searches all pcap and pcapng files in a given directory and + * all its sub-directories (unless stated otherwise) and outputs how many and + * which packets in those files match a certain pattern given by the user. The + * pattern is given in Berkeley Packet Filter (BPF) syntax + * (http://biot.com/capstats/bpf.html). For example: if running the application + * with the following parameters: PcapSearch.exe -d C:\ -s "ip net 1.1.1.1" -r + * C:\report.txt The application will search all '.pcap' files in all + * directories under C drive and try to match packets that matches IP 1.1.1.1. + * The result will be printed to stdout and a more detailed report will be + * printed to c:\report.txt Output example: 1 packets found in + * 'C:\\path\example\Dns.pcap' 5 packets found in + * 'C:\\path\example\bla1\my_pcap2.pcap' 7299 packets found in + * 'C:\\path2\example\example2\big_pcap.pcap' 7435 packets found in + * 'C:\\path3\dir1\dir2\dir3\dir4\another.pcap' 435 packets found in + * 'C:\\path3\dirx\diry\dirz\ok.pcap' 4662 packets found in + * 'C:\\path4\gotit.pcap' 7299 packets found in 'C:\\enough.pcap' * - * There are switches that allows the user to search only in the provided folder (without sub-directories), search user-defined file extensions (sometimes - * pcap files have an extension which is not '.pcap'), and output or not output the detailed report + * There are switches that allows the user to search only in the provided folder + * (without sub-directories), search user-defined file extensions (sometimes + * pcap files have an extension which is not '.pcap'), and output or not output + * the detailed report * - * For more details about modes of operation and parameters please run PcapSearch -h + * For more details about modes of operation and parameters please run + * PcapSearch -h */ -#include -#include +#include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include #include +#include #include -#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include - - -static struct option PcapSearchOptions[] = -{ - {"input-dir", required_argument, nullptr, 'd'}, - {"not-include-sub-dir", no_argument, nullptr, 'n'}, - {"search", required_argument, nullptr, 's'}, - {"detailed-report", required_argument, nullptr, 'r'}, - {"set-extensions", required_argument, nullptr, 'e'}, - {"version", no_argument, nullptr, 'v'}, - {"help", no_argument, nullptr, 'h'}, - {nullptr, 0, nullptr, 0} -}; - - -#define EXIT_WITH_ERROR(reason) do { \ - printUsage(); \ - std::cout << std::endl << "ERROR: " << reason << std::endl << std::endl; \ - exit(1); \ - } while(0) +static struct option PcapSearchOptions[] = { + {"input-dir", required_argument, nullptr, 'd'}, + {"not-include-sub-dir", no_argument, nullptr, 'n'}, + {"search", required_argument, nullptr, 's'}, + {"detailed-report", required_argument, nullptr, 'r'}, + {"set-extensions", required_argument, nullptr, 'e'}, + {"version", no_argument, nullptr, 'v'}, + {"help", no_argument, nullptr, 'h'}, + {nullptr, 0, nullptr, 0}}; + +#define EXIT_WITH_ERROR(reason) \ + do { \ + printUsage(); \ + std::cout << std::endl \ + << "ERROR: " << reason << std::endl \ + << std::endl; \ + exit(1); \ + } while (0) #if defined(_WIN32) #define DIR_SEPARATOR "\\" @@ -66,364 +69,371 @@ static struct option PcapSearchOptions[] = #define DIR_SEPARATOR "/" #endif - /** * Print application usage */ -void printUsage() -{ - std::cout << std::endl - << "Usage:" << std::endl - << "------" << std::endl - << pcpp::AppName::get() << " [-h] [-v] [-n] [-r file_name] [-e extension_list] -d directory -s search_criteria" << std::endl - << std::endl - << "Options:" << std::endl - << std::endl - << " -d directory : Input directory" << std::endl - << " -n : Don't include sub-directories (default is include them)" << std::endl - << " -s search_criteria : Criteria to search in Berkeley Packet Filter (BPF) syntax (http://biot.com/capstats/bpf.html)" << std::endl - << " i.e: 'ip net 1.1.1.1'" << std::endl - << " -r file_name : Write a detailed search report to a file" << std::endl - << " -e extension_list : Set file extensions to search. The default is searching '.pcap' and '.pcapng' files." << std::endl - << " extension_list should be a comma-separated list of extensions, for example: pcap,net,dmp" << std::endl - << " -v : Displays the current version and exists" << std::endl - << " -h : Displays this help message and exits" << std::endl - << std::endl; +void printUsage() { + std::cout + << std::endl + << "Usage:" << std::endl + << "------" << std::endl + << pcpp::AppName::get() + << " [-h] [-v] [-n] [-r file_name] [-e extension_list] -d directory -s " + "search_criteria" + << std::endl + << std::endl + << "Options:" << std::endl + << std::endl + << " -d directory : Input directory" << std::endl + << " -n : Don't include sub-directories (default is " + "include them)" + << std::endl + << " -s search_criteria : Criteria to search in Berkeley Packet " + "Filter (BPF) syntax (http://biot.com/capstats/bpf.html)" + << std::endl + << " i.e: 'ip net 1.1.1.1'" << std::endl + << " -r file_name : Write a detailed search report to a file" + << std::endl + << " -e extension_list : Set file extensions to search. The default " + "is searching '.pcap' and '.pcapng' files." + << std::endl + << " extension_list should be a comma-separated " + "list of extensions, for example: pcap,net,dmp" + << std::endl + << " -v : Displays the current version and exists" + << std::endl + << " -h : Displays this help message and exits" + << std::endl + << std::endl; } - /** * Print application version */ -void printAppVersion() -{ - std::cout - << pcpp::AppName::get() << " " << pcpp::getPcapPlusPlusVersionFull() << std::endl - << "Built: " << pcpp::getBuildDateTime() << std::endl - << "Built from: " << pcpp::getGitInfo() << std::endl; - exit(0); +void printAppVersion() { + std::cout << pcpp::AppName::get() << " " << pcpp::getPcapPlusPlusVersionFull() + << std::endl + << "Built: " << pcpp::getBuildDateTime() << std::endl + << "Built from: " << pcpp::getGitInfo() << std::endl; + exit(0); } - /* * Returns the extension of a given file name */ -std::string getExtension(const std::string& fileName) -{ - return fileName.substr(fileName.find_last_of(".") + 1); +std::string getExtension(const std::string& fileName) { + return fileName.substr(fileName.find_last_of(".") + 1); } - /** - * Searches all packet in a given pcap file for a certain search criteria. Returns how many packets matched the search criteria + * Searches all packet in a given pcap file for a certain search criteria. + * Returns how many packets matched the search criteria */ -int searchPcap(const std::string& pcapFilePath, std::string searchCriteria, std::ofstream* detailedReportFile) -{ - // create the pcap/pcap-ng reader - pcpp::IFileReaderDevice* reader = pcpp::IFileReaderDevice::getReader(pcapFilePath); - - // if the reader fails to open - if (!reader->open()) - { - if (detailedReportFile != nullptr) - { - // PcapPlusPlus logger saves the last internal error. Write this error to the report file - (*detailedReportFile) << "File '" << pcapFilePath << "':" << std::endl; - (*detailedReportFile) << " "; - (*detailedReportFile) << pcpp::Logger::getInstance().getLastError() << std::endl; - } - - // free the reader memory and return - delete reader; - return 0; - } - - // set the filter for the file so only packets that match the search criteria will be read - if (!reader->setFilter(std::move(searchCriteria))) - { - // free the reader memory and return - delete reader; - return 0; - } - - if (detailedReportFile != nullptr) - { - (*detailedReportFile) << "File '" << pcapFilePath << "':" << std::endl; - } - - int packetCount = 0; - pcpp::RawPacket rawPacket; - - // read packets from the file. Since we already set the filter, only packets that matches the filter will be read - while (reader->getNextPacket(rawPacket)) - { - // if a detailed report is required, parse the packet and print it to the report file - if (detailedReportFile != nullptr) - { - // parse the packet - pcpp::Packet parsedPacket(&rawPacket); - - // print layer by layer by layer as we want to add a few spaces before each layer - std::vector packetLayers; - parsedPacket.toStringList(packetLayers); - for (std::vector::iterator iter = packetLayers.begin(); iter != packetLayers.end(); iter++) - (*detailedReportFile) << "\n " << (*iter); - (*detailedReportFile) << std::endl; - } - - // count the packet read - packetCount++; - } - - // close the reader file - reader->close(); - - // finalize the report - if (detailedReportFile != nullptr) - { - if (packetCount > 0) - (*detailedReportFile) << "\n"; - - (*detailedReportFile) << " ----> Found " << packetCount << " packets" << std::endl << std::endl; - - } - - // free the reader memory - delete reader; - - // return how many packets matched the search criteria - return packetCount; +int searchPcap(const std::string& pcapFilePath, std::string searchCriteria, + std::ofstream* detailedReportFile) { + // create the pcap/pcap-ng reader + pcpp::IFileReaderDevice* reader = + pcpp::IFileReaderDevice::getReader(pcapFilePath); + + // if the reader fails to open + if (!reader->open()) { + if (detailedReportFile != nullptr) { + // PcapPlusPlus logger saves the last internal error. Write this error to + // the report file + (*detailedReportFile) << "File '" << pcapFilePath << "':" << std::endl; + (*detailedReportFile) << " "; + (*detailedReportFile) + << pcpp::Logger::getInstance().getLastError() << std::endl; + } + + // free the reader memory and return + delete reader; + return 0; + } + + // set the filter for the file so only packets that match the search criteria + // will be read + if (!reader->setFilter(std::move(searchCriteria))) { + // free the reader memory and return + delete reader; + return 0; + } + + if (detailedReportFile != nullptr) { + (*detailedReportFile) << "File '" << pcapFilePath << "':" << std::endl; + } + + int packetCount = 0; + pcpp::RawPacket rawPacket; + + // read packets from the file. Since we already set the filter, only packets + // that matches the filter will be read + while (reader->getNextPacket(rawPacket)) { + // if a detailed report is required, parse the packet and print it to the + // report file + if (detailedReportFile != nullptr) { + // parse the packet + pcpp::Packet parsedPacket(&rawPacket); + + // print layer by layer by layer as we want to add a few spaces before + // each layer + std::vector packetLayers; + parsedPacket.toStringList(packetLayers); + for (std::vector::iterator iter = packetLayers.begin(); + iter != packetLayers.end(); iter++) + (*detailedReportFile) << "\n " << (*iter); + (*detailedReportFile) << std::endl; + } + + // count the packet read + packetCount++; + } + + // close the reader file + reader->close(); + + // finalize the report + if (detailedReportFile != nullptr) { + if (packetCount > 0) + (*detailedReportFile) << "\n"; + + (*detailedReportFile) << " ----> Found " << packetCount << " packets" + << std::endl + << std::endl; + } + + // free the reader memory + delete reader; + + // return how many packets matched the search criteria + return packetCount; } - /** - * Searches all pcap files in given directory (and sub-directories if directed by the user) and output how many packets in each file matches a given - * search criteria. This method outputs how many directories were searched, how many files were searched and how many packets were matched + * Searches all pcap files in given directory (and sub-directories if directed + * by the user) and output how many packets in each file matches a given search + * criteria. This method outputs how many directories were searched, how many + * files were searched and how many packets were matched */ -void searchDirectories(const std::string &directory, bool includeSubDirectories, const std::string &searchCriteria, std::ofstream* detailedReportFile, - std::map extensionsToSearch, - int& totalDirSearched, int& totalFilesSearched, int& totalPacketsFound) -{ - // open the directory - DIR *dir = opendir(directory.c_str()); - - // dir is null usually when user has no access permissions - if (dir == nullptr) - return; - - struct dirent *entry = readdir(dir); - - std::vector pcapList; - - // go over all files in this directory - while (entry != nullptr) - { - std::string name(entry->d_name); - - // construct directory full path - std::string dirPath = directory; - std::string dirSep = DIR_SEPARATOR; - if (0 != directory.compare(directory.length() - dirSep.length(), dirSep.length(), dirSep)) // directory doesn't contain separator in the end - dirPath += DIR_SEPARATOR; - dirPath += name; - - struct stat info; - - // get file attributes - if (stat(dirPath.c_str(), &info) != 0) - { - entry = readdir(dir); - continue; - } - - // if the file is not a directory - if (!(info.st_mode & S_IFDIR)) - { - // check if the file extension matches the requested extensions to search. If it does, put the file name in a list of files - // that should be searched (don't do the search just yet) - if (extensionsToSearch.find(getExtension(name)) != extensionsToSearch.end()) - pcapList.push_back(dirPath); - entry = readdir(dir); - continue; - } - - // if the file is a '.' or '..' skip it - if (name == "." || name == "..") - { - entry = readdir(dir); - continue; - } - - // if we got to here it means the file is actually a directory. If required to search sub-directories, call this method recursively to search - // inside this sub-directory - if (includeSubDirectories) - searchDirectories(dirPath, true, searchCriteria, detailedReportFile, extensionsToSearch, totalDirSearched, totalFilesSearched, totalPacketsFound); - - // move to the next file - entry = readdir(dir); - } - - // close dir - closedir(dir); - - totalDirSearched++; - - // when we get to here we already covered all sub-directories and collected all the files in this directory that are required for search - // go over each such file and search its packets to find the search criteria - for (std::vector::iterator iter = pcapList.begin(); iter != pcapList.end(); iter++) - { - // do the actual search - int packetsFound = searchPcap(*iter, searchCriteria, detailedReportFile); - - // add to total matched packets - totalFilesSearched++; - if (packetsFound > 0) - { - std::cout << packetsFound << " packets found in '" << *iter << "'" << std::endl; - totalPacketsFound += packetsFound; - } - } +void searchDirectories(const std::string& directory, bool includeSubDirectories, + const std::string& searchCriteria, + std::ofstream* detailedReportFile, + std::map extensionsToSearch, + int& totalDirSearched, int& totalFilesSearched, + int& totalPacketsFound) { + // open the directory + DIR* dir = opendir(directory.c_str()); + + // dir is null usually when user has no access permissions + if (dir == nullptr) + return; + + struct dirent* entry = readdir(dir); + + std::vector pcapList; + + // go over all files in this directory + while (entry != nullptr) { + std::string name(entry->d_name); + + // construct directory full path + std::string dirPath = directory; + std::string dirSep = DIR_SEPARATOR; + if (0 != directory.compare( + directory.length() - dirSep.length(), dirSep.length(), + dirSep)) // directory doesn't contain separator in the end + dirPath += DIR_SEPARATOR; + dirPath += name; + + struct stat info; + + // get file attributes + if (stat(dirPath.c_str(), &info) != 0) { + entry = readdir(dir); + continue; + } + + // if the file is not a directory + if (!(info.st_mode & S_IFDIR)) { + // check if the file extension matches the requested extensions to search. + // If it does, put the file name in a list of files that should be + // searched (don't do the search just yet) + if (extensionsToSearch.find(getExtension(name)) != + extensionsToSearch.end()) + pcapList.push_back(dirPath); + entry = readdir(dir); + continue; + } + + // if the file is a '.' or '..' skip it + if (name == "." || name == "..") { + entry = readdir(dir); + continue; + } + + // if we got to here it means the file is actually a directory. If required + // to search sub-directories, call this method recursively to search inside + // this sub-directory + if (includeSubDirectories) + searchDirectories(dirPath, true, searchCriteria, detailedReportFile, + extensionsToSearch, totalDirSearched, + totalFilesSearched, totalPacketsFound); + + // move to the next file + entry = readdir(dir); + } + + // close dir + closedir(dir); + + totalDirSearched++; + + // when we get to here we already covered all sub-directories and collected + // all the files in this directory that are required for search go over each + // such file and search its packets to find the search criteria + for (std::vector::iterator iter = pcapList.begin(); + iter != pcapList.end(); iter++) { + // do the actual search + int packetsFound = searchPcap(*iter, searchCriteria, detailedReportFile); + + // add to total matched packets + totalFilesSearched++; + if (packetsFound > 0) { + std::cout << packetsFound << " packets found in '" << *iter << "'" + << std::endl; + totalPacketsFound += packetsFound; + } + } } - - /** * main method of this utility */ -int main(int argc, char* argv[]) -{ - pcpp::AppName::init(argc, argv); - - std::string inputDirectory = ""; - - std::string searchCriteria = ""; - - bool includeSubDirectories = true; - - std::string detailedReportFileName = ""; - - std::map extensionsToSearch; - - // the default (unless set otherwise) is to search in '.pcap' and '.pcapng' extensions - extensionsToSearch["pcap"] = true; - extensionsToSearch["pcapng"] = true; - - int optionIndex = 0; - int opt = 0; - - while((opt = getopt_long(argc, argv, "d:s:r:e:hvn", PcapSearchOptions, &optionIndex)) != -1) - { - switch (opt) - { - case 0: - break; - case 'd': - inputDirectory = optarg; - break; - case 'n': - includeSubDirectories = false; - break; - case 's': - searchCriteria = optarg; - break; - case 'r': - detailedReportFileName = optarg; - break; - case 'e': - { - // read the extension list into the map - extensionsToSearch.clear(); - std::string extensionsListAsString = std::string(optarg); - std::stringstream stream(extensionsListAsString); - std::string extension; - // break comma-separated string into string list - while(std::getline(stream, extension, ',')) - { - // add the extension into the map if it doesn't already exist - if (extensionsToSearch.find(extension) == extensionsToSearch.end()) - extensionsToSearch[extension] = true; - } - - // verify list is not empty - if (extensionsToSearch.empty()) - { - EXIT_WITH_ERROR("Couldn't parse extensions list"); - } - break; - } - case 'h': - printUsage(); - exit(0); - case 'v': - printAppVersion(); - break; - default: - printUsage(); - exit(-1); - } - } - - if (inputDirectory == "") - { - EXIT_WITH_ERROR("Input directory was not given"); - } - - if (searchCriteria == "") - { - EXIT_WITH_ERROR("Search criteria was not given"); - } - - DIR *dir = opendir(inputDirectory.c_str()); - if (dir == nullptr) - { - EXIT_WITH_ERROR("Cannot find or open input directory"); - } - closedir(dir); - - // verify the search criteria is a valid BPF filter - pcpp::BPFStringFilter filter(searchCriteria); - if(!filter.verifyFilter()) - { - EXIT_WITH_ERROR("Search criteria isn't valid"); - } - - // open the detailed report file if requested by the user - std::ofstream* detailedReportFile = nullptr; - if (detailedReportFileName != "") - { - detailedReportFile = new std::ofstream(); - detailedReportFile->open(detailedReportFileName.c_str()); - if (detailedReportFile->fail()) - { - EXIT_WITH_ERROR("Couldn't open detailed report file '" << detailedReportFileName << "' for writing"); - } - } - - - std::cout << "Searching..." << std::endl; - int totalDirSearched = 0; - int totalFilesSearched = 0; - int totalPacketsFound = 0; - - // the main call - start searching! - searchDirectories(inputDirectory, includeSubDirectories, searchCriteria, detailedReportFile, extensionsToSearch, totalDirSearched, totalFilesSearched, totalPacketsFound); - - // after search is done, close the report file and delete its instance - std::cout << std::endl << std::endl - << "Done! Searched " - << totalFilesSearched << " files in " - << totalDirSearched << " directories, " - << totalPacketsFound << " packets were matched to search criteria" - << std::endl; - - if (detailedReportFile != nullptr) - { - if (detailedReportFile->is_open()) - detailedReportFile->close(); - - delete detailedReportFile; - std::cout << "Detailed report written to '" << detailedReportFileName << "'" << std::endl; - } - - return 0; +int main(int argc, char* argv[]) { + pcpp::AppName::init(argc, argv); + + std::string inputDirectory = ""; + + std::string searchCriteria = ""; + + bool includeSubDirectories = true; + + std::string detailedReportFileName = ""; + + std::map extensionsToSearch; + + // the default (unless set otherwise) is to search in '.pcap' and '.pcapng' + // extensions + extensionsToSearch["pcap"] = true; + extensionsToSearch["pcapng"] = true; + + int optionIndex = 0; + int opt = 0; + + while ((opt = getopt_long(argc, argv, "d:s:r:e:hvn", PcapSearchOptions, + &optionIndex)) != -1) { + switch (opt) { + case 0: + break; + case 'd': + inputDirectory = optarg; + break; + case 'n': + includeSubDirectories = false; + break; + case 's': + searchCriteria = optarg; + break; + case 'r': + detailedReportFileName = optarg; + break; + case 'e': { + // read the extension list into the map + extensionsToSearch.clear(); + std::string extensionsListAsString = std::string(optarg); + std::stringstream stream(extensionsListAsString); + std::string extension; + // break comma-separated string into string list + while (std::getline(stream, extension, ',')) { + // add the extension into the map if it doesn't already exist + if (extensionsToSearch.find(extension) == extensionsToSearch.end()) + extensionsToSearch[extension] = true; + } + + // verify list is not empty + if (extensionsToSearch.empty()) { + EXIT_WITH_ERROR("Couldn't parse extensions list"); + } + break; + } + case 'h': + printUsage(); + exit(0); + case 'v': + printAppVersion(); + break; + default: + printUsage(); + exit(-1); + } + } + + if (inputDirectory == "") { + EXIT_WITH_ERROR("Input directory was not given"); + } + + if (searchCriteria == "") { + EXIT_WITH_ERROR("Search criteria was not given"); + } + + DIR* dir = opendir(inputDirectory.c_str()); + if (dir == nullptr) { + EXIT_WITH_ERROR("Cannot find or open input directory"); + } + closedir(dir); + + // verify the search criteria is a valid BPF filter + pcpp::BPFStringFilter filter(searchCriteria); + if (!filter.verifyFilter()) { + EXIT_WITH_ERROR("Search criteria isn't valid"); + } + + // open the detailed report file if requested by the user + std::ofstream* detailedReportFile = nullptr; + if (detailedReportFileName != "") { + detailedReportFile = new std::ofstream(); + detailedReportFile->open(detailedReportFileName.c_str()); + if (detailedReportFile->fail()) { + EXIT_WITH_ERROR("Couldn't open detailed report file '" + << detailedReportFileName << "' for writing"); + } + } + + std::cout << "Searching..." << std::endl; + int totalDirSearched = 0; + int totalFilesSearched = 0; + int totalPacketsFound = 0; + + // the main call - start searching! + searchDirectories(inputDirectory, includeSubDirectories, searchCriteria, + detailedReportFile, extensionsToSearch, totalDirSearched, + totalFilesSearched, totalPacketsFound); + + // after search is done, close the report file and delete its instance + std::cout << std::endl + << std::endl + << "Done! Searched " << totalFilesSearched << " files in " + << totalDirSearched << " directories, " << totalPacketsFound + << " packets were matched to search criteria" << std::endl; + + if (detailedReportFile != nullptr) { + if (detailedReportFile->is_open()) + detailedReportFile->close(); + + delete detailedReportFile; + std::cout << "Detailed report written to '" << detailedReportFileName << "'" + << std::endl; + } + + return 0; } diff --git a/Examples/PcapSplitter/ConnectionSplitters.h b/Examples/PcapSplitter/ConnectionSplitters.h index 0c326f91ae..3a953bb182 100644 --- a/Examples/PcapSplitter/ConnectionSplitters.h +++ b/Examples/PcapSplitter/ConnectionSplitters.h @@ -2,215 +2,201 @@ #include "Splitters.h" - /** * Splits a pcap file by 2-tuple (IP src and IP dst). Works for IPv4 & IPv6. * All packets that aren't IPv4 or IPv6 will be placed in one file. - * If the user wants to limit the number of files, the splitter will divide the 2-tuple connections equally between the - * files. If no limit is set then each connection will be written to a separate file + * If the user wants to limit the number of files, the splitter will divide the + * 2-tuple connections equally between the files. If no limit is set then each + * connection will be written to a separate file */ -class TwoTupleSplitter : public ValueBasedSplitter -{ -public: - - /** - * A c'tor for this class that gets the maximum number of files. If this number is lower or equal to 0 it's - * considered not to have a file count limit - */ - explicit TwoTupleSplitter(int maxFiles) : ValueBasedSplitter(maxFiles) {} - - /** - * Find the 2-tuple flow for this packet and get the file number it belongs to. If flow is new, return a new file number - */ - int getFileNumber(pcpp::Packet& packet, std::vector& filesToClose) - { - // hash the 2-tuple and look for it in the flow table - uint32_t hash = pcpp::hash2Tuple(&packet); - - // if flow isn't found in the flow table - if (m_FlowTable.find(hash) == m_FlowTable.end()) - { - // create a new entry and get a new file number for it - m_FlowTable[hash] = getNextFileNumber(filesToClose); - } - else // flow is found in the 2-tuple flow table - { - // indicate file is being written because this file may not be in the LRU list (and hence closed), - // so we need to put it there, open it, and maybe close another file - writingToFile(m_FlowTable[hash], filesToClose); - } - - return m_FlowTable[hash]; - } +class TwoTupleSplitter : public ValueBasedSplitter { + public: + /** + * A c'tor for this class that gets the maximum number of files. If this + * number is lower or equal to 0 it's considered not to have a file count + * limit + */ + explicit TwoTupleSplitter(int maxFiles) : ValueBasedSplitter(maxFiles) {} + + /** + * Find the 2-tuple flow for this packet and get the file number it belongs + * to. If flow is new, return a new file number + */ + int getFileNumber(pcpp::Packet& packet, std::vector& filesToClose) { + // hash the 2-tuple and look for it in the flow table + uint32_t hash = pcpp::hash2Tuple(&packet); + + // if flow isn't found in the flow table + if (m_FlowTable.find(hash) == m_FlowTable.end()) { + // create a new entry and get a new file number for it + m_FlowTable[hash] = getNextFileNumber(filesToClose); + } else // flow is found in the 2-tuple flow table + { + // indicate file is being written because this file may not be in the LRU + // list (and hence closed), so we need to put it there, open it, and maybe + // close another file + writingToFile(m_FlowTable[hash], filesToClose); + } + + return m_FlowTable[hash]; + } }; - /** - * Splits a pcap file by connection (IP src + IP dst + port src + port dst + protocol) - * Works for IPv4, IPv6, TCP and UDP. - * All packets that aren't IPv4/IPv6 or TCP/UDP will be placed in one file. - * If the user wants to limit the number of files, the splitter will divide the connections equally between the - * files. If no limit is set then each connection will be written to a separate file + * Splits a pcap file by connection (IP src + IP dst + port src + port dst + + * protocol) Works for IPv4, IPv6, TCP and UDP. All packets that aren't + * IPv4/IPv6 or TCP/UDP will be placed in one file. If the user wants to limit + * the number of files, the splitter will divide the connections equally between + * the files. If no limit is set then each connection will be written to a + * separate file */ -class FiveTupleSplitter : public ValueBasedSplitter -{ -private: - - // a flow table for saving TCP state per flow. Currently the only data that is saved is whether - // the last packet seen on the flow was a TCP SYN packet - std::map m_TcpFlowTable; - - /** - * A utility method that takes a packet and returns true if it's a TCP SYN packet - */ - bool isTcpSyn(pcpp::Packet& packet) - { - if (packet.isPacketOfType(pcpp::TCP)) - { - // extract the TCP layer - pcpp::TcpLayer* tcpLayer = packet.getLayerOfType(); - - // extract SYN and ACK flags - bool isSyn = (tcpLayer->getTcpHeader()->synFlag == 1); - bool isNotAck = (tcpLayer->getTcpHeader()->ackFlag == 0); - - // return true only if it's a pure SYN packet (and not SYN/ACK) - return (isSyn && isNotAck); - } - - return false; - } - -public: - - /** - * A c'tor for this class that gets the maximum number of files. If this number is lower or equal to 0 it's - * considered not to have a file count limit - */ - explicit FiveTupleSplitter(int maxFiles) : ValueBasedSplitter(maxFiles) {} - - /** - * Find the flow for this packet and get the file number it belongs to. If flow is new, return a new file number - */ - int getFileNumber(pcpp::Packet& packet, std::vector& filesToClose) - { - // hash the 5-tuple and look for it in the flow table - uint32_t hash = pcpp::hash5Tuple(&packet); - - // if flow isn't found in the flow table - if (m_FlowTable.find(hash) == m_FlowTable.end()) - { - // create a new entry and get a new file number for it - m_FlowTable[hash] = getNextFileNumber(filesToClose); - - // if this is s a TCP packet check whether it's a SYN packet - // and save this data in the TCP flow table - if (packet.isPacketOfType(pcpp::TCP)) - { - m_TcpFlowTable[hash] = isTcpSyn(packet); - } - } - else // flow is found in the flow table - { - if (packet.isPacketOfType(pcpp::TCP)) - { - // if this is a TCP flow, check if this is a SYN packet - bool isSyn = isTcpSyn(packet); - - // if this is a SYN packet it means this is a beginning of a new flow - //(with the same 5-tuple as the previous one), so assign a new file number to it. - // unless the last packet was also SYN, which is an indication of SYN retransmission. - // In this case don't assign a new file number - if (isSyn && m_TcpFlowTable.find(hash) != m_TcpFlowTable.end() && m_TcpFlowTable[hash] == false) - { - m_FlowTable[hash] = getNextFileNumber(filesToClose); - } - else - { - // indicate file is being written because this file may not be in the LRU list (and hence closed), - // so we need to put it there, open it, and maybe close another file - writingToFile(m_FlowTable[hash], filesToClose); - } - - // update the TCP flow table - m_TcpFlowTable[hash] = isSyn; - } - else - { - // indicate file is being written because this file may not be in the LRU list (and hence closed), - // so we need to put it there, open it, and maybe close another file - writingToFile(m_FlowTable[hash], filesToClose); - } - } - - return m_FlowTable[hash]; - } - - void updateStringStream(std::ostringstream & sstream, const std::string & srcIp, uint16_t srcPort, const std::string & dstIp, uint16_t dstPort) - { - sstream << hyphenIP(srcIp) - << "_" - << srcPort - << "-" - << hyphenIP(dstIp) - << "_" - << dstPort; - } - - /** - * Re-implement Splitter's getFileName() method, this time with the IPs/Ports/protocol value - */ - std::string getFileName(pcpp::Packet& packet, const std::string &outputPcapBasePath, int fileNumber) - { - std::ostringstream sstream; - - // if it's not a TCP or UDP packet, put it in file #0 - if (!packet.isPacketOfType(pcpp::TCP) && !packet.isPacketOfType(pcpp::UDP)) - { - return Splitter::getFileName(packet, outputPcapBasePath, fileNumber); - } - - sstream << "connection-"; - - if (packet.isPacketOfType(pcpp::TCP)) - { - // extract TCP layer - pcpp::TcpLayer* tcpLayer = packet.getLayerOfType(); - if (tcpLayer != nullptr) - { - uint16_t srcPort = tcpLayer->getSrcPort(); - uint16_t dstPort = tcpLayer->getDstPort(); - - sstream << "tcp_"; - - if ((tcpLayer->getTcpHeader()->synFlag == 1) && (tcpLayer->getTcpHeader()->ackFlag == 0)) - { - updateStringStream(sstream, getSrcIPString(packet), srcPort, getDstIPString(packet), dstPort); - } else if (((tcpLayer->getTcpHeader()->synFlag == 1) && - (tcpLayer->getTcpHeader()->ackFlag == 1) - ) || (srcPort < dstPort) ) - { - updateStringStream(sstream, getDstIPString(packet), dstPort, getSrcIPString(packet), srcPort); - } else - { - updateStringStream(sstream, getSrcIPString(packet), srcPort, getDstIPString(packet), dstPort); - } - return outputPcapBasePath + sstream.str(); - } - } - else if (packet.isPacketOfType(pcpp::UDP)) - { - // for UDP packets, decide the server port by the lower port - pcpp::UdpLayer* udpLayer = packet.getLayerOfType(); - if (udpLayer != nullptr) - { - sstream << "udp_"; - updateStringStream(sstream, getSrcIPString(packet), udpLayer->getSrcPort(), getDstIPString(packet), udpLayer->getDstPort()); - return outputPcapBasePath + sstream.str(); - } - } - - // if reached here, return 'miscellaneous' - return outputPcapBasePath + "miscellaneous"; - } +class FiveTupleSplitter : public ValueBasedSplitter { + private: + // a flow table for saving TCP state per flow. Currently the only data that is + // saved is whether the last packet seen on the flow was a TCP SYN packet + std::map m_TcpFlowTable; + + /** + * A utility method that takes a packet and returns true if it's a TCP SYN + * packet + */ + bool isTcpSyn(pcpp::Packet& packet) { + if (packet.isPacketOfType(pcpp::TCP)) { + // extract the TCP layer + pcpp::TcpLayer* tcpLayer = packet.getLayerOfType(); + + // extract SYN and ACK flags + bool isSyn = (tcpLayer->getTcpHeader()->synFlag == 1); + bool isNotAck = (tcpLayer->getTcpHeader()->ackFlag == 0); + + // return true only if it's a pure SYN packet (and not SYN/ACK) + return (isSyn && isNotAck); + } + + return false; + } + + public: + /** + * A c'tor for this class that gets the maximum number of files. If this + * number is lower or equal to 0 it's considered not to have a file count + * limit + */ + explicit FiveTupleSplitter(int maxFiles) : ValueBasedSplitter(maxFiles) {} + + /** + * Find the flow for this packet and get the file number it belongs to. If + * flow is new, return a new file number + */ + int getFileNumber(pcpp::Packet& packet, std::vector& filesToClose) { + // hash the 5-tuple and look for it in the flow table + uint32_t hash = pcpp::hash5Tuple(&packet); + + // if flow isn't found in the flow table + if (m_FlowTable.find(hash) == m_FlowTable.end()) { + // create a new entry and get a new file number for it + m_FlowTable[hash] = getNextFileNumber(filesToClose); + + // if this is s a TCP packet check whether it's a SYN packet + // and save this data in the TCP flow table + if (packet.isPacketOfType(pcpp::TCP)) { + m_TcpFlowTable[hash] = isTcpSyn(packet); + } + } else // flow is found in the flow table + { + if (packet.isPacketOfType(pcpp::TCP)) { + // if this is a TCP flow, check if this is a SYN packet + bool isSyn = isTcpSyn(packet); + + // if this is a SYN packet it means this is a beginning of a new flow + //(with the same 5-tuple as the previous one), so assign a new file + //number to it. + // unless the last packet was also SYN, which is an indication of SYN + // retransmission. In this case don't assign a new file number + if (isSyn && m_TcpFlowTable.find(hash) != m_TcpFlowTable.end() && + m_TcpFlowTable[hash] == false) { + m_FlowTable[hash] = getNextFileNumber(filesToClose); + } else { + // indicate file is being written because this file may not be in the + // LRU list (and hence closed), so we need to put it there, open it, + // and maybe close another file + writingToFile(m_FlowTable[hash], filesToClose); + } + + // update the TCP flow table + m_TcpFlowTable[hash] = isSyn; + } else { + // indicate file is being written because this file may not be in the + // LRU list (and hence closed), so we need to put it there, open it, and + // maybe close another file + writingToFile(m_FlowTable[hash], filesToClose); + } + } + + return m_FlowTable[hash]; + } + + void updateStringStream(std::ostringstream& sstream, const std::string& srcIp, + uint16_t srcPort, const std::string& dstIp, + uint16_t dstPort) { + sstream << hyphenIP(srcIp) << "_" << srcPort << "-" << hyphenIP(dstIp) + << "_" << dstPort; + } + + /** + * Re-implement Splitter's getFileName() method, this time with the + * IPs/Ports/protocol value + */ + std::string getFileName(pcpp::Packet& packet, + const std::string& outputPcapBasePath, + int fileNumber) { + std::ostringstream sstream; + + // if it's not a TCP or UDP packet, put it in file #0 + if (!packet.isPacketOfType(pcpp::TCP) && + !packet.isPacketOfType(pcpp::UDP)) { + return Splitter::getFileName(packet, outputPcapBasePath, fileNumber); + } + + sstream << "connection-"; + + if (packet.isPacketOfType(pcpp::TCP)) { + // extract TCP layer + pcpp::TcpLayer* tcpLayer = packet.getLayerOfType(); + if (tcpLayer != nullptr) { + uint16_t srcPort = tcpLayer->getSrcPort(); + uint16_t dstPort = tcpLayer->getDstPort(); + + sstream << "tcp_"; + + if ((tcpLayer->getTcpHeader()->synFlag == 1) && + (tcpLayer->getTcpHeader()->ackFlag == 0)) { + updateStringStream(sstream, getSrcIPString(packet), srcPort, + getDstIPString(packet), dstPort); + } else if (((tcpLayer->getTcpHeader()->synFlag == 1) && + (tcpLayer->getTcpHeader()->ackFlag == 1)) || + (srcPort < dstPort)) { + updateStringStream(sstream, getDstIPString(packet), dstPort, + getSrcIPString(packet), srcPort); + } else { + updateStringStream(sstream, getSrcIPString(packet), srcPort, + getDstIPString(packet), dstPort); + } + return outputPcapBasePath + sstream.str(); + } + } else if (packet.isPacketOfType(pcpp::UDP)) { + // for UDP packets, decide the server port by the lower port + pcpp::UdpLayer* udpLayer = packet.getLayerOfType(); + if (udpLayer != nullptr) { + sstream << "udp_"; + updateStringStream(sstream, getSrcIPString(packet), + udpLayer->getSrcPort(), getDstIPString(packet), + udpLayer->getDstPort()); + return outputPcapBasePath + sstream.str(); + } + } + + // if reached here, return 'miscellaneous' + return outputPcapBasePath + "miscellaneous"; + } }; diff --git a/Examples/PcapSplitter/IPPortSplitters.h b/Examples/PcapSplitter/IPPortSplitters.h index 6a8d23d0cb..17876f29da 100644 --- a/Examples/PcapSplitter/IPPortSplitters.h +++ b/Examples/PcapSplitter/IPPortSplitters.h @@ -1,528 +1,557 @@ #pragma once -#include "Splitters.h" #include "PacketUtils.h" +#include "Splitters.h" #include "SystemUtils.h" - /** - * A virtual abstract class for all splitters that split files by IP address or TCP/UDP port. Inherits from ValueBasedSplitter, - * so it already contains a mapping of IP/port to file number, a flow table, and supports max number of files or undefined - * number of files. This class arranges packets by TCP/UDP flows and for each flow lets the inherited classes determine - * to which file number this flow will be matched + * A virtual abstract class for all splitters that split files by IP address or + * TCP/UDP port. Inherits from ValueBasedSplitter, so it already contains a + * mapping of IP/port to file number, a flow table, and supports max number of + * files or undefined number of files. This class arranges packets by TCP/UDP + * flows and for each flow lets the inherited classes determine to which file + * number this flow will be matched */ -class IPPortSplitter : public ValueBasedSplitter -{ -public: - - /** - * C'tor for this class, does nothing but calling its ancestor - */ - IPPortSplitter(int maxFiles) : ValueBasedSplitter(maxFiles) {} - - /** - * Implements Splitter's abstract method. This method takes a packet and decides to which flow it belongs to (can - * be an existing flow or a new flow). When opening new flows it uses a virtual abstract method that should be - * Implemented by inherited classes to determine to which file number the flow will be written to - */ - int getFileNumber(pcpp::Packet& packet, std::vector& filesToClose) - { - // if it's not a TCP or UDP packet, put it in file #0 - if (!packet.isPacketOfType(pcpp::TCP) && !packet.isPacketOfType(pcpp::UDP)) - { - return 0; - } - - // hash the 5-tuple and look for it in the flow table - uint32_t hash = pcpp::hash5Tuple(&packet); - - if (m_FlowTable.find(hash) != m_FlowTable.end()) - { - writingToFile(m_FlowTable[hash], filesToClose); - - // if found it, follow the file number written in the hash record - return m_FlowTable[hash]; - } - - // if it's the first packet seen on this flow, try to guess the server port - - if (packet.isPacketOfType(pcpp::TCP)) - { - // extract TCP layer - pcpp::TcpLayer* tcpLayer = packet.getLayerOfType(); - if (tcpLayer != NULL) - { - uint16_t srcPort = tcpLayer->getSrcPort(); - uint16_t dstPort = tcpLayer->getDstPort(); - - if (tcpLayer->getTcpHeader()->synFlag) - { - // SYN packet - if (!tcpLayer->getTcpHeader()->ackFlag) - { - m_FlowTable[hash] = getFileNumberForValue(getValue(packet, SYN, srcPort, dstPort), filesToClose); - return m_FlowTable[hash]; - } - // SYN/ACK packet - else - { - m_FlowTable[hash] = getFileNumberForValue(getValue(packet, SYN_ACK, srcPort, dstPort), filesToClose); - return m_FlowTable[hash]; - } - } - // Other TCP packet - else - { - m_FlowTable[hash] = getFileNumberForValue(getValue(packet, TCP_OTHER, srcPort, dstPort), filesToClose); - return m_FlowTable[hash]; - } - } - } - - else if (packet.isPacketOfType(pcpp::UDP)) - { - // for UDP packets, decide the server port by the lower port - pcpp::UdpLayer* udpLayer = packet.getLayerOfType(); - if (udpLayer != NULL) - { - uint16_t srcPort = udpLayer->getSrcPort(); - uint16_t dstPort = udpLayer->getDstPort(); - m_FlowTable[hash] = getFileNumberForValue(getValue(packet, UDP, srcPort, dstPort), filesToClose); - return m_FlowTable[hash]; - } - } - - // if reached here, return 0 - writingToFile(0, filesToClose); - return 0; - } - - - /** - * Re-implement Splitter's getFileName() method, this time with the IP/port value - */ - std::string getFileName(pcpp::Packet& packet, const std::string &outputPcapBasePath, int fileNumber) - { - // first set the base string as the outputPcapBasePath - std::string result = outputPcapBasePath; - - // if it's not a TCP or UDP packet, put it in file #0 - if (!packet.isPacketOfType(pcpp::TCP) && !packet.isPacketOfType(pcpp::UDP)) - { - return result + "miscellaneous"; - } - - if (packet.isPacketOfType(pcpp::TCP)) - { - // extract TCP layer - pcpp::TcpLayer* tcpLayer = packet.getLayerOfType(); - if (tcpLayer != NULL) - { - uint16_t srcPort = tcpLayer->getSrcPort(); - uint16_t dstPort = tcpLayer->getDstPort(); - - if (tcpLayer->getTcpHeader()->synFlag) - { - // SYN packet - if (!tcpLayer->getTcpHeader()->ackFlag) - { - return result + getValueString(packet, SYN, srcPort, dstPort); - } - // SYN/ACK packet - else - { - return result + getValueString(packet, SYN_ACK, srcPort, dstPort); - } - } - // Other TCP packet - else - { - return result + getValueString(packet, TCP_OTHER, srcPort, dstPort); - } - } - } - - else if (packet.isPacketOfType(pcpp::UDP)) - { - // for UDP packets, decide the server port by the lower port - pcpp::UdpLayer* udpLayer = packet.getLayerOfType(); - if (udpLayer != NULL) - { - uint16_t srcPort = udpLayer->getSrcPort(); - uint16_t dstPort = udpLayer->getDstPort(); - return result + getValueString(packet, UDP, srcPort, dstPort); - } - } - - // if reached here, return 'miscellaneous' - return result + "miscellaneous"; - } - -protected: - - /** - * An enum for TCP/UDP packet type: can be either TCP-SYN, TCP-SYN/ACK, Other TCP packet of UDP packet - */ - enum PacketType - { - SYN, - SYN_ACK, - TCP_OTHER, - UDP - }; - - /** - * This is the virtual abstract method that needs to be implemented by inherited classes. It gets the packet, - * the packet type, and the source and dest ports and should return the value by which file will be split. - * For example: if files should be split by client IP, this method should extract the client IP and return it as - * uint32_t value, or if files should be split by server port, this method should extract the server port and - * return it as uint32_t value - */ - virtual uint32_t getValue(pcpp::Packet& packet, PacketType packetType, uint16_t srcPort, uint16_t dstPort) = 0; - - /** - * This is a virtual abstract method that needs to be implemented by inherited classes. It gets the packet, - * packet type, src and dest ports and return the value by which the file will be split, but in its string format. - * For example: if the file is split by client-ip the expected result is the client-ip string ("a.b.c.d") - */ - virtual std::string getValueString(pcpp::Packet& packet, PacketType packetType, uint16_t srcPort, uint16_t dstPort) = 0; - - /** - * An auxiliary method for extracting packet's IPv4/IPv6 source address hashed as 4 bytes uint32_t value - */ - uint32_t getSrcIPValue(pcpp::Packet& packet) - { - if (packet.isPacketOfType(pcpp::IPv4)) - return packet.getLayerOfType()->getSrcIPv4Address().toInt(); - else if (packet.isPacketOfType(pcpp::IPv6)) - return pcpp::fnvHash((uint8_t*)packet.getLayerOfType()->getSrcIPv6Address().toBytes(), 16); - else - return 0; - } - - /** - * An auxiliary method for extracting packet's IPv4/IPv6 dest address hashed as 4 bytes uint32_t value - */ - uint32_t getDstIPValue(pcpp::Packet& packet) - { - if (packet.isPacketOfType(pcpp::IPv4)) - return packet.getLayerOfType()->getDstIPv4Address().toInt(); - else if (packet.isPacketOfType(pcpp::IPv6)) - return pcpp::fnvHash((uint8_t*)packet.getLayerOfType()->getDstIPv6Address().toBytes(), 16); - else - return 0; - } - - /** - * An auxiliary method to indicate whether an IPv4/IPv6 source address is multicast or not - */ - bool isSrcIPMulticast(pcpp::Packet& packet) - { - if (packet.isPacketOfType(pcpp::IP)) - return packet.getLayerOfType()->getSrcIPAddress().isMulticast(); - return false; - } - - /** - * An auxiliary method to indicate whether an IPv4/IPv6 dest address is multicast or not - */ - bool isDstIPMulticast(pcpp::Packet& packet) - { - if (packet.isPacketOfType(pcpp::IP)) - return packet.getLayerOfType()->getDstIPAddress().isMulticast(); - return false; - } +class IPPortSplitter : public ValueBasedSplitter { + public: + /** + * C'tor for this class, does nothing but calling its ancestor + */ + IPPortSplitter(int maxFiles) : ValueBasedSplitter(maxFiles) {} + + /** + * Implements Splitter's abstract method. This method takes a packet and + * decides to which flow it belongs to (can be an existing flow or a new + * flow). When opening new flows it uses a virtual abstract method that should + * be Implemented by inherited classes to determine to which file number the + * flow will be written to + */ + int getFileNumber(pcpp::Packet& packet, std::vector& filesToClose) { + // if it's not a TCP or UDP packet, put it in file #0 + if (!packet.isPacketOfType(pcpp::TCP) && + !packet.isPacketOfType(pcpp::UDP)) { + return 0; + } + + // hash the 5-tuple and look for it in the flow table + uint32_t hash = pcpp::hash5Tuple(&packet); + + if (m_FlowTable.find(hash) != m_FlowTable.end()) { + writingToFile(m_FlowTable[hash], filesToClose); + + // if found it, follow the file number written in the hash record + return m_FlowTable[hash]; + } + + // if it's the first packet seen on this flow, try to guess the server port + + if (packet.isPacketOfType(pcpp::TCP)) { + // extract TCP layer + pcpp::TcpLayer* tcpLayer = packet.getLayerOfType(); + if (tcpLayer != NULL) { + uint16_t srcPort = tcpLayer->getSrcPort(); + uint16_t dstPort = tcpLayer->getDstPort(); + + if (tcpLayer->getTcpHeader()->synFlag) { + // SYN packet + if (!tcpLayer->getTcpHeader()->ackFlag) { + m_FlowTable[hash] = getFileNumberForValue( + getValue(packet, SYN, srcPort, dstPort), filesToClose); + return m_FlowTable[hash]; + } + // SYN/ACK packet + else { + m_FlowTable[hash] = getFileNumberForValue( + getValue(packet, SYN_ACK, srcPort, dstPort), filesToClose); + return m_FlowTable[hash]; + } + } + // Other TCP packet + else { + m_FlowTable[hash] = getFileNumberForValue( + getValue(packet, TCP_OTHER, srcPort, dstPort), filesToClose); + return m_FlowTable[hash]; + } + } + } + + else if (packet.isPacketOfType(pcpp::UDP)) { + // for UDP packets, decide the server port by the lower port + pcpp::UdpLayer* udpLayer = packet.getLayerOfType(); + if (udpLayer != NULL) { + uint16_t srcPort = udpLayer->getSrcPort(); + uint16_t dstPort = udpLayer->getDstPort(); + m_FlowTable[hash] = getFileNumberForValue( + getValue(packet, UDP, srcPort, dstPort), filesToClose); + return m_FlowTable[hash]; + } + } + + // if reached here, return 0 + writingToFile(0, filesToClose); + return 0; + } + + /** + * Re-implement Splitter's getFileName() method, this time with the IP/port + * value + */ + std::string getFileName(pcpp::Packet& packet, + const std::string& outputPcapBasePath, + int fileNumber) { + // first set the base string as the outputPcapBasePath + std::string result = outputPcapBasePath; + + // if it's not a TCP or UDP packet, put it in file #0 + if (!packet.isPacketOfType(pcpp::TCP) && + !packet.isPacketOfType(pcpp::UDP)) { + return result + "miscellaneous"; + } + + if (packet.isPacketOfType(pcpp::TCP)) { + // extract TCP layer + pcpp::TcpLayer* tcpLayer = packet.getLayerOfType(); + if (tcpLayer != NULL) { + uint16_t srcPort = tcpLayer->getSrcPort(); + uint16_t dstPort = tcpLayer->getDstPort(); + + if (tcpLayer->getTcpHeader()->synFlag) { + // SYN packet + if (!tcpLayer->getTcpHeader()->ackFlag) { + return result + getValueString(packet, SYN, srcPort, dstPort); + } + // SYN/ACK packet + else { + return result + getValueString(packet, SYN_ACK, srcPort, dstPort); + } + } + // Other TCP packet + else { + return result + getValueString(packet, TCP_OTHER, srcPort, dstPort); + } + } + } + + else if (packet.isPacketOfType(pcpp::UDP)) { + // for UDP packets, decide the server port by the lower port + pcpp::UdpLayer* udpLayer = packet.getLayerOfType(); + if (udpLayer != NULL) { + uint16_t srcPort = udpLayer->getSrcPort(); + uint16_t dstPort = udpLayer->getDstPort(); + return result + getValueString(packet, UDP, srcPort, dstPort); + } + } + + // if reached here, return 'miscellaneous' + return result + "miscellaneous"; + } + + protected: + /** + * An enum for TCP/UDP packet type: can be either TCP-SYN, TCP-SYN/ACK, Other + * TCP packet of UDP packet + */ + enum PacketType { SYN, + SYN_ACK, + TCP_OTHER, + UDP }; + + /** + * This is the virtual abstract method that needs to be implemented by + * inherited classes. It gets the packet, the packet type, and the source and + * dest ports and should return the value by which file will be split. For + * example: if files should be split by client IP, this method should extract + * the client IP and return it as uint32_t value, or if files should be split + * by server port, this method should extract the server port and return it as + * uint32_t value + */ + virtual uint32_t getValue(pcpp::Packet& packet, PacketType packetType, + uint16_t srcPort, uint16_t dstPort) = 0; + + /** + * This is a virtual abstract method that needs to be implemented by inherited + * classes. It gets the packet, packet type, src and dest ports and return the + * value by which the file will be split, but in its string format. For + * example: if the file is split by client-ip the expected result is the + * client-ip string ("a.b.c.d") + */ + virtual std::string getValueString(pcpp::Packet& packet, + PacketType packetType, uint16_t srcPort, + uint16_t dstPort) = 0; + + /** + * An auxiliary method for extracting packet's IPv4/IPv6 source address hashed + * as 4 bytes uint32_t value + */ + uint32_t getSrcIPValue(pcpp::Packet& packet) { + if (packet.isPacketOfType(pcpp::IPv4)) + return packet.getLayerOfType() + ->getSrcIPv4Address() + .toInt(); + else if (packet.isPacketOfType(pcpp::IPv6)) + return pcpp::fnvHash((uint8_t*)packet.getLayerOfType() + ->getSrcIPv6Address() + .toBytes(), + 16); + else + return 0; + } + + /** + * An auxiliary method for extracting packet's IPv4/IPv6 dest address hashed + * as 4 bytes uint32_t value + */ + uint32_t getDstIPValue(pcpp::Packet& packet) { + if (packet.isPacketOfType(pcpp::IPv4)) + return packet.getLayerOfType() + ->getDstIPv4Address() + .toInt(); + else if (packet.isPacketOfType(pcpp::IPv6)) + return pcpp::fnvHash((uint8_t*)packet.getLayerOfType() + ->getDstIPv6Address() + .toBytes(), + 16); + else + return 0; + } + + /** + * An auxiliary method to indicate whether an IPv4/IPv6 source address is + * multicast or not + */ + bool isSrcIPMulticast(pcpp::Packet& packet) { + if (packet.isPacketOfType(pcpp::IP)) + return packet.getLayerOfType() + ->getSrcIPAddress() + .isMulticast(); + return false; + } + + /** + * An auxiliary method to indicate whether an IPv4/IPv6 dest address is + * multicast or not + */ + bool isDstIPMulticast(pcpp::Packet& packet) { + if (packet.isPacketOfType(pcpp::IP)) + return packet.getLayerOfType() + ->getDstIPAddress() + .isMulticast(); + return false; + } }; - - /** - * Splits a pcap file by client IP. This means that all flows with a certain client IP will be written to the same - * file. The client IP for each flow is determined as follows: 1) if it's a TCP flow and we have the SYN packet - the - * client IP is the source IP of the SYN packet 2) if it's a TCP flow and we only have the SYN/ACK packet - the - * client IP is the dest IP of the SYN/ACK packet 3) if it's a partial TCP flow and don't have the SYN or SYN/ACK packets, - * the client IP will be determined by the port: the higher port is considered the client side 4) if it's a UDP multicast - * flow - the client IP will be determined by the port: the port corresponding to the multicast address is the client side - * 5) If it's a non-multicast UDP flow - the client IP will be determined by the port: the higher port is considered the - * client side + * Splits a pcap file by client IP. This means that all flows with a certain + * client IP will be written to the same file. The client IP for each flow is + * determined as follows: 1) if it's a TCP flow and we have the SYN packet - the + * client IP is the source IP of the SYN packet 2) if it's a TCP flow and we + * only have the SYN/ACK packet - the client IP is the dest IP of the SYN/ACK + * packet 3) if it's a partial TCP flow and don't have the SYN or SYN/ACK + * packets, the client IP will be determined by the port: the higher port is + * considered the client side 4) if it's a UDP multicast flow - the client IP + * will be determined by the port: the port corresponding to the multicast + * address is the client side 5) If it's a non-multicast UDP flow - the client + * IP will be determined by the port: the higher port is considered the client + * side */ -class ClientIPSplitter : public IPPortSplitter -{ -public: - - /** - * C'tor for this class, does nothing but calling its ancestor - */ - explicit ClientIPSplitter(int maxFiles) : IPPortSplitter(maxFiles) {} - -protected: - - /** - * Implementation of the abstract method of IPPortSplitter. This method returns the client IP for a certain flow - * by the logic written at the description of this class - */ - uint32_t getValue(pcpp::Packet& packet, PacketType packetType, uint16_t srcPort, uint16_t dstPort) - { - switch (packetType) - { - case SYN: - return getSrcIPValue(packet); - case SYN_ACK: - return getDstIPValue(packet); - case UDP: - if(isSrcIPMulticast(packet)) return getSrcIPValue(packet); - else if(isDstIPMulticast(packet)) return getDstIPValue(packet); - else return srcPort >= dstPort ? getSrcIPValue(packet) : getDstIPValue(packet); - // other TCP packet - default: - if (srcPort >= dstPort) - return getSrcIPValue(packet); - else - return getDstIPValue(packet); - } - } - - std::string getValueString(pcpp::Packet& packet, PacketType packetType, uint16_t srcPort, uint16_t dstPort) - { - std::string prefix = "client-ip-"; - - switch (packetType) - { - case SYN: - return prefix + hyphenIP(getSrcIPString(packet)); - case SYN_ACK: - return prefix + hyphenIP(getDstIPString(packet)); - case UDP: - if(isSrcIPMulticast(packet)) return prefix + hyphenIP(getSrcIPString(packet)); - else if(isDstIPMulticast(packet)) return prefix + hyphenIP(getDstIPString(packet)); - else return srcPort >= dstPort ? prefix + hyphenIP(getSrcIPString(packet)) : prefix + hyphenIP(getDstIPString(packet)); - // other TCP packet - default: - if (srcPort >= dstPort) - return prefix + hyphenIP(getSrcIPString(packet)); - else - return prefix + hyphenIP(getDstIPString(packet)); - } - } +class ClientIPSplitter : public IPPortSplitter { + public: + /** + * C'tor for this class, does nothing but calling its ancestor + */ + explicit ClientIPSplitter(int maxFiles) : IPPortSplitter(maxFiles) {} + + protected: + /** + * Implementation of the abstract method of IPPortSplitter. This method + * returns the client IP for a certain flow by the logic written at the + * description of this class + */ + uint32_t getValue(pcpp::Packet& packet, PacketType packetType, + uint16_t srcPort, uint16_t dstPort) { + switch (packetType) { + case SYN: + return getSrcIPValue(packet); + case SYN_ACK: + return getDstIPValue(packet); + case UDP: + if (isSrcIPMulticast(packet)) + return getSrcIPValue(packet); + else if (isDstIPMulticast(packet)) + return getDstIPValue(packet); + else + return srcPort >= dstPort ? getSrcIPValue(packet) + : getDstIPValue(packet); + // other TCP packet + default: + if (srcPort >= dstPort) + return getSrcIPValue(packet); + else + return getDstIPValue(packet); + } + } + + std::string getValueString(pcpp::Packet& packet, PacketType packetType, + uint16_t srcPort, uint16_t dstPort) { + std::string prefix = "client-ip-"; + + switch (packetType) { + case SYN: + return prefix + hyphenIP(getSrcIPString(packet)); + case SYN_ACK: + return prefix + hyphenIP(getDstIPString(packet)); + case UDP: + if (isSrcIPMulticast(packet)) + return prefix + hyphenIP(getSrcIPString(packet)); + else if (isDstIPMulticast(packet)) + return prefix + hyphenIP(getDstIPString(packet)); + else + return srcPort >= dstPort ? prefix + hyphenIP(getSrcIPString(packet)) + : prefix + hyphenIP(getDstIPString(packet)); + // other TCP packet + default: + if (srcPort >= dstPort) + return prefix + hyphenIP(getSrcIPString(packet)); + else + return prefix + hyphenIP(getDstIPString(packet)); + } + } }; - - /** - * Splits a pcap file by server IP. This means that all flows with a certain server IP will be written to the same - * file. The server IP for each flow is determined as follows: 1) if it's a TCP flow and we have the SYN packet - the - * server IP is the dest IP of the SYN packet 2) if it's a TCP flow and we only have the SYN/ACK packet - the - * server IP is the source IP of the SYN/ACK packet 3) if it's a partial TCP flow and don't have the SYN or SYN/ACK packets, - * the server IP will be determined by the port: the lower port is considered the server side 4) if it's a multicast UDP flow - - * the server IP will be determined by the port: the port corresponding to the non-multicast address is consdered as server side - * 5) if i's a non-multicast UDP flow - the server IP will be determined by the port: the lower port is considered the + * Splits a pcap file by server IP. This means that all flows with a certain + * server IP will be written to the same file. The server IP for each flow is + * determined as follows: 1) if it's a TCP flow and we have the SYN packet - the + * server IP is the dest IP of the SYN packet 2) if it's a TCP flow and we only + * have the SYN/ACK packet - the server IP is the source IP of the SYN/ACK + * packet 3) if it's a partial TCP flow and don't have the SYN or SYN/ACK + * packets, the server IP will be determined by the port: the lower port is + * considered the server side 4) if it's a multicast UDP flow - the server IP + * will be determined by the port: the port corresponding to the non-multicast + * address is consdered as server side 5) if i's a non-multicast UDP flow - the + * server IP will be determined by the port: the lower port is considered the * server side */ -class ServerIPSplitter : public IPPortSplitter -{ -public: - - /** - * C'tor for this class, does nothing but calling its ancestor - */ - explicit ServerIPSplitter(int maxFiles) : IPPortSplitter(maxFiles) {} - -protected: - - /** - * Implementation of the abstract method of IPPortSplitter. This method returns the server IP for a certain flow - * by the logic written at the description of this class - */ - uint32_t getValue(pcpp::Packet& packet, PacketType packetType, uint16_t srcPort, uint16_t dstPort) - { - switch (packetType) - { - case SYN: - return getDstIPValue(packet); - case SYN_ACK: - return getSrcIPValue(packet); - case UDP: - if(isSrcIPMulticast(packet)) return getDstIPValue(packet); - else if(isDstIPMulticast(packet)) return getSrcIPValue(packet); - else return srcPort >= dstPort ? getDstIPValue(packet) : getSrcIPValue(packet); - // other TCP packet - default: - if (srcPort >= dstPort) - return getDstIPValue(packet); - else - return getSrcIPValue(packet); - } - } - - std::string getValueString(pcpp::Packet& packet, PacketType packetType, uint16_t srcPort, uint16_t dstPort) - { - std::string prefix = "server-ip-"; - - switch (packetType) - { - case SYN: - return prefix + hyphenIP(getDstIPString(packet)); - case SYN_ACK: - return prefix + hyphenIP(getSrcIPString(packet)); - case UDP: - if(isSrcIPMulticast(packet)) return prefix + hyphenIP(getDstIPString(packet)); - else if(isDstIPMulticast(packet)) return prefix + hyphenIP(getSrcIPString(packet)); - else return srcPort >= dstPort ? prefix + hyphenIP(getDstIPString(packet)) : prefix + hyphenIP(getSrcIPString(packet)); - // other TCP packet - default: - if (srcPort >= dstPort) - return prefix + hyphenIP(getDstIPString(packet)); - else - return prefix + hyphenIP(getSrcIPString(packet)); - } - } - +class ServerIPSplitter : public IPPortSplitter { + public: + /** + * C'tor for this class, does nothing but calling its ancestor + */ + explicit ServerIPSplitter(int maxFiles) : IPPortSplitter(maxFiles) {} + + protected: + /** + * Implementation of the abstract method of IPPortSplitter. This method + * returns the server IP for a certain flow by the logic written at the + * description of this class + */ + uint32_t getValue(pcpp::Packet& packet, PacketType packetType, + uint16_t srcPort, uint16_t dstPort) { + switch (packetType) { + case SYN: + return getDstIPValue(packet); + case SYN_ACK: + return getSrcIPValue(packet); + case UDP: + if (isSrcIPMulticast(packet)) + return getDstIPValue(packet); + else if (isDstIPMulticast(packet)) + return getSrcIPValue(packet); + else + return srcPort >= dstPort ? getDstIPValue(packet) + : getSrcIPValue(packet); + // other TCP packet + default: + if (srcPort >= dstPort) + return getDstIPValue(packet); + else + return getSrcIPValue(packet); + } + } + + std::string getValueString(pcpp::Packet& packet, PacketType packetType, + uint16_t srcPort, uint16_t dstPort) { + std::string prefix = "server-ip-"; + + switch (packetType) { + case SYN: + return prefix + hyphenIP(getDstIPString(packet)); + case SYN_ACK: + return prefix + hyphenIP(getSrcIPString(packet)); + case UDP: + if (isSrcIPMulticast(packet)) + return prefix + hyphenIP(getDstIPString(packet)); + else if (isDstIPMulticast(packet)) + return prefix + hyphenIP(getSrcIPString(packet)); + else + return srcPort >= dstPort ? prefix + hyphenIP(getDstIPString(packet)) + : prefix + hyphenIP(getSrcIPString(packet)); + // other TCP packet + default: + if (srcPort >= dstPort) + return prefix + hyphenIP(getDstIPString(packet)); + else + return prefix + hyphenIP(getSrcIPString(packet)); + } + } }; - - /** - * Splits a pcap file by server port (most of the time is similar to protocol). This means that all flows with a certain - * server port will be written to the same file. The server port for each flow is determined as follows: 1) if it's a TCP - * flow and we have the SYN packet - the server port is the dest port of the SYN packet 2) if it's a TCP flow and we only - * have the SYN/ACK packet - the server port is the source port of the SYN/ACK packet 3) if it's a partial TCP flow and - * we don't have the SYN or SYN/ACK packets, the server port will be determined by the port: the lower port is considered - * the server side 4) if it's a UDP multicast flow - if the sourceIP is a multicast address, the dest port is considered - * as a server port, otherwise if the destIP is a multicast address, the source port is considered as a server port 5) if - * it's a UDP flow - the server port will be determined by the port: the lower port is considered as server port + * Splits a pcap file by server port (most of the time is similar to protocol). + * This means that all flows with a certain server port will be written to the + * same file. The server port for each flow is determined as follows: 1) if it's + * a TCP flow and we have the SYN packet - the server port is the dest port of + * the SYN packet 2) if it's a TCP flow and we only have the SYN/ACK packet - + * the server port is the source port of the SYN/ACK packet 3) if it's a partial + * TCP flow and we don't have the SYN or SYN/ACK packets, the server port will + * be determined by the port: the lower port is considered the server side 4) if + * it's a UDP multicast flow - if the sourceIP is a multicast address, the dest + * port is considered as a server port, otherwise if the destIP is a multicast + * address, the source port is considered as a server port 5) if it's a UDP flow + * - the server port will be determined by the port: the lower port is + * considered as server port */ -class ServerPortSplitter : public IPPortSplitter -{ -public: - - /** - * C'tor for this class, does nothing but calling its ancestor - */ - explicit ServerPortSplitter(int maxFiles) : IPPortSplitter(maxFiles) {} - -protected: - - /** - * Implementation of the abstract method of IPPortSplitter. This method returns the server port for a certain flow - * by the logic written at the description of this class - */ - uint32_t getValue(pcpp::Packet& packet, PacketType packetType, uint16_t srcPort, uint16_t dstPort) - { - switch (packetType) - { - case SYN: - return dstPort; - case SYN_ACK: - return srcPort; - case UDP: - if(isSrcIPMulticast(packet)) return dstPort; - else if(isDstIPMulticast(packet)) return srcPort; - else return std::min(srcPort, dstPort); - // other TCP packet - default: - return std::min(srcPort, dstPort); - } - } - - std::string getValueString(pcpp::Packet& packet, PacketType packetType, uint16_t srcPort, uint16_t dstPort) - { - std::string prefix = "server-port-"; - - uint16_t res = 0; - switch (packetType) - { - case SYN: - res = dstPort; - break; - case SYN_ACK: - res = srcPort; - break; - case UDP: - if(isSrcIPMulticast(packet)) res = dstPort; - else if(isDstIPMulticast(packet)) res = srcPort; - else res = std::min(srcPort, dstPort); - break; - // other TCP packet - default: - res = std::min(srcPort, dstPort); - break; - } - - std::ostringstream sstream; - sstream << res; - return prefix + sstream.str(); - } +class ServerPortSplitter : public IPPortSplitter { + public: + /** + * C'tor for this class, does nothing but calling its ancestor + */ + explicit ServerPortSplitter(int maxFiles) : IPPortSplitter(maxFiles) {} + + protected: + /** + * Implementation of the abstract method of IPPortSplitter. This method + * returns the server port for a certain flow by the logic written at the + * description of this class + */ + uint32_t getValue(pcpp::Packet& packet, PacketType packetType, + uint16_t srcPort, uint16_t dstPort) { + switch (packetType) { + case SYN: + return dstPort; + case SYN_ACK: + return srcPort; + case UDP: + if (isSrcIPMulticast(packet)) + return dstPort; + else if (isDstIPMulticast(packet)) + return srcPort; + else + return std::min(srcPort, dstPort); + // other TCP packet + default: + return std::min(srcPort, dstPort); + } + } + + std::string getValueString(pcpp::Packet& packet, PacketType packetType, + uint16_t srcPort, uint16_t dstPort) { + std::string prefix = "server-port-"; + + uint16_t res = 0; + switch (packetType) { + case SYN: + res = dstPort; + break; + case SYN_ACK: + res = srcPort; + break; + case UDP: + if (isSrcIPMulticast(packet)) + res = dstPort; + else if (isDstIPMulticast(packet)) + res = srcPort; + else + res = std::min(srcPort, dstPort); + break; + // other TCP packet + default: + res = std::min(srcPort, dstPort); + break; + } + + std::ostringstream sstream; + sstream << res; + return prefix + sstream.str(); + } }; /** - * Splits a pcap file by server client (most of the time is similar to protocol). This means that all flows with a certain - * client port will be written to the same file. The client port for each flow is determined as follows: 1) if it's a TCP - * flow and we have the SYN packet - the client port is the source port of the SYN packet 2) if it's a TCP flow and we only - * have the SYN/ACK packet - the client port is the dest port of the SYN/ACK packet 3) if it's a partial TCP flow and - * we don't have the SYN or SYN/ACK packets, the server port will be determined by the port: the higher port is considered - * the client side 4) if it's a UDP multicast flow - if the sourceIP is a multicast address, the source port is considered - * as a client port, otherwise if the destIP is a multicast address, the dest port is considered as a client port 5) if - * it's a UDP flow - the client port will be determined by the port: the higher port is considered as client port + * Splits a pcap file by server client (most of the time is similar to + * protocol). This means that all flows with a certain client port will be + * written to the same file. The client port for each flow is determined as + * follows: 1) if it's a TCP flow and we have the SYN packet - the client port + * is the source port of the SYN packet 2) if it's a TCP flow and we only have + * the SYN/ACK packet - the client port is the dest port of the SYN/ACK packet + * 3) if it's a partial TCP flow and we don't have the SYN or SYN/ACK packets, + * the server port will be determined by the port: the higher port is considered + * the client side 4) if it's a UDP multicast flow - if the sourceIP is a + * multicast address, the source port is considered as a client port, otherwise + * if the destIP is a multicast address, the dest port is considered as a client + * port 5) if it's a UDP flow - the client port will be determined by the port: + * the higher port is considered as client port */ -class ClientPortSplitter : public IPPortSplitter -{ -public: - - /** - * C'tor for this class, does nothing but calling its ancestor - */ - explicit ClientPortSplitter(int maxFiles) : IPPortSplitter(maxFiles) {} - -protected: - - /** - * Implementation of the abstract method of IPPortSplitter. This method returns the client port for a certain flow - * by the logic written at the description of this class - */ - uint32_t getValue(pcpp::Packet& packet, PacketType packetType, uint16_t srcPort, uint16_t dstPort) - { - switch (packetType) - { - case SYN: - return srcPort; - case SYN_ACK: - return dstPort; - case UDP: - if(isSrcIPMulticast(packet)) return srcPort; - else if(isDstIPMulticast(packet)) return dstPort; - else return std::max(srcPort, dstPort); - // other TCP packet - default: - return std::max(srcPort, dstPort); - } - } - - std::string getValueString(pcpp::Packet& packet, PacketType packetType, uint16_t srcPort, uint16_t dstPort) - { - std::string prefix = "client-port-"; - - uint16_t res = 0; - switch (packetType) - { - case SYN: - res = srcPort; - break; - case SYN_ACK: - res = dstPort; - break; - case UDP: - if(isSrcIPMulticast(packet)) res = srcPort; - else if(isDstIPMulticast(packet)) res = dstPort; - else res = std::max(srcPort, dstPort); - break; - // other TCP packet - default: - res = std::max(srcPort, dstPort); - break; - } - - std::ostringstream sstream; - sstream << res; - return prefix + sstream.str(); - } +class ClientPortSplitter : public IPPortSplitter { + public: + /** + * C'tor for this class, does nothing but calling its ancestor + */ + explicit ClientPortSplitter(int maxFiles) : IPPortSplitter(maxFiles) {} + + protected: + /** + * Implementation of the abstract method of IPPortSplitter. This method + * returns the client port for a certain flow by the logic written at the + * description of this class + */ + uint32_t getValue(pcpp::Packet& packet, PacketType packetType, + uint16_t srcPort, uint16_t dstPort) { + switch (packetType) { + case SYN: + return srcPort; + case SYN_ACK: + return dstPort; + case UDP: + if (isSrcIPMulticast(packet)) + return srcPort; + else if (isDstIPMulticast(packet)) + return dstPort; + else + return std::max(srcPort, dstPort); + // other TCP packet + default: + return std::max(srcPort, dstPort); + } + } + + std::string getValueString(pcpp::Packet& packet, PacketType packetType, + uint16_t srcPort, uint16_t dstPort) { + std::string prefix = "client-port-"; + + uint16_t res = 0; + switch (packetType) { + case SYN: + res = srcPort; + break; + case SYN_ACK: + res = dstPort; + break; + case UDP: + if (isSrcIPMulticast(packet)) + res = srcPort; + else if (isDstIPMulticast(packet)) + res = dstPort; + else + res = std::max(srcPort, dstPort); + break; + // other TCP packet + default: + res = std::max(srcPort, dstPort); + break; + } + + std::ostringstream sstream; + sstream << res; + return prefix + sstream.str(); + } }; diff --git a/Examples/PcapSplitter/SimpleSplitters.h b/Examples/PcapSplitter/SimpleSplitters.h index f69b67cd85..5a761f7eda 100644 --- a/Examples/PcapSplitter/SimpleSplitters.h +++ b/Examples/PcapSplitter/SimpleSplitters.h @@ -1,207 +1,192 @@ #pragma once -#include "Splitters.h" #include "PcapDevice.h" #include "PcapFilter.h" +#include "Splitters.h" /** * Splits a pcap file by number of packets */ -class PacketCountSplitter : public Splitter -{ -private: - int m_PacketCount; - int m_MaxPacketsPerFile; - -public: - - /** - * A c'tor for this class which gets the packet count for each split file - */ - explicit PacketCountSplitter(int maxPacketsPerFile) - { - m_PacketCount = 0; - m_MaxPacketsPerFile = maxPacketsPerFile; - } - - /** - * Return the current file number if its packet count didn't reach the limit, or else return the next - * file number and close the current file - */ - int getFileNumber(pcpp::Packet& packet, std::vector& filesToClose) - { - // check the current file number - int curFile = m_PacketCount / m_MaxPacketsPerFile; - // increment packet count - m_PacketCount++; - // check the new file number - int nextFile = m_PacketCount / m_MaxPacketsPerFile; - // if reached packet count limit, close the previous file and return the next file number - if (curFile != nextFile) - filesToClose.push_back(curFile); - return curFile; - } - - /** - * Make sure packet count is a positive number - */ - bool isSplitterParamLegal(std::string& errorString) - { - if (m_MaxPacketsPerFile < 1) - { - errorString = "max packets per file must be be a positive number greater than 0"; - return false; - } - - return true; - } +class PacketCountSplitter : public Splitter { + private: + int m_PacketCount; + int m_MaxPacketsPerFile; + + public: + /** + * A c'tor for this class which gets the packet count for each split file + */ + explicit PacketCountSplitter(int maxPacketsPerFile) { + m_PacketCount = 0; + m_MaxPacketsPerFile = maxPacketsPerFile; + } + + /** + * Return the current file number if its packet count didn't reach the limit, + * or else return the next file number and close the current file + */ + int getFileNumber(pcpp::Packet& packet, std::vector& filesToClose) { + // check the current file number + int curFile = m_PacketCount / m_MaxPacketsPerFile; + // increment packet count + m_PacketCount++; + // check the new file number + int nextFile = m_PacketCount / m_MaxPacketsPerFile; + // if reached packet count limit, close the previous file and return the + // next file number + if (curFile != nextFile) + filesToClose.push_back(curFile); + return curFile; + } + + /** + * Make sure packet count is a positive number + */ + bool isSplitterParamLegal(std::string& errorString) { + if (m_MaxPacketsPerFile < 1) { + errorString = + "max packets per file must be be a positive number greater than 0"; + return false; + } + + return true; + } }; - - /** * Splits a pcap file by number of byte in each file */ -class FileSizeSplitter : public Splitter -{ -private: - uint64_t m_TotalSize; - uint64_t m_MaxBytesPerFile; - - static const int PCAP_FILE_HEADER_SIZE = 24; // == sizeof(pcap_file_header) - static const int PCAP_PACKET_HEADER_SIZE = 16; // == sizeof(pcap_pkthdr) - -public: - - /** - * A c'tor for this class which gets the file size in bytes for each split file - */ - explicit FileSizeSplitter(uint64_t maxBytesPerFile) - { - m_TotalSize = 0; - // each file size contains a pcap header with size of PCAP_FILE_HEADER_SIZE - m_MaxBytesPerFile = maxBytesPerFile - PCAP_FILE_HEADER_SIZE; - } - - /** - * Return the current file number if its size didn't reach the file size limit, or else return the next - * file number and close the current file - */ - int getFileNumber(pcpp::Packet& packet, std::vector& filesToClose) - { - // check the current file - int prevFile = m_TotalSize / m_MaxBytesPerFile; - // add the current packet size and packet header - m_TotalSize += (uint64_t)packet.getRawPacket()->getRawDataLen() + PCAP_PACKET_HEADER_SIZE; - // calculate the new file number - int nextFile = m_TotalSize / m_MaxBytesPerFile; - // if reached the maximum size per file, close the previous file - if (prevFile != nextFile) - filesToClose.push_back(prevFile); - return nextFile; - } - - /** - * Each file size must be at least in size of PCAP_FILE_HEADER_SIZE + PCAP_PACKET_HEADER_SIZE - */ - bool isSplitterParamLegal(std::string& errorString) - { - if (m_MaxBytesPerFile < PCAP_PACKET_HEADER_SIZE + 1) - { - errorString = "max bytes per file must be be a positive number greater than 48"; - return false; - } - - return true; - } - +class FileSizeSplitter : public Splitter { + private: + uint64_t m_TotalSize; + uint64_t m_MaxBytesPerFile; + + static const int PCAP_FILE_HEADER_SIZE = 24; // == sizeof(pcap_file_header) + static const int PCAP_PACKET_HEADER_SIZE = 16; // == sizeof(pcap_pkthdr) + + public: + /** + * A c'tor for this class which gets the file size in bytes for each split + * file + */ + explicit FileSizeSplitter(uint64_t maxBytesPerFile) { + m_TotalSize = 0; + // each file size contains a pcap header with size of PCAP_FILE_HEADER_SIZE + m_MaxBytesPerFile = maxBytesPerFile - PCAP_FILE_HEADER_SIZE; + } + + /** + * Return the current file number if its size didn't reach the file size + * limit, or else return the next file number and close the current file + */ + int getFileNumber(pcpp::Packet& packet, std::vector& filesToClose) { + // check the current file + int prevFile = m_TotalSize / m_MaxBytesPerFile; + // add the current packet size and packet header + m_TotalSize += (uint64_t)packet.getRawPacket()->getRawDataLen() + + PCAP_PACKET_HEADER_SIZE; + // calculate the new file number + int nextFile = m_TotalSize / m_MaxBytesPerFile; + // if reached the maximum size per file, close the previous file + if (prevFile != nextFile) + filesToClose.push_back(prevFile); + return nextFile; + } + + /** + * Each file size must be at least in size of PCAP_FILE_HEADER_SIZE + + * PCAP_PACKET_HEADER_SIZE + */ + bool isSplitterParamLegal(std::string& errorString) { + if (m_MaxBytesPerFile < PCAP_PACKET_HEADER_SIZE + 1) { + errorString = + "max bytes per file must be be a positive number greater than 48"; + return false; + } + + return true; + } }; - /** - * Splits a pcap file into two files: one that contains all packets matching a given BPF filter and one that contains the rest - * of the packets + * Splits a pcap file into two files: one that contains all packets matching a + * given BPF filter and one that contains the rest of the packets */ -class BpfCriteriaSplitter : public Splitter -{ -private: - std::string m_BpfFilter; - pcpp::BPFStringFilter filter; - -public: - explicit BpfCriteriaSplitter(const std::string &bpfFilter) : m_BpfFilter(bpfFilter), filter(bpfFilter) {} - - /** - * Return file #0 if packet matches the BPF filer, and file #1 if it's not - */ - int getFileNumber(pcpp::Packet& packet, std::vector& filesToClose) - { - if (pcpp::IPcapDevice::matchPacketWithFilter(filter, packet.getRawPacket())) - return 0; - return 1; - } - - /** - * Re-implement Splitter's getFileName() method, clarifying which file was matched by the BPF - * filter and which didn't - */ - std::string getFileName(pcpp::Packet& packet, const std::string &outputPcapBasePath, int fileNumber) - { - if (fileNumber == 0) - return outputPcapBasePath + "match-bpf"; - else - return outputPcapBasePath + "not-match-bpf"; - } - - /** - * Verifies the BPF filter set in the c'tor is a valid BPF filter - */ - bool isSplitterParamLegal(std::string& errorString) - { - if (m_BpfFilter == "") - { - errorString = "No BPF filter was set or set an empty one"; - return false; - } - - - pcpp::BPFStringFilter localFilter(m_BpfFilter); - bool filterValid = localFilter.verifyFilter(); - if (!filterValid) - errorString = "BPF filter is not valid"; - - return filterValid; - } +class BpfCriteriaSplitter : public Splitter { + private: + std::string m_BpfFilter; + pcpp::BPFStringFilter filter; + + public: + explicit BpfCriteriaSplitter(const std::string& bpfFilter) + : m_BpfFilter(bpfFilter), filter(bpfFilter) {} + + /** + * Return file #0 if packet matches the BPF filer, and file #1 if it's not + */ + int getFileNumber(pcpp::Packet& packet, std::vector& filesToClose) { + if (pcpp::IPcapDevice::matchPacketWithFilter(filter, packet.getRawPacket())) + return 0; + return 1; + } + + /** + * Re-implement Splitter's getFileName() method, clarifying which file was + * matched by the BPF filter and which didn't + */ + std::string getFileName(pcpp::Packet& packet, + const std::string& outputPcapBasePath, + int fileNumber) { + if (fileNumber == 0) + return outputPcapBasePath + "match-bpf"; + else + return outputPcapBasePath + "not-match-bpf"; + } + + /** + * Verifies the BPF filter set in the c'tor is a valid BPF filter + */ + bool isSplitterParamLegal(std::string& errorString) { + if (m_BpfFilter == "") { + errorString = "No BPF filter was set or set an empty one"; + return false; + } + + pcpp::BPFStringFilter localFilter(m_BpfFilter); + bool filterValid = localFilter.verifyFilter(); + if (!filterValid) + errorString = "BPF filter is not valid"; + + return filterValid; + } }; - /** - * Split a pcap file to an arbitrary number of files in a round-robin manner, each read packet to the next file in line + * Split a pcap file to an arbitrary number of files in a round-robin manner, + * each read packet to the next file in line */ -class RoundRobinSplitter : public SplitterWithMaxFiles -{ -public: - explicit RoundRobinSplitter(int numOfFiles) : SplitterWithMaxFiles(numOfFiles) { } - - /** - * Get the next file number, SplitterWithMaxFiles#getNextFileNumber() takes care of the round-robin method - */ - int getFileNumber(pcpp::Packet& packet, std::vector& filesToClose) - { - return getNextFileNumber(filesToClose); - } - - /** - * Number of files must be a positive integer - */ - bool isSplitterParamLegal(std::string& errorString) - { - if (m_MaxFiles < 1) - { - errorString = "number of files must be a positive integer"; - return false; - } - - return true; - } +class RoundRobinSplitter : public SplitterWithMaxFiles { + public: + explicit RoundRobinSplitter(int numOfFiles) + : SplitterWithMaxFiles(numOfFiles) {} + + /** + * Get the next file number, SplitterWithMaxFiles#getNextFileNumber() takes + * care of the round-robin method + */ + int getFileNumber(pcpp::Packet& packet, std::vector& filesToClose) { + return getNextFileNumber(filesToClose); + } + + /** + * Number of files must be a positive integer + */ + bool isSplitterParamLegal(std::string& errorString) { + if (m_MaxFiles < 1) { + errorString = "number of files must be a positive integer"; + return false; + } + + return true; + } }; diff --git a/Examples/PcapSplitter/Splitters.h b/Examples/PcapSplitter/Splitters.h index e4ae645dc8..96ef456a9a 100644 --- a/Examples/PcapSplitter/Splitters.h +++ b/Examples/PcapSplitter/Splitters.h @@ -1,240 +1,242 @@ #pragma once -#include "LRUList.h" -#include "RawPacket.h" -#include "Packet.h" +#include "DnsLayer.h" #include "IPv4Layer.h" #include "IPv6Layer.h" +#include "LRUList.h" +#include "Packet.h" +#include "PacketUtils.h" +#include "RawPacket.h" #include "TcpLayer.h" #include "UdpLayer.h" -#include "DnsLayer.h" -#include "PacketUtils.h" -#include #include #include +#include #include /** - * The base splitter class. All type of splitters inherit from it. It's a virtual abstract class that doesn't - * implement any logic + * The base splitter class. All type of splitters inherit from it. It's a + * virtual abstract class that doesn't implement any logic */ -class Splitter -{ -public: - - /** - * A method that gets a packet and returns: - * - The file number to write the packet to - * - A vector of file numbers to close (may be empty) - */ - virtual int getFileNumber(pcpp::Packet& packet, std::vector& filesToClose) = 0; - - /** - * Most splitters have a parameter (for example: packet count for each file or max file size, etc.). - * This method should return true if the parameter value is legal or false otherwise. If parameter value is - * illegal it should return a proper error string that will be displayed for the user - */ - virtual bool isSplitterParamLegal(std::string& errorString) = 0; - - /** - * A method that enables the splitter to decide what will be the output file names based on the file number - * (determined also by the splitter), the output path and input file name (determined by the user) and the - * first packet that will be written to this file. The default implementation is the following: - * ' /requested-path/original-file-name-[4-digit-number-starting-at-0000].pcap' - */ - virtual std::string getFileName(pcpp::Packet& packet, const std::string &outputPcapBasePath, int fileNumber) - { - std::ostringstream sstream; - sstream << std::setw(4) << std::setfill( '0' ) << fileNumber; - return outputPcapBasePath.c_str() + sstream.str(); - } - - /** - * A virtual d'tor - */ - virtual ~Splitter() {} +class Splitter { + public: + /** + * A method that gets a packet and returns: + * - The file number to write the packet to + * - A vector of file numbers to close (may be empty) + */ + virtual int getFileNumber(pcpp::Packet& packet, + std::vector& filesToClose) = 0; + + /** + * Most splitters have a parameter (for example: packet count for each file or + * max file size, etc.). This method should return true if the parameter value + * is legal or false otherwise. If parameter value is illegal it should return + * a proper error string that will be displayed for the user + */ + virtual bool isSplitterParamLegal(std::string& errorString) = 0; + + /** + * A method that enables the splitter to decide what will be the output file + * names based on the file number (determined also by the splitter), the + * output path and input file name (determined by the user) and the first + * packet that will be written to this file. The default implementation is the + * following: ' + * /requested-path/original-file-name-[4-digit-number-starting-at-0000].pcap' + */ + virtual std::string getFileName(pcpp::Packet& packet, + const std::string& outputPcapBasePath, + int fileNumber) { + std::ostringstream sstream; + sstream << std::setw(4) << std::setfill('0') << fileNumber; + return outputPcapBasePath.c_str() + sstream.str(); + } + + /** + * A virtual d'tor + */ + virtual ~Splitter() {} }; - /** - * A virtual abstract splitter which represent splitters that may or may not have a limit on the number of - * output files after the split - * Since any OS has a limit on concurrently open files, this class implements a mechanism that makes sure no more than a - * certain number of files are opened concurrently without limiting the number of output files desired by the user. - * The idea is to use a LRU list that holds the number of currently open files. Each time a packet is written to a - * certain file, the file number is advance to the head of the LRU list (or added if it's not there), - * and if the list is full then the least recently used file is leaving it and gets closed. The next time a packet will - * be written to a file that left the LRU list, this file will be put back in the LRU list, re-opened and packet will - * be appended to that file + * A virtual abstract splitter which represent splitters that may or may not + * have a limit on the number of output files after the split Since any OS has a + * limit on concurrently open files, this class implements a mechanism that + * makes sure no more than a certain number of files are opened concurrently + * without limiting the number of output files desired by the user. The idea is + * to use a LRU list that holds the number of currently open files. Each time a + * packet is written to a certain file, the file number is advance to the head + * of the LRU list (or added if it's not there), and if the list is full then + * the least recently used file is leaving it and gets closed. The next time a + * packet will be written to a file that left the LRU list, this file will be + * put back in the LRU list, re-opened and packet will be appended to that file */ -class SplitterWithMaxFiles : public Splitter -{ - // in order to support all OS's, the maximum number of concurrent open file is set to 250 - static const int MAX_NUMBER_OF_CONCURRENT_OPEN_FILES = 250; - -protected: - int m_MaxFiles; - int m_NextFile; - pcpp::LRUList m_LRUFileList; - - /** - * A helper method that needs to be called by child classes each time a packet is written to a certain file. - * This method puts the file in the LRU list, and if the list is full it pulls out the least recently used file - * and returns it in filesToClose vector. The application will take care of closing that file - */ - void writingToFile(int fileNum, std::vector& filesToClose) - { - int fileToClose; - if (m_LRUFileList.put(fileNum, &fileToClose) == 1) - filesToClose.push_back(fileToClose); - } - - /** - * A helper method that is called by child classes and returns the next file number. If there's no output file limit - * it just return prev_file_number+1. But if there is a file limit it return file number in cyclic manner, meaning if - * reached the max file number, the next file number will be 0. - * In addition the method puts the next file in the LRU list and if the list is full it pulls out the least recently - * used file and returns it in filesToClose vector. The application will take care of closing that file - */ - int getNextFileNumber(std::vector& filesToClose) - { - int nextFile = 0; - - // zero or negative m_MaxFiles means no limit - if (m_MaxFiles <= 0) - nextFile = m_NextFile++; - else // m_MaxFiles is positive, meaning there is a output file limit - { - nextFile = (m_NextFile) % m_MaxFiles; - m_NextFile++; - } - - - // put the next file in the LRU list - int fileToClose; - if (m_LRUFileList.put(nextFile, &fileToClose) == 1) - { - // if a file is pulled out of the LRU list - return it - filesToClose.push_back(fileToClose); - } - return nextFile; - } - - /** - * A protected c'tor for this class which gets the output file limit size. If maxFile is UNLIMITED_FILES_MAGIC_NUMBER, - * it's considered there's no output files limit - */ - explicit SplitterWithMaxFiles(int maxFiles, int firstFileNumber = 0) : m_LRUFileList(MAX_NUMBER_OF_CONCURRENT_OPEN_FILES) - { - m_MaxFiles = maxFiles; - m_NextFile = firstFileNumber; - } - -public: - - static const int UNLIMITED_FILES_MAGIC_NUMBER = -12345; - - /** - * This method checks the maximum number of file parameter. If it equals UNLIMITED_FILES_MAGIC_NUMBER it means there - * is no limit. Else it verifies the limit is a positive number - */ - bool isSplitterParamLegal(std::string& errorString) - { - // unlimited number of output files - if (m_MaxFiles == UNLIMITED_FILES_MAGIC_NUMBER) - return true; - - if (m_MaxFiles <= 0) - { - errorString = "max number of file must be a positive number"; - return false; - } - - return true; - } +class SplitterWithMaxFiles : public Splitter { + // in order to support all OS's, the maximum number of concurrent open file is + // set to 250 + static const int MAX_NUMBER_OF_CONCURRENT_OPEN_FILES = 250; + + protected: + int m_MaxFiles; + int m_NextFile; + pcpp::LRUList m_LRUFileList; + + /** + * A helper method that needs to be called by child classes each time a packet + * is written to a certain file. This method puts the file in the LRU list, + * and if the list is full it pulls out the least recently used file and + * returns it in filesToClose vector. The application will take care of + * closing that file + */ + void writingToFile(int fileNum, std::vector& filesToClose) { + int fileToClose; + if (m_LRUFileList.put(fileNum, &fileToClose) == 1) + filesToClose.push_back(fileToClose); + } + + /** + * A helper method that is called by child classes and returns the next file + * number. If there's no output file limit it just return prev_file_number+1. + * But if there is a file limit it return file number in cyclic manner, + * meaning if reached the max file number, the next file number will be 0. In + * addition the method puts the next file in the LRU list and if the list is + * full it pulls out the least recently used file and returns it in + * filesToClose vector. The application will take care of closing that file + */ + int getNextFileNumber(std::vector& filesToClose) { + int nextFile = 0; + + // zero or negative m_MaxFiles means no limit + if (m_MaxFiles <= 0) + nextFile = m_NextFile++; + else // m_MaxFiles is positive, meaning there is a output file limit + { + nextFile = (m_NextFile) % m_MaxFiles; + m_NextFile++; + } + + // put the next file in the LRU list + int fileToClose; + if (m_LRUFileList.put(nextFile, &fileToClose) == 1) { + // if a file is pulled out of the LRU list - return it + filesToClose.push_back(fileToClose); + } + return nextFile; + } + + /** + * A protected c'tor for this class which gets the output file limit size. If + * maxFile is UNLIMITED_FILES_MAGIC_NUMBER, it's considered there's no output + * files limit + */ + explicit SplitterWithMaxFiles(int maxFiles, int firstFileNumber = 0) + : m_LRUFileList(MAX_NUMBER_OF_CONCURRENT_OPEN_FILES) { + m_MaxFiles = maxFiles; + m_NextFile = firstFileNumber; + } + + public: + static const int UNLIMITED_FILES_MAGIC_NUMBER = -12345; + + /** + * This method checks the maximum number of file parameter. If it equals + * UNLIMITED_FILES_MAGIC_NUMBER it means there is no limit. Else it verifies + * the limit is a positive number + */ + bool isSplitterParamLegal(std::string& errorString) { + // unlimited number of output files + if (m_MaxFiles == UNLIMITED_FILES_MAGIC_NUMBER) + return true; + + if (m_MaxFiles <= 0) { + errorString = "max number of file must be a positive number"; + return false; + } + + return true; + } }; - /** - * An abstract virtual splitter which represent splitters that needs to keep a mapping between a certain packet value to - * a certain file number the packet needs to be written to. For example: in client-ip splitter all flows with a - * certain client-ip should be written to the same file. So this class will enable it to keep a mapping between client-ips - * and file numbers. This class inherits SplitterWithMaxFiles so it supports having or not having a limit on the number - * of output files + * An abstract virtual splitter which represent splitters that needs to keep a + * mapping between a certain packet value to a certain file number the packet + * needs to be written to. For example: in client-ip splitter all flows with a + * certain client-ip should be written to the same file. So this class will + * enable it to keep a mapping between client-ips and file numbers. This class + * inherits SplitterWithMaxFiles so it supports having or not having a limit on + * the number of output files */ -class ValueBasedSplitter : public SplitterWithMaxFiles -{ -protected: - // A flow table that keeps track of all flows (a flow is usually identified by 5-tuple) - std::map m_FlowTable; - // a map between the relevant packet value (e.g client-ip) and the file to write the packet to - std::map m_ValueToFileTable; - - /** - * A protected c'tor for this class that only propagate the maxFiles to its ancestor - */ - explicit ValueBasedSplitter(int maxFiles) : SplitterWithMaxFiles(maxFiles, 1) {} - - /** - * A helper method that gets the packet value and returns the file to write it to, and also a file to close if the - * LRU list is full - */ - int getFileNumberForValue(uint32_t value, std::vector& filesToClose) - { - // search the value in the value-to-file map. If it's there, return the file number - if (m_ValueToFileTable.find(value) != m_ValueToFileTable.end()) - { - // if value was already seen, follow the same file number - return m_ValueToFileTable[value]; - } - - // if it's not there, use SplitterWithMaxFiles's helper method to get a new file number, put it in the map - // and return this file number - m_ValueToFileTable[value] = getNextFileNumber(filesToClose); - return m_ValueToFileTable[value]; - } +class ValueBasedSplitter : public SplitterWithMaxFiles { + protected: + // A flow table that keeps track of all flows (a flow is usually identified by + // 5-tuple) + std::map m_FlowTable; + // a map between the relevant packet value (e.g client-ip) and the file to + // write the packet to + std::map m_ValueToFileTable; + + /** + * A protected c'tor for this class that only propagate the maxFiles to its + * ancestor + */ + explicit ValueBasedSplitter(int maxFiles) + : SplitterWithMaxFiles(maxFiles, 1) {} + + /** + * A helper method that gets the packet value and returns the file to write it + * to, and also a file to close if the LRU list is full + */ + int getFileNumberForValue(uint32_t value, std::vector& filesToClose) { + // search the value in the value-to-file map. If it's there, return the file + // number + if (m_ValueToFileTable.find(value) != m_ValueToFileTable.end()) { + // if value was already seen, follow the same file number + return m_ValueToFileTable[value]; + } + + // if it's not there, use SplitterWithMaxFiles's helper method to get a new + // file number, put it in the map and return this file number + m_ValueToFileTable[value] = getNextFileNumber(filesToClose); + return m_ValueToFileTable[value]; + } }; /** - * An auxiliary method for extracting packet's IPv4/IPv6 source address as string + * An auxiliary method for extracting packet's IPv4/IPv6 source address as + * string */ -std::string getSrcIPString(pcpp::Packet& packet) -{ - if (packet.isPacketOfType(pcpp::IP)) - return packet.getLayerOfType()->getSrcIPAddress().toString(); - return "miscellaneous"; +std::string getSrcIPString(pcpp::Packet& packet) { + if (packet.isPacketOfType(pcpp::IP)) + return packet.getLayerOfType()->getSrcIPAddress().toString(); + return "miscellaneous"; } /** * An auxiliary method for extracting packet's IPv4/IPv6 dest address string */ -std::string getDstIPString(pcpp::Packet& packet) -{ - if (packet.isPacketOfType(pcpp::IP)) - return packet.getLayerOfType()->getDstIPAddress().toString(); - return "miscellaneous"; +std::string getDstIPString(pcpp::Packet& packet) { + if (packet.isPacketOfType(pcpp::IP)) + return packet.getLayerOfType()->getDstIPAddress().toString(); + return "miscellaneous"; } /** * An auxiliary method for replacing '.' and ':' in IPv4/IPv6 addresses with '-' */ -std::string hyphenIP(std::string ipVal) -{ - // for IPv4 - replace '.' with '-' - int loc = ipVal.find("."); - while (loc >= 0) - { - ipVal.replace(loc, 1, "-"); - loc = ipVal.find("."); - } - - // for IPv6 - replace ':' with '-' - loc = ipVal.find(":"); - while (loc >= 0) - { - ipVal.replace(loc, 1, "-"); - loc = ipVal.find(":"); - } - - return ipVal; +std::string hyphenIP(std::string ipVal) { + // for IPv4 - replace '.' with '-' + int loc = ipVal.find("."); + while (loc >= 0) { + ipVal.replace(loc, 1, "-"); + loc = ipVal.find("."); + } + + // for IPv6 - replace ':' with '-' + loc = ipVal.find(":"); + while (loc >= 0) { + ipVal.replace(loc, 1, "-"); + loc = ipVal.find(":"); + } + + return ipVal; } diff --git a/Examples/PcapSplitter/main.cpp b/Examples/PcapSplitter/main.cpp index 7d48ee41b6..bda6321cf2 100644 --- a/Examples/PcapSplitter/main.cpp +++ b/Examples/PcapSplitter/main.cpp @@ -1,97 +1,106 @@ /** * PcapSplitter application * ======================== - * An application that splits a pcap file into smaller pcap files by a user-defined criteria: + * An application that splits a pcap file into smaller pcap files by a + * user-defined criteria: * - * 1) File-size - splits the pcap file to smaller pcap files, each file with a certain size defined by the user - * 2) Packet-count - splits the pcap file to smaller pcap files, each with number of packets defined by the user - * 3) Client-IP - splits the pcap file to smaller pcap files so each file contains all TCP/UDP connections - * initiated by a certain client-ip, for example: file#1 will contain connections initiated by 1.1.1.1, file#2 - * will contain connections initiated by 1.2.3.4, and so on. The user can limit the number of output files, in - * this case multiple client-ips will be written to the same file. If the user doesn't set such limit - each file - * will contain one client-ip - * 4) Server-IP - splits the pcap file to smaller pcap files so each file contains all TCP/UDP connections - * to a certain server-ip, for example: file#1 will contain connections to 8.8.8.8, file#2 will contain connections - * to 10.12.13.14, and so on. The user can limit the number of output files, in this case multiple server-ips will - * be written to the same file. If the user doesn't set such limit - each file will contain one server-ip - * 5) Server-port - splits the pcap file to smaller pcap files so each file contains all TCP/UDP connections - * to a certain server port, for example: file#1 will contain all port 80 connections (HTTP), file#2 will contain - * all port 25 (SMTP) connections, and so on. The user can limit the number of output files, in this case connections - * to multiple server ports will be written to the same file. If the user doesn't set such limit - each file will + * 1) File-size - splits the pcap file to smaller pcap files, each file with a + * certain size defined by the user 2) Packet-count - splits the pcap file to + * smaller pcap files, each with number of packets defined by the user 3) + * Client-IP - splits the pcap file to smaller pcap files so each file contains + * all TCP/UDP connections initiated by a certain client-ip, for example: file#1 + * will contain connections initiated by 1.1.1.1, file#2 will contain + * connections initiated by 1.2.3.4, and so on. The user can limit the number of + * output files, in this case multiple client-ips will be written to the same + * file. If the user doesn't set such limit - each file will contain one + * client-ip 4) Server-IP - splits the pcap file to smaller pcap files so each + * file contains all TCP/UDP connections to a certain server-ip, for example: + * file#1 will contain connections to 8.8.8.8, file#2 will contain connections + * to 10.12.13.14, and so on. The user can limit the number of output files, + * in this case multiple server-ips will be written to the same file. If the + * user doesn't set such limit - each file will contain one server-ip 5) + * Server-port - splits the pcap file to smaller pcap files so each file + * contains all TCP/UDP connections to a certain server port, for example: + * file#1 will contain all port 80 connections (HTTP), file#2 will contain all + * port 25 (SMTP) connections, and so on. The user can limit the number of + * output files, in this case connections to multiple server ports will be + * written to the same file. If the user doesn't set such limit - each file will * contain connection to one server port only - * 6) IP source and IP dest - splits the pcap file to smaller pcap files so each file contains all connections made - * between two IP addresses. The user can limit the number of output files, in this case multiple pairs of IP source - * and dest will be written to the same file. If the user doesn't set such limit - all connection of one pair of - * source and dest IP will be written to each file - * 7) Connection - splits a pcap file to smaller pcap files by TCP/UDP connection meaning each connection will be written - * to a certain file. The user can limit the number of output files, in this case an equal number of connections will - * be written to the same file. If the user doesn't set such limit - each file will contain one connection - * 8) BPF filter - splits the pcap file into two files: one that contains all packets matching the input BPF filter - * and the other one with the rest of the packets + * 6) IP source and IP dest - splits the pcap file to smaller pcap files so each + * file contains all connections made between two IP addresses. The user can + * limit the number of output files, in this case multiple pairs of IP source + * and dest will be written to the same file. If the user doesn't set such + * limit - all connection of one pair of source and dest IP will be written to + * each file 7) Connection - splits a pcap file to smaller pcap files by TCP/UDP + * connection meaning each connection will be written to a certain file. The + * user can limit the number of output files, in this case an equal number of + * connections will be written to the same file. If the user doesn't set such + * limit - each file will contain one connection 8) BPF filter - splits the pcap + * file into two files: one that contains all packets matching the input BPF + * filter and the other one with the rest of the packets * * Remarks: * - Options 3-7 supports both IPv4 and IPV6 - * - Number of output files isn't limited, unless the user set such limit in options 3-7 - * - There is no limit on the size of the input file, the number of packets it contains or the number of connections it - * contains - * - The user can also set a BPF filter to instruct the application to handle only packets filtered by the filter. The rest - * of the packets in the input file will be ignored - * - In options 3-5 & 7 all packets which aren't UDP or TCP (hence don't belong to any connection) will be written to - * one output file, separate from the other output files (usually file#0) + * - Number of output files isn't limited, unless the user set such limit in + * options 3-7 + * - There is no limit on the size of the input file, the number of packets it + * contains or the number of connections it contains + * - The user can also set a BPF filter to instruct the application to handle + * only packets filtered by the filter. The rest of the packets in the input + * file will be ignored + * - In options 3-5 & 7 all packets which aren't UDP or TCP (hence don't belong + * to any connection) will be written to one output file, separate from the + * other output files (usually file#0) * - Works only on files of the pcap (TCPDUMP) format * */ - -#include -#include -#include -#include -#include -#include -#include -#include +#include "ConnectionSplitters.h" +#include "IPPortSplitters.h" +#include "SimpleSplitters.h" #include #include -#include "SimpleSplitters.h" -#include "IPPortSplitters.h" -#include "ConnectionSplitters.h" -#include -#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include - -static struct option PcapSplitterOptions[] = -{ - {"input-file", required_argument, nullptr, 'f'}, - {"output-file", required_argument, nullptr, 'o'}, - {"method", required_argument, nullptr, 'm'}, - {"param", required_argument, nullptr, 'p'}, - {"filter", required_argument, nullptr, 'i'}, - {"help", no_argument, nullptr, 'h'}, - {"version", no_argument, nullptr, 'v'}, - {nullptr, 0, nullptr, 0} -}; - - -#define EXIT_WITH_ERROR(reason) do { \ - printUsage(); \ - std::cout << std::endl << "ERROR: " << reason << std::endl << std::endl; \ - exit(1); \ - } while(0) - - -#define SPLIT_BY_FILE_SIZE "file-size" -#define SPLIT_BY_PACKET_COUNT "packet-count" -#define SPLIT_BY_IP_CLIENT "client-ip" -#define SPLIT_BY_IP_SERVER "server-ip" -#define SPLIT_BY_SERVER_PORT "server-port" -#define SPLIT_BY_CLIENT_PORT "client-port" -#define SPLIT_BY_2_TUPLE "ip-src-dst" -#define SPLIT_BY_5_TUPLE "connection" -#define SPLIT_BY_BPF_FILTER "bpf-filter" -#define SPLIT_BY_ROUND_ROBIN "round-robin" - +static struct option PcapSplitterOptions[] = { + {"input-file", required_argument, nullptr, 'f'}, + {"output-file", required_argument, nullptr, 'o'}, + {"method", required_argument, nullptr, 'm'}, + {"param", required_argument, nullptr, 'p'}, + {"filter", required_argument, nullptr, 'i'}, + {"help", no_argument, nullptr, 'h'}, + {"version", no_argument, nullptr, 'v'}, + {nullptr, 0, nullptr, 0}}; + +#define EXIT_WITH_ERROR(reason) \ + do { \ + printUsage(); \ + std::cout << std::endl \ + << "ERROR: " << reason << std::endl \ + << std::endl; \ + exit(1); \ + } while (0) + +#define SPLIT_BY_FILE_SIZE "file-size" +#define SPLIT_BY_PACKET_COUNT "packet-count" +#define SPLIT_BY_IP_CLIENT "client-ip" +#define SPLIT_BY_IP_SERVER "server-ip" +#define SPLIT_BY_SERVER_PORT "server-port" +#define SPLIT_BY_CLIENT_PORT "client-port" +#define SPLIT_BY_2_TUPLE "ip-src-dst" +#define SPLIT_BY_5_TUPLE "connection" +#define SPLIT_BY_BPF_FILTER "bpf-filter" +#define SPLIT_BY_ROUND_ROBIN "round-robin" #if defined(_WIN32) #define SEPARATOR '\\' @@ -99,388 +108,447 @@ static struct option PcapSplitterOptions[] = #define SEPARATOR '/' #endif - /** * Print application usage */ -void printUsage() -{ - std::cout << std::endl - << "Usage:" << std::endl - << "------" << std::endl - << pcpp::AppName::get() << " [-h] [-v] [-i filter] -f pcap_file -o output_dir -m split_method [-p split_param]" << std::endl - << std::endl - << "Options:" << std::endl - << std::endl - << " -f pcap_file : Input pcap file name" << std::endl - << " -o output_dir : The directory where the output files shall be written" << std::endl - << " -m split_method : The method to split with. Can take one of the following params:" << std::endl - << " 'file-size' - split files by size in bytes" << std::endl - << " 'packet-count' - split files by packet count" << std::endl - << " 'client-ip' - split files by client IP, meaning all connections with" << std::endl - << " the same client IP will be in the same file" << std::endl - << " 'server-ip' - split files by server IP, meaning all connections with" << std::endl - << " the same server IP will be in the same file" << std::endl - << " 'server-port' - split files by server port, meaning all connections with" << std::endl - << " the same server port will be in the same file" << std::endl - << " 'client-port' - split files by client port, meaning all connections with" << std::endl - << " the same client port will be in the same file" << std::endl - << " 'ip-src-dst' - split files by IP src and dst (2-tuple), meaning all connections" << std::endl - << " with the same IPs will be in the same file" << std::endl - << " 'connection' - split files by connection (5-tuple), meaning all packets" << std::endl - << " of a connection will be in the same file" << std::endl - << " 'bpf-filter' - split file into two files: one that contains all packets" << std::endl - << " matching the given BPF filter (file #0) and one that contains" << std::endl - << " the rest of the packets (file #1)" << std::endl - << " 'round-robin' - split the file in a round-robin manner - each packet to a" << std::endl - << " different file" << std::endl - << " -p split-param : The relevant parameter for the split method:" << std::endl - << " 'method = file-size' => split-param is the max size per file (in bytes)." << std::endl - << " split-param is required for this method" << std::endl - << " 'method = packet-count' => split-param is the number of packet per file." << std::endl - << " split-param is required for this method" << std::endl - << " 'method = client-ip' => split-param is max number of files to open." << std::endl - << " If not provided the default is unlimited number of files" << std::endl - << " 'method = server-ip' => split-param is max number of files to open." << std::endl - << " If not provided the default is unlimited number of files" << std::endl - << " 'method = server-port' => split-param is max number of files to open." << std::endl - << " If not provided the default is unlimited number of files" << std::endl - << " 'method = ip-src-dst' => split-param is max number of files to open." << std::endl - << " If not provided the default is unlimited number of files" << std::endl - << " 'method = connection' => split-param is max number of files to open." << std::endl - << " If not provided the default is unlimited number of files" << std::endl - << " 'method = bpf-filter' => split-param is the BPF filter to match upon" << std::endl - << " 'method = round-robin' => split-param is number of files to round-robin packets between" << std::endl - << " -i filter : Apply a BPF filter, meaning only filtered packets will be counted in the split" << std::endl - << " -v : Displays the current version and exists" << std::endl - << " -h : Displays this help message and exits" << std::endl - << std::endl; +void printUsage() { + std::cout + << std::endl + << "Usage:" << std::endl + << "------" << std::endl + << pcpp::AppName::get() + << " [-h] [-v] [-i filter] -f pcap_file -o output_dir -m split_method " + "[-p split_param]" + << std::endl + << std::endl + << "Options:" << std::endl + << std::endl + << " -f pcap_file : Input pcap file name" << std::endl + << " -o output_dir : The directory where the output files shall be " + "written" + << std::endl + << " -m split_method : The method to split with. Can take one of the " + "following params:" + << std::endl + << " 'file-size' - split files by size in bytes" + << std::endl + << " 'packet-count' - split files by packet count" + << std::endl + << " 'client-ip' - split files by client IP, " + "meaning all connections with" + << std::endl + << " the same client IP will be in " + "the same file" + << std::endl + << " 'server-ip' - split files by server IP, " + "meaning all connections with" + << std::endl + << " the same server IP will be in " + "the same file" + << std::endl + << " 'server-port' - split files by server port, " + "meaning all connections with" + << std::endl + << " the same server port will be " + "in the same file" + << std::endl + << " 'client-port' - split files by client port, " + "meaning all connections with" + << std::endl + << " the same client port will be " + "in the same file" + << std::endl + << " 'ip-src-dst' - split files by IP src and dst " + "(2-tuple), meaning all connections" + << std::endl + << " with the same IPs will be in " + "the same file" + << std::endl + << " 'connection' - split files by connection " + "(5-tuple), meaning all packets" + << std::endl + << " of a connection will be in " + "the same file" + << std::endl + << " 'bpf-filter' - split file into two files: " + "one that contains all packets" + << std::endl + << " matching the given BPF filter " + "(file #0) and one that contains" + << std::endl + << " the rest of the packets (file " + "#1)" + << std::endl + << " 'round-robin' - split the file in a " + "round-robin manner - each packet to a" + << std::endl + << " different file" << std::endl + << " -p split-param : The relevant parameter for the split method:" + << std::endl + << " 'method = file-size' => split-param is the " + "max size per file (in bytes)." + << std::endl + << " split-param is " + "required for this method" + << std::endl + << " 'method = packet-count' => split-param is the " + "number of packet per file." + << std::endl + << " split-param is " + "required for this method" + << std::endl + << " 'method = client-ip' => split-param is max " + "number of files to open." + << std::endl + << " If not provided the " + "default is unlimited number of files" + << std::endl + << " 'method = server-ip' => split-param is max " + "number of files to open." + << std::endl + << " If not provided the " + "default is unlimited number of files" + << std::endl + << " 'method = server-port' => split-param is max " + "number of files to open." + << std::endl + << " If not provided the " + "default is unlimited number of files" + << std::endl + << " 'method = ip-src-dst' => split-param is max " + "number of files to open." + << std::endl + << " If not provided the " + "default is unlimited number of files" + << std::endl + << " 'method = connection' => split-param is max " + "number of files to open." + << std::endl + << " If not provided the " + "default is unlimited number of files" + << std::endl + << " 'method = bpf-filter' => split-param is the " + "BPF filter to match upon" + << std::endl + << " 'method = round-robin' => split-param is " + "number of files to round-robin packets between" + << std::endl + << " -i filter : Apply a BPF filter, meaning only filtered " + "packets will be counted in the split" + << std::endl + << " -v : Displays the current version and exists" + << std::endl + << " -h : Displays this help message and exits" + << std::endl + << std::endl; } - /** * Print application version */ -void printAppVersion() -{ - std::cout - << pcpp::AppName::get() << " " << pcpp::getPcapPlusPlusVersionFull() << std::endl - << "Built: " << pcpp::getBuildDateTime() << std::endl - << "Built from: " << pcpp::getGitInfo() << std::endl; - exit(0); +void printAppVersion() { + std::cout << pcpp::AppName::get() << " " << pcpp::getPcapPlusPlusVersionFull() + << std::endl + << "Built: " << pcpp::getBuildDateTime() << std::endl + << "Built from: " << pcpp::getGitInfo() << std::endl; + exit(0); } - /** - * An auxiliary method for extracting the file name without the extension from a file path, - * for example: for the input '/home/myuser/mypcap.pcap' -> return value will be 'mypcap' + * An auxiliary method for extracting the file name without the extension from a + * file path, for example: for the input '/home/myuser/mypcap.pcap' -> return + * value will be 'mypcap' */ -std::string getFileNameWithoutExtension(const std::string& path) -{ - // if path is empty, return an empty string - if (path == "") - return ""; - - // find the last "\\" or "/" (depends on the os) - where path ends and filename starts - size_t i = path.rfind(SEPARATOR, path.length()); - if (i != std::string::npos) - { - // extract filename from path - std::string fileNameWithExtension = path.substr(i+1, path.length() - i); - - // from the file name - remove the extension (the part after the ".") - i = fileNameWithExtension.rfind('.', fileNameWithExtension.length()); - if (i != std::string::npos) - return fileNameWithExtension.substr(0, i); - - return fileNameWithExtension; - } - // filename without a path - else - { - // from the file name - remove the extension (the part after the ".") - i = path.rfind('.', path.length()); - if (i != std::string::npos) - return path.substr(0, i); - - // filename doesn't have an extension - return path; - } - - return(""); +std::string getFileNameWithoutExtension(const std::string& path) { + // if path is empty, return an empty string + if (path == "") + return ""; + + // find the last "\\" or "/" (depends on the os) - where path ends and + // filename starts + size_t i = path.rfind(SEPARATOR, path.length()); + if (i != std::string::npos) { + // extract filename from path + std::string fileNameWithExtension = path.substr(i + 1, path.length() - i); + + // from the file name - remove the extension (the part after the ".") + i = fileNameWithExtension.rfind('.', fileNameWithExtension.length()); + if (i != std::string::npos) + return fileNameWithExtension.substr(0, i); + + return fileNameWithExtension; + } + // filename without a path + else { + // from the file name - remove the extension (the part after the ".") + i = path.rfind('.', path.length()); + if (i != std::string::npos) + return path.substr(0, i); + + // filename doesn't have an extension + return path; + } + + return (""); } - /** * main method of this utility */ -int main(int argc, char* argv[]) -{ - pcpp::AppName::init(argc, argv); - - std::string inputPcapFileName = ""; - std::string outputPcapDir = ""; - - std::string filter = ""; - - std::string method = ""; - - char param[1000]; - memset(param, 0, 1000); - - bool paramWasSet = false; - - int optionIndex = 0; - int opt = 0; - - while((opt = getopt_long(argc, argv, "f:o:m:p:i:vh", PcapSplitterOptions, &optionIndex)) != -1) - { - switch (opt) - { - case 0: - break; - case 'f': - inputPcapFileName = optarg; - break; - case 'o': - outputPcapDir = optarg; - break; - case 'm': - method = optarg; - break; - case 'p': - strncpy(param, optarg, 999); - paramWasSet = true; - break; - case 'i': - filter = optarg; - break; - case 'h': - printUsage(); - exit(0); - case 'v': - printAppVersion(); - break; - default: - printUsage(); - exit(-1); - } - } - - if (inputPcapFileName == "") - { - EXIT_WITH_ERROR("Input file name was not given"); - } - - if (outputPcapDir == "") - { - EXIT_WITH_ERROR("Output directory name was not given"); - } - - if (!pcpp::directoryExists(outputPcapDir)) - { - EXIT_WITH_ERROR("Output directory doesn't exist"); - } - - if (method == "") - { - EXIT_WITH_ERROR("Split method was not given"); - } - - Splitter* splitter = nullptr; - - // decide of the splitter to use, according to the user's choice - if (method == SPLIT_BY_FILE_SIZE) - { - uint64_t paramAsUint64 = (paramWasSet ? strtoull(param, nullptr, 10) : 0); - splitter = new FileSizeSplitter(paramAsUint64); - } - else if (method == SPLIT_BY_PACKET_COUNT) - { - int paramAsInt = (paramWasSet ? atoi(param) : 0); - splitter = new PacketCountSplitter(paramAsInt); - } - else if (method == SPLIT_BY_IP_CLIENT) - { - int paramAsInt = (paramWasSet ? atoi(param) : SplitterWithMaxFiles::UNLIMITED_FILES_MAGIC_NUMBER); - splitter = new ClientIPSplitter(paramAsInt); - } - else if (method == SPLIT_BY_IP_SERVER) - { - int paramAsInt = (paramWasSet ? atoi(param) : SplitterWithMaxFiles::UNLIMITED_FILES_MAGIC_NUMBER); - splitter = new ServerIPSplitter(paramAsInt); - } - else if (method == SPLIT_BY_SERVER_PORT) - { - int paramAsInt = (paramWasSet ? atoi(param) : SplitterWithMaxFiles::UNLIMITED_FILES_MAGIC_NUMBER); - splitter = new ServerPortSplitter(paramAsInt); - } - else if (method == SPLIT_BY_CLIENT_PORT) - { - int paramAsInt = (paramWasSet ? atoi(param) : SplitterWithMaxFiles::UNLIMITED_FILES_MAGIC_NUMBER); - splitter = new ClientPortSplitter(paramAsInt); - } - else if (method == SPLIT_BY_2_TUPLE) - { - int paramAsInt = (paramWasSet ? atoi(param) : SplitterWithMaxFiles::UNLIMITED_FILES_MAGIC_NUMBER); - splitter = new TwoTupleSplitter(paramAsInt); - } - else if (method == SPLIT_BY_5_TUPLE) - { - int paramAsInt = (paramWasSet ? atoi(param) : SplitterWithMaxFiles::UNLIMITED_FILES_MAGIC_NUMBER); - splitter = new FiveTupleSplitter(paramAsInt); - } - else if (method == SPLIT_BY_BPF_FILTER) - { - splitter = new BpfCriteriaSplitter(std::string(param)); - } - else if (method == SPLIT_BY_ROUND_ROBIN) - { - int paramAsInt = (paramWasSet ? atoi(param) : 0); - splitter = new RoundRobinSplitter(paramAsInt); - } - else - EXIT_WITH_ERROR("Unknown method '" << method << "'"); - - - // verify splitter param is legal, otherwise return an error - std::string errorStr; - if (!splitter->isSplitterParamLegal(errorStr)) - { - delete splitter; - EXIT_WITH_ERROR(errorStr); - } - - // prepare the output file format: /requested-path/original-file-name-[4-digit-number-starting-at-0000].pcap - std::string outputPcapFileName = outputPcapDir + std::string(1, SEPARATOR) + getFileNameWithoutExtension(inputPcapFileName) + "-"; - - // open a pcap file for reading - pcpp::IFileReaderDevice* reader = pcpp::IFileReaderDevice::getReader(inputPcapFileName); - bool isReaderPcapng = (dynamic_cast(reader) != nullptr); - - if (reader == nullptr || !reader->open()) - { - EXIT_WITH_ERROR("Error opening input pcap file"); - } - - // set a filter if provided - if (filter != "") - { - if (!reader->setFilter(filter)) - EXIT_WITH_ERROR("Couldn't set filter '" << filter << "'"); - } - - std::cout << "Started..." << std::endl; - - // determine output file extension - std::string outputFileExtenison = (isReaderPcapng ? ".pcapng" : ".pcap"); - - int packetCountSoFar = 0; - int numOfFiles = 0; - pcpp::RawPacket rawPacket; - - // prepare a map of file number to IFileWriterDevice - std::map outputFiles; - - // read all packets from input file, for each packet do: - while (reader->getNextPacket(rawPacket)) - { - // parse the raw packet into a parsed packet - pcpp::Packet parsedPacket(&rawPacket); - - std::vector filesToClose; - - // call the splitter to get the file number to write the current packet to - int fileNum = splitter->getFileNumber(parsedPacket, filesToClose); - - // if file number is seen for the first time (meaning it's the first packet written to it) - if (outputFiles.find(fileNum) == outputFiles.end()) - { - // get file name from the splitter and add the .pcap extension - std::string fileName = splitter->getFileName(parsedPacket, outputPcapFileName, fileNum) + outputFileExtenison; - - // create a new IFileWriterDevice for this file - if (isReaderPcapng) - { - // if reader is pcapng, create a pcapng writer - outputFiles[fileNum] = new pcpp::PcapNgFileWriterDevice(fileName); - } - else - { - // if reader is pcap, create a pcap writer - outputFiles[fileNum] = new pcpp::PcapFileWriterDevice(fileName, rawPacket.getLinkLayerType()); - } - - // open the writer - if (!outputFiles[fileNum]->open()) - break; - - numOfFiles++; - } - - // if file number exists in the map but PcapFileWriterDevice is null it means this file was open once and - // then closed. In this case we need to re-open the PcapFileWriterDevice in append mode - else if (outputFiles[fileNum] == nullptr) - { - // get file name from the splitter and add the .pcap extension - std::string fileName = splitter->getFileName(parsedPacket, outputPcapFileName, fileNum) + outputFileExtenison; - - // re-create the IFileWriterDevice object - if (isReaderPcapng) - { - // if reader is pcapng, create a pcapng writer - outputFiles[fileNum] = new pcpp::PcapNgFileWriterDevice(fileName); - } - else - { - // if reader is pcap, create a pcap writer - outputFiles[fileNum] = new pcpp::PcapFileWriterDevice(fileName, rawPacket.getLinkLayerType()); - } - - // open the writer in __append__ mode - if (!outputFiles[fileNum]->open(true)) - break; - } - - // write the packet to the writer - outputFiles[fileNum]->writePacket(*parsedPacket.getRawPacket()); - - // if splitter wants us to close files - go over the file numbers and close them - for (std::vector::iterator it = filesToClose.begin(); it != filesToClose.end(); it++) - { - // check if that file number is in the map - if (outputFiles.find(*it) != outputFiles.end()) - { - // close the writer - outputFiles[*it]->close(); - - // free the writer memory and put null in the map record - delete outputFiles[*it]; - outputFiles[*it] = nullptr; - } - } - - packetCountSoFar++; - } - - std::cout << "Finished. Read and written " << packetCountSoFar << " packets to " << numOfFiles << " files" << std::endl; - - // close the reader file - reader->close(); - - // free reader memory - delete reader; - delete splitter; - - // close the writer files which are still open - for(std::map::iterator it = outputFiles.begin(); it != outputFiles.end(); ++it) - { - if (it->second != NULL) - { - it->second->close(); - delete it->second; - } - } - - return 0; +int main(int argc, char* argv[]) { + pcpp::AppName::init(argc, argv); + + std::string inputPcapFileName = ""; + std::string outputPcapDir = ""; + + std::string filter = ""; + + std::string method = ""; + + char param[1000]; + memset(param, 0, 1000); + + bool paramWasSet = false; + + int optionIndex = 0; + int opt = 0; + + while ((opt = getopt_long(argc, argv, "f:o:m:p:i:vh", PcapSplitterOptions, + &optionIndex)) != -1) { + switch (opt) { + case 0: + break; + case 'f': + inputPcapFileName = optarg; + break; + case 'o': + outputPcapDir = optarg; + break; + case 'm': + method = optarg; + break; + case 'p': + strncpy(param, optarg, 999); + paramWasSet = true; + break; + case 'i': + filter = optarg; + break; + case 'h': + printUsage(); + exit(0); + case 'v': + printAppVersion(); + break; + default: + printUsage(); + exit(-1); + } + } + + if (inputPcapFileName == "") { + EXIT_WITH_ERROR("Input file name was not given"); + } + + if (outputPcapDir == "") { + EXIT_WITH_ERROR("Output directory name was not given"); + } + + if (!pcpp::directoryExists(outputPcapDir)) { + EXIT_WITH_ERROR("Output directory doesn't exist"); + } + + if (method == "") { + EXIT_WITH_ERROR("Split method was not given"); + } + + Splitter* splitter = nullptr; + + // decide of the splitter to use, according to the user's choice + if (method == SPLIT_BY_FILE_SIZE) { + uint64_t paramAsUint64 = (paramWasSet ? strtoull(param, nullptr, 10) : 0); + splitter = new FileSizeSplitter(paramAsUint64); + } else if (method == SPLIT_BY_PACKET_COUNT) { + int paramAsInt = (paramWasSet ? atoi(param) : 0); + splitter = new PacketCountSplitter(paramAsInt); + } else if (method == SPLIT_BY_IP_CLIENT) { + int paramAsInt = + (paramWasSet ? atoi(param) + : SplitterWithMaxFiles::UNLIMITED_FILES_MAGIC_NUMBER); + splitter = new ClientIPSplitter(paramAsInt); + } else if (method == SPLIT_BY_IP_SERVER) { + int paramAsInt = + (paramWasSet ? atoi(param) + : SplitterWithMaxFiles::UNLIMITED_FILES_MAGIC_NUMBER); + splitter = new ServerIPSplitter(paramAsInt); + } else if (method == SPLIT_BY_SERVER_PORT) { + int paramAsInt = + (paramWasSet ? atoi(param) + : SplitterWithMaxFiles::UNLIMITED_FILES_MAGIC_NUMBER); + splitter = new ServerPortSplitter(paramAsInt); + } else if (method == SPLIT_BY_CLIENT_PORT) { + int paramAsInt = + (paramWasSet ? atoi(param) + : SplitterWithMaxFiles::UNLIMITED_FILES_MAGIC_NUMBER); + splitter = new ClientPortSplitter(paramAsInt); + } else if (method == SPLIT_BY_2_TUPLE) { + int paramAsInt = + (paramWasSet ? atoi(param) + : SplitterWithMaxFiles::UNLIMITED_FILES_MAGIC_NUMBER); + splitter = new TwoTupleSplitter(paramAsInt); + } else if (method == SPLIT_BY_5_TUPLE) { + int paramAsInt = + (paramWasSet ? atoi(param) + : SplitterWithMaxFiles::UNLIMITED_FILES_MAGIC_NUMBER); + splitter = new FiveTupleSplitter(paramAsInt); + } else if (method == SPLIT_BY_BPF_FILTER) { + splitter = new BpfCriteriaSplitter(std::string(param)); + } else if (method == SPLIT_BY_ROUND_ROBIN) { + int paramAsInt = (paramWasSet ? atoi(param) : 0); + splitter = new RoundRobinSplitter(paramAsInt); + } else + EXIT_WITH_ERROR("Unknown method '" << method << "'"); + + // verify splitter param is legal, otherwise return an error + std::string errorStr; + if (!splitter->isSplitterParamLegal(errorStr)) { + delete splitter; + EXIT_WITH_ERROR(errorStr); + } + + // prepare the output file format: + // /requested-path/original-file-name-[4-digit-number-starting-at-0000].pcap + std::string outputPcapFileName = + outputPcapDir + std::string(1, SEPARATOR) + + getFileNameWithoutExtension(inputPcapFileName) + "-"; + + // open a pcap file for reading + pcpp::IFileReaderDevice* reader = + pcpp::IFileReaderDevice::getReader(inputPcapFileName); + bool isReaderPcapng = + (dynamic_cast(reader) != nullptr); + + if (reader == nullptr || !reader->open()) { + EXIT_WITH_ERROR("Error opening input pcap file"); + } + + // set a filter if provided + if (filter != "") { + if (!reader->setFilter(filter)) + EXIT_WITH_ERROR("Couldn't set filter '" << filter << "'"); + } + + std::cout << "Started..." << std::endl; + + // determine output file extension + std::string outputFileExtenison = (isReaderPcapng ? ".pcapng" : ".pcap"); + + int packetCountSoFar = 0; + int numOfFiles = 0; + pcpp::RawPacket rawPacket; + + // prepare a map of file number to IFileWriterDevice + std::map outputFiles; + + // read all packets from input file, for each packet do: + while (reader->getNextPacket(rawPacket)) { + // parse the raw packet into a parsed packet + pcpp::Packet parsedPacket(&rawPacket); + + std::vector filesToClose; + + // call the splitter to get the file number to write the current packet to + int fileNum = splitter->getFileNumber(parsedPacket, filesToClose); + + // if file number is seen for the first time (meaning it's the first packet + // written to it) + if (outputFiles.find(fileNum) == outputFiles.end()) { + // get file name from the splitter and add the .pcap extension + std::string fileName = + splitter->getFileName(parsedPacket, outputPcapFileName, fileNum) + + outputFileExtenison; + + // create a new IFileWriterDevice for this file + if (isReaderPcapng) { + // if reader is pcapng, create a pcapng writer + outputFiles[fileNum] = new pcpp::PcapNgFileWriterDevice(fileName); + } else { + // if reader is pcap, create a pcap writer + outputFiles[fileNum] = new pcpp::PcapFileWriterDevice( + fileName, rawPacket.getLinkLayerType()); + } + + // open the writer + if (!outputFiles[fileNum]->open()) + break; + + numOfFiles++; + } + + // if file number exists in the map but PcapFileWriterDevice is null it + // means this file was open once and then closed. In this case we need to + // re-open the PcapFileWriterDevice in append mode + else if (outputFiles[fileNum] == nullptr) { + // get file name from the splitter and add the .pcap extension + std::string fileName = + splitter->getFileName(parsedPacket, outputPcapFileName, fileNum) + + outputFileExtenison; + + // re-create the IFileWriterDevice object + if (isReaderPcapng) { + // if reader is pcapng, create a pcapng writer + outputFiles[fileNum] = new pcpp::PcapNgFileWriterDevice(fileName); + } else { + // if reader is pcap, create a pcap writer + outputFiles[fileNum] = new pcpp::PcapFileWriterDevice( + fileName, rawPacket.getLinkLayerType()); + } + + // open the writer in __append__ mode + if (!outputFiles[fileNum]->open(true)) + break; + } + + // write the packet to the writer + outputFiles[fileNum]->writePacket(*parsedPacket.getRawPacket()); + + // if splitter wants us to close files - go over the file numbers and close + // them + for (std::vector::iterator it = filesToClose.begin(); + it != filesToClose.end(); it++) { + // check if that file number is in the map + if (outputFiles.find(*it) != outputFiles.end()) { + // close the writer + outputFiles[*it]->close(); + + // free the writer memory and put null in the map record + delete outputFiles[*it]; + outputFiles[*it] = nullptr; + } + } + + packetCountSoFar++; + } + + std::cout << "Finished. Read and written " << packetCountSoFar + << " packets to " << numOfFiles << " files" << std::endl; + + // close the reader file + reader->close(); + + // free reader memory + delete reader; + delete splitter; + + // close the writer files which are still open + for (std::map::iterator it = + outputFiles.begin(); + it != outputFiles.end(); ++it) { + if (it->second != NULL) { + it->second->close(); + delete it->second; + } + } + + return 0; } diff --git a/Examples/PfRingExample-FilterTraffic/Common.h b/Examples/PfRingExample-FilterTraffic/Common.h index f54a7bc4d5..cc9487698e 100644 --- a/Examples/PfRingExample-FilterTraffic/Common.h +++ b/Examples/PfRingExample-FilterTraffic/Common.h @@ -5,145 +5,161 @@ #include -#include -#include #include #include #include #include - +#include +#include /** * Macros for exiting the application with error */ -#define EXIT_WITH_ERROR(reason) do { \ - std::cout << std::endl << "ERROR: " << reason << std::endl << std::endl; \ - exit(1); \ - } while(0) - -#define EXIT_WITH_ERROR_AND_PRINT_USAGE(reason) do { \ - printUsage(); \ - std::cout << std::endl << "ERROR: " << reason << std::endl << std::endl; \ - exit(1); \ - } while (0) +#define EXIT_WITH_ERROR(reason) \ + do { \ + std::cout << std::endl \ + << "ERROR: " << reason << std::endl \ + << std::endl; \ + exit(1); \ + } while (0) + +#define EXIT_WITH_ERROR_AND_PRINT_USAGE(reason) \ + do { \ + printUsage(); \ + std::cout << std::endl \ + << "ERROR: " << reason << std::endl \ + << std::endl; \ + exit(1); \ + } while (0) /** * Collect and analyze packet and flow statistics */ -struct PacketStats -{ -public: - uint8_t ThreadId; - - int PacketCount; - int EthCount; - int ArpCount; - int Ip4Count; - int Ip6Count; - int TcpCount; - int UdpCount; - int HttpCount; - - int MatchedTcpFlows; - int MatchedUdpFlows; - int MatchedPackets; - - PacketStats() : ThreadId(MAX_NUM_OF_CORES+1), PacketCount(0), EthCount(0), ArpCount(0), Ip4Count(0), Ip6Count(0), TcpCount(0), UdpCount(0), HttpCount(0), MatchedTcpFlows(0), MatchedUdpFlows(0), MatchedPackets(0) {} - - void collectStats(pcpp::Packet& packet) - { - PacketCount++; - if (packet.isPacketOfType(pcpp::Ethernet)) - EthCount++; - if (packet.isPacketOfType(pcpp::ARP)) - ArpCount++; - if (packet.isPacketOfType(pcpp::IPv4)) - Ip4Count++; - if (packet.isPacketOfType(pcpp::IPv6)) - Ip6Count++; - if (packet.isPacketOfType(pcpp::TCP)) - TcpCount++; - if (packet.isPacketOfType(pcpp::UDP)) - UdpCount++; - if (packet.isPacketOfType(pcpp::HTTP)) - HttpCount++; - } - - void collectStats(const PacketStats& stats) - { - PacketCount += stats.PacketCount; - EthCount += stats.EthCount; - ArpCount += stats.ArpCount; - Ip4Count += stats.Ip4Count; - Ip6Count += stats.Ip6Count; - TcpCount += stats.TcpCount; - UdpCount += stats.UdpCount; - HttpCount += stats.HttpCount; - - MatchedTcpFlows += stats.MatchedTcpFlows; - MatchedUdpFlows += stats.MatchedUdpFlows; - MatchedPackets += stats.MatchedPackets; - } - - void clear() { ThreadId = MAX_NUM_OF_CORES+1; PacketCount = 0; EthCount = 0; ArpCount = 0; Ip4Count = 0; Ip6Count = 0; TcpCount = 0; UdpCount = 0; HttpCount = 0; MatchedTcpFlows = 0; MatchedUdpFlows = 0; MatchedPackets = 0; } - - std::string getStatValuesAsString(const std::string &delimiter) - { - std::stringstream values; - if (ThreadId == MAX_NUM_OF_CORES+1) - values << "Total" << delimiter; - else - values << (int)ThreadId << delimiter; - values << PacketCount << delimiter; - values << EthCount << delimiter; - values << ArpCount << delimiter; - values << Ip4Count << delimiter; - values << Ip6Count << delimiter; - values << TcpCount << delimiter; - values << UdpCount << delimiter; - values << HttpCount << delimiter; - values << MatchedTcpFlows << delimiter; - values << MatchedUdpFlows << delimiter; - values << MatchedPackets; - - return values.str(); - } - - - static void getStatsColumns(std::vector& columnNames, std::vector& columnWidths) - { - columnNames.clear(); - columnWidths.clear(); - - static const int narrowColumnWidth = 11; - static const int wideColumnWidth = 18; - - columnNames.push_back("Core ID"); - columnNames.push_back("Packet Cnt"); - columnNames.push_back("Eth Cnt"); - columnNames.push_back("ARP Cnt"); - columnNames.push_back("IPv4 Cnt"); - columnNames.push_back("IPv6 Cnt"); - columnNames.push_back("TCP Cnt"); - columnNames.push_back("UDP Cnt"); - columnNames.push_back("HTTP Cnt"); - columnNames.push_back("Matched TCP Flows"); - columnNames.push_back("Matched UDP Flows"); - columnNames.push_back("Matched Packets"); - - columnWidths.push_back(7); - columnWidths.push_back(narrowColumnWidth); - columnWidths.push_back(narrowColumnWidth); - columnWidths.push_back(narrowColumnWidth); - columnWidths.push_back(narrowColumnWidth); - columnWidths.push_back(narrowColumnWidth); - columnWidths.push_back(narrowColumnWidth); - columnWidths.push_back(narrowColumnWidth); - columnWidths.push_back(narrowColumnWidth); - columnWidths.push_back(wideColumnWidth); - columnWidths.push_back(wideColumnWidth); - columnWidths.push_back(wideColumnWidth); - - } +struct PacketStats { + public: + uint8_t ThreadId; + + int PacketCount; + int EthCount; + int ArpCount; + int Ip4Count; + int Ip6Count; + int TcpCount; + int UdpCount; + int HttpCount; + + int MatchedTcpFlows; + int MatchedUdpFlows; + int MatchedPackets; + + PacketStats() + : ThreadId(MAX_NUM_OF_CORES + 1), PacketCount(0), EthCount(0), + ArpCount(0), Ip4Count(0), Ip6Count(0), TcpCount(0), UdpCount(0), + HttpCount(0), MatchedTcpFlows(0), MatchedUdpFlows(0), + MatchedPackets(0) {} + + void collectStats(pcpp::Packet& packet) { + PacketCount++; + if (packet.isPacketOfType(pcpp::Ethernet)) + EthCount++; + if (packet.isPacketOfType(pcpp::ARP)) + ArpCount++; + if (packet.isPacketOfType(pcpp::IPv4)) + Ip4Count++; + if (packet.isPacketOfType(pcpp::IPv6)) + Ip6Count++; + if (packet.isPacketOfType(pcpp::TCP)) + TcpCount++; + if (packet.isPacketOfType(pcpp::UDP)) + UdpCount++; + if (packet.isPacketOfType(pcpp::HTTP)) + HttpCount++; + } + + void collectStats(const PacketStats& stats) { + PacketCount += stats.PacketCount; + EthCount += stats.EthCount; + ArpCount += stats.ArpCount; + Ip4Count += stats.Ip4Count; + Ip6Count += stats.Ip6Count; + TcpCount += stats.TcpCount; + UdpCount += stats.UdpCount; + HttpCount += stats.HttpCount; + + MatchedTcpFlows += stats.MatchedTcpFlows; + MatchedUdpFlows += stats.MatchedUdpFlows; + MatchedPackets += stats.MatchedPackets; + } + + void clear() { + ThreadId = MAX_NUM_OF_CORES + 1; + PacketCount = 0; + EthCount = 0; + ArpCount = 0; + Ip4Count = 0; + Ip6Count = 0; + TcpCount = 0; + UdpCount = 0; + HttpCount = 0; + MatchedTcpFlows = 0; + MatchedUdpFlows = 0; + MatchedPackets = 0; + } + + std::string getStatValuesAsString(const std::string& delimiter) { + std::stringstream values; + if (ThreadId == MAX_NUM_OF_CORES + 1) + values << "Total" << delimiter; + else + values << (int)ThreadId << delimiter; + values << PacketCount << delimiter; + values << EthCount << delimiter; + values << ArpCount << delimiter; + values << Ip4Count << delimiter; + values << Ip6Count << delimiter; + values << TcpCount << delimiter; + values << UdpCount << delimiter; + values << HttpCount << delimiter; + values << MatchedTcpFlows << delimiter; + values << MatchedUdpFlows << delimiter; + values << MatchedPackets; + + return values.str(); + } + + static void getStatsColumns(std::vector& columnNames, + std::vector& columnWidths) { + columnNames.clear(); + columnWidths.clear(); + + static const int narrowColumnWidth = 11; + static const int wideColumnWidth = 18; + + columnNames.push_back("Core ID"); + columnNames.push_back("Packet Cnt"); + columnNames.push_back("Eth Cnt"); + columnNames.push_back("ARP Cnt"); + columnNames.push_back("IPv4 Cnt"); + columnNames.push_back("IPv6 Cnt"); + columnNames.push_back("TCP Cnt"); + columnNames.push_back("UDP Cnt"); + columnNames.push_back("HTTP Cnt"); + columnNames.push_back("Matched TCP Flows"); + columnNames.push_back("Matched UDP Flows"); + columnNames.push_back("Matched Packets"); + + columnWidths.push_back(7); + columnWidths.push_back(narrowColumnWidth); + columnWidths.push_back(narrowColumnWidth); + columnWidths.push_back(narrowColumnWidth); + columnWidths.push_back(narrowColumnWidth); + columnWidths.push_back(narrowColumnWidth); + columnWidths.push_back(narrowColumnWidth); + columnWidths.push_back(narrowColumnWidth); + columnWidths.push_back(narrowColumnWidth); + columnWidths.push_back(wideColumnWidth); + columnWidths.push_back(wideColumnWidth); + columnWidths.push_back(wideColumnWidth); + } }; diff --git a/Examples/PfRingExample-FilterTraffic/PacketMatchingEngine.h b/Examples/PfRingExample-FilterTraffic/PacketMatchingEngine.h index c19bc2949d..56914cc2e4 100644 --- a/Examples/PfRingExample-FilterTraffic/PacketMatchingEngine.h +++ b/Examples/PfRingExample-FilterTraffic/PacketMatchingEngine.h @@ -1,105 +1,97 @@ #pragma once -#include "SystemUtils.h" -#include "Packet.h" #include "IPv4Layer.h" +#include "Packet.h" +#include "SystemUtils.h" #include "TcpLayer.h" #include "UdpLayer.h" /** - * Responsible for matching packets by match criteria received from the user. Current match criteria are a combination of zero or more of the - * following parameters: source IP, dest IP, source TCP/UDP port, dest TCP/UDP port and TCP/UDP protocol. + * Responsible for matching packets by match criteria received from the user. + * Current match criteria are a combination of zero or more of the following + * parameters: source IP, dest IP, source TCP/UDP port, dest TCP/UDP port and + * TCP/UDP protocol. */ -class PacketMatchingEngine -{ -private: - pcpp::IPv4Address m_SrcIpToMatch, m_DstIpToMatch; - uint16_t m_SrcPortToMatch, m_DstPortToMatch; - pcpp::ProtocolType m_ProtocolToMatch; - bool m_MatchSrcIp, m_MatchDstIp; - bool m_MatchSrcPort, m_MatchDstPort; - bool m_MatchProtocol; -public: - PacketMatchingEngine(const pcpp::IPv4Address& srcIpToMatch, const pcpp::IPv4Address& dstIpToMatch, uint16_t srcPortToMatch, uint16_t dstPortToMatch, pcpp::ProtocolType protocolToMatch) - : m_SrcIpToMatch(srcIpToMatch), m_DstIpToMatch(dstIpToMatch), - m_SrcPortToMatch(srcPortToMatch), m_DstPortToMatch(dstPortToMatch), m_ProtocolToMatch(protocolToMatch), - m_MatchSrcIp(false), m_MatchDstIp(false), m_MatchSrcPort(false), m_MatchDstPort(false), m_MatchProtocol(false) - { - if (m_SrcIpToMatch != pcpp::IPv4Address::Zero) - m_MatchSrcIp = true; - if (m_DstIpToMatch != pcpp::IPv4Address::Zero) - m_MatchDstIp = true; - if (m_SrcPortToMatch != 0) - m_MatchSrcPort = true; - if (m_DstPortToMatch != 0) - m_MatchDstPort = true; - if (m_ProtocolToMatch == pcpp::TCP || m_ProtocolToMatch == pcpp::UDP) - m_MatchProtocol = true; - } +class PacketMatchingEngine { + private: + pcpp::IPv4Address m_SrcIpToMatch, m_DstIpToMatch; + uint16_t m_SrcPortToMatch, m_DstPortToMatch; + pcpp::ProtocolType m_ProtocolToMatch; + bool m_MatchSrcIp, m_MatchDstIp; + bool m_MatchSrcPort, m_MatchDstPort; + bool m_MatchProtocol; + + public: + PacketMatchingEngine(const pcpp::IPv4Address& srcIpToMatch, + const pcpp::IPv4Address& dstIpToMatch, + uint16_t srcPortToMatch, uint16_t dstPortToMatch, + pcpp::ProtocolType protocolToMatch) + : m_SrcIpToMatch(srcIpToMatch), m_DstIpToMatch(dstIpToMatch), + m_SrcPortToMatch(srcPortToMatch), m_DstPortToMatch(dstPortToMatch), + m_ProtocolToMatch(protocolToMatch), m_MatchSrcIp(false), + m_MatchDstIp(false), m_MatchSrcPort(false), m_MatchDstPort(false), + m_MatchProtocol(false) { + if (m_SrcIpToMatch != pcpp::IPv4Address::Zero) + m_MatchSrcIp = true; + if (m_DstIpToMatch != pcpp::IPv4Address::Zero) + m_MatchDstIp = true; + if (m_SrcPortToMatch != 0) + m_MatchSrcPort = true; + if (m_DstPortToMatch != 0) + m_MatchDstPort = true; + if (m_ProtocolToMatch == pcpp::TCP || m_ProtocolToMatch == pcpp::UDP) + m_MatchProtocol = true; + } - bool isMatched(pcpp::Packet& packet) - { - if (m_MatchSrcIp || m_MatchDstIp) - { - if (!packet.isPacketOfType(pcpp::IPv4)) - { - return false; - } + bool isMatched(pcpp::Packet& packet) { + if (m_MatchSrcIp || m_MatchDstIp) { + if (!packet.isPacketOfType(pcpp::IPv4)) { + return false; + } - pcpp::IPv4Layer* ip4Layer = packet.getLayerOfType(); - if (m_MatchSrcIp && (ip4Layer->getSrcIPv4Address() != m_SrcIpToMatch)) - { - return false; - } + pcpp::IPv4Layer* ip4Layer = packet.getLayerOfType(); + if (m_MatchSrcIp && (ip4Layer->getSrcIPv4Address() != m_SrcIpToMatch)) { + return false; + } - if (m_MatchDstIp && (ip4Layer->getDstIPv4Address() != m_DstIpToMatch)) - { - return false; - } - } + if (m_MatchDstIp && (ip4Layer->getDstIPv4Address() != m_DstIpToMatch)) { + return false; + } + } - if (m_MatchSrcPort || m_MatchDstPort) - { - uint16_t srcPort, dstPort; - if (packet.isPacketOfType(pcpp::TCP)) - { - srcPort = packet.getLayerOfType()->getSrcPort(); - dstPort = packet.getLayerOfType()->getDstPort(); - } - else if (packet.isPacketOfType(pcpp::UDP)) - { - srcPort = packet.getLayerOfType()->getSrcPort(); - dstPort = packet.getLayerOfType()->getDstPort(); - } - else - { - return false; - } + if (m_MatchSrcPort || m_MatchDstPort) { + uint16_t srcPort, dstPort; + if (packet.isPacketOfType(pcpp::TCP)) { + srcPort = packet.getLayerOfType()->getSrcPort(); + dstPort = packet.getLayerOfType()->getDstPort(); + } else if (packet.isPacketOfType(pcpp::UDP)) { + srcPort = packet.getLayerOfType()->getSrcPort(); + dstPort = packet.getLayerOfType()->getDstPort(); + } else { + return false; + } - if (m_MatchSrcPort && (srcPort != m_SrcPortToMatch)) - { - return false; - } + if (m_MatchSrcPort && (srcPort != m_SrcPortToMatch)) { + return false; + } - if (m_MatchDstPort && (dstPort != m_DstPortToMatch)) - { - return false; - } - } + if (m_MatchDstPort && (dstPort != m_DstPortToMatch)) { + return false; + } + } - if (m_MatchProtocol) - { - if (m_ProtocolToMatch == pcpp::TCP && (!packet.isPacketOfType(pcpp::TCP))) - { - return false; - } + if (m_MatchProtocol) { + if (m_ProtocolToMatch == pcpp::TCP && + (!packet.isPacketOfType(pcpp::TCP))) { + return false; + } - if (m_ProtocolToMatch == pcpp::UDP && (!packet.isPacketOfType(pcpp::UDP))) - { - return false; - } - } + if (m_ProtocolToMatch == pcpp::UDP && + (!packet.isPacketOfType(pcpp::UDP))) { + return false; + } + } - return true; - } + return true; + } }; diff --git a/Examples/PfRingExample-FilterTraffic/main.cpp b/Examples/PfRingExample-FilterTraffic/main.cpp index 7fe3ef9070..3a48b2dfcd 100644 --- a/Examples/PfRingExample-FilterTraffic/main.cpp +++ b/Examples/PfRingExample-FilterTraffic/main.cpp @@ -1,517 +1,546 @@ /** * Filter Traffic PF_RING example application * ========================================== - * An application that listens to one or more PF_RING interface, captures all traffic - * and matches packets by user-defined matching criteria. Matching criteria is given on startup and can contain one or more of the following: - * source IP, destination IP, source TCP/UDP port, destination TCP/UDP port and TCP or UDP protocol. Matching is done per flow, meaning the first packet - * received on a flow is matched against the matching criteria and if it's matched then all packets of the same flow will be matched too. - * Packets that are matched can be send to another PF_RING interface and/or be save to a pcap file. - * In addition the application collect statistics on received and matched packets: number of packets per protocol, number of matched flows and number + * An application that listens to one or more PF_RING interface, captures all + * traffic and matches packets by user-defined matching criteria. Matching + * criteria is given on startup and can contain one or more of the following: + * source IP, destination IP, source TCP/UDP port, destination TCP/UDP port and + * TCP or UDP protocol. Matching is done per flow, meaning the first packet + * received on a flow is matched against the matching criteria and if it's + * matched then all packets of the same flow will be matched too. Packets that + * are matched can be send to another PF_RING interface and/or be save to a pcap + * file. In addition the application collect statistics on received and matched + * packets: number of packets per protocol, number of matched flows and number * of matched packets. * - * The application uses PfRingDevice's multi-threaded capturing. Number of capture threads can be set by the user (to the maximum of machine's core number minus 1) - * or set to default (default is all machine cores minus one management core the application runs on). Each core is assigned with one capture thread. - * PfRingDevice tries to assign one RX channel for each capturing thread (to improve performance), but if NIC doesn't enough RX channels to - * provide one for each thread, it will assign several thread with the same RX channel - * For example: if NIC supports 4 RX channels but the user asks for 6 capturing threads than 4 cores will share 2 RX channels and the 2 remaining cores will - * use RX channels of their own. - * Each capturing thread does exactly the same work: receiving packets, collecting packet statistics, matching flows and sending/saving matched packets + * The application uses PfRingDevice's multi-threaded capturing. Number of + * capture threads can be set by the user (to the maximum of machine's core + * number minus 1) or set to default (default is all machine cores minus one + * management core the application runs on). Each core is assigned with one + * capture thread. PfRingDevice tries to assign one RX channel for each + * capturing thread (to improve performance), but if NIC doesn't enough RX + * channels to provide one for each thread, it will assign several thread with + * the same RX channel For example: if NIC supports 4 RX channels but the user + * asks for 6 capturing threads than 4 cores will share 2 RX channels and the 2 + * remaining cores will use RX channels of their own. Each capturing thread does + * exactly the same work: receiving packets, collecting packet statistics, + * matching flows and sending/saving matched packets * - * Another thing shown here is getting interface capabilities such as total RX channels available, MAC address, PF_RING interface - * index, MTU, etc. + * Another thing shown here is getting interface capabilities such as total RX + * channels available, MAC address, PF_RING interface index, MTU, etc. * * __Important__: - * 1. Before compiling this application make sure you set "Compile PcapPlusPlus with PF_RING" to "y" in configure-linux.sh. Otherwise - * the application won't compile - * 2. Before running the application make sure you load the PF_RING kernel module: sudo insmod /kernel/pf_ring.ko - * Otherwise the application will exit with an error log that instructs you to load the kernel module - * 3. This application (like all applications using PF_RING) should be run as 'sudo' + * 1. Before compiling this application make sure you set "Compile PcapPlusPlus + * with PF_RING" to "y" in configure-linux.sh. Otherwise the application won't + * compile + * 2. Before running the application make sure you load the PF_RING kernel + * module: sudo insmod /kernel/pf_ring.ko Otherwise the + * application will exit with an error log that instructs you to load the kernel + * module + * 3. This application (like all applications using PF_RING) should be run as + * 'sudo' */ #include "Common.h" #include "PacketMatchingEngine.h" -#include -#include +#include #include -#include +#include #include +#include +#include #include -#include -#include -#include #include #include #include +#include #include +#include - -static struct option PfFilterTrafficOptions[] = -{ - {"interface-name", required_argument, 0, 'n'}, - {"send-matched-packets", required_argument, 0, 's'}, - {"save-matched-packets", required_argument, 0, 'f'}, - {"match-source-ip", required_argument, 0, 'i'}, - {"match-dest-ip", required_argument, 0, 'I'}, - {"match-source-port", required_argument, 0, 'p'}, - {"match-dest-port", required_argument, 0, 'P'}, - {"match-protocol", required_argument, 0, 'r'}, - {"num-of-threads", required_argument, 0, 't'}, - {"help", no_argument, 0, 'h'}, - {"version", no_argument, 0, 'v'}, - {"list", no_argument, 0, 'l'}, - {0, 0, 0, 0} -}; - +static struct option PfFilterTrafficOptions[] = { + {"interface-name", required_argument, 0, 'n'}, + {"send-matched-packets", required_argument, 0, 's'}, + {"save-matched-packets", required_argument, 0, 'f'}, + {"match-source-ip", required_argument, 0, 'i'}, + {"match-dest-ip", required_argument, 0, 'I'}, + {"match-source-port", required_argument, 0, 'p'}, + {"match-dest-port", required_argument, 0, 'P'}, + {"match-protocol", required_argument, 0, 'r'}, + {"num-of-threads", required_argument, 0, 't'}, + {"help", no_argument, 0, 'h'}, + {"version", no_argument, 0, 'v'}, + {"list", no_argument, 0, 'l'}, + {0, 0, 0, 0}}; /** * A struct that holds all arguments passed to capture threads: packetArrived() */ -struct CaptureThreadArgs -{ - PacketStats* packetStatArr; - PacketMatchingEngine* matchingEngine; - std::map* flowTables; - pcpp::PfRingDevice* sendPacketsTo; - pcpp::PcapFileWriterDevice** pcapWriters; - - CaptureThreadArgs() : packetStatArr(NULL), matchingEngine(NULL), flowTables(NULL), sendPacketsTo(NULL), pcapWriters(NULL) {} +struct CaptureThreadArgs { + PacketStats* packetStatArr; + PacketMatchingEngine* matchingEngine; + std::map* flowTables; + pcpp::PfRingDevice* sendPacketsTo; + pcpp::PcapFileWriterDevice** pcapWriters; + + CaptureThreadArgs() + : packetStatArr(NULL), matchingEngine(NULL), flowTables(NULL), + sendPacketsTo(NULL), pcapWriters(NULL) {} }; - /** * Print application usage */ -void printUsage() -{ - std::cout << std::endl - << "Usage:" << std::endl - << "------" << std::endl - << pcpp::AppName::get() << " [-hvl] [-s INTERFACE_NAME] [-f FILENAME] [-i IPV4_ADDR] [-I IPV4_ADDR] [-p PORT] [-P PORT] [-r PROTOCOL]" << std::endl - << " [-c NUM_OF_THREADS] -n INTERFACE_NAME" << std::endl - << std::endl - << "Options:" << std::endl - << std::endl - << " -h|--help : Displays this help message and exits" << std::endl - << " -v|--version : Displays the current version and exits" << std::endl - << " -l|--list : Print the list of PF_RING devices and exists" << std::endl - << " -n|--interface-name INTERFACE_NAME : A PF_RING interface name to receive packets from." << std::endl - << " To see all available interfaces use the -l switch" << std::endl - << " -s|--send-matched-packets INTERFACE_NAME : PF_RING interface name to send matched packets to" << std::endl - << " -f|--save-matched-packets FILEPATH : Save matched packets to pcap files under FILEPATH." << std::endl - << " Packets matched by thread X will be saved under" << std::endl - << " 'FILEPATH/ThreadX.pcap'" << std::endl - << " -i|--match-source-ip IPV4_ADDR : Match source IPv4 address" << std::endl - << " -I|--match-dest-ip IPV4_ADDR : Match destination IPv4 address" << std::endl - << " -p|--match-source-port PORT : Match source TCP/UDP port" << std::endl - << " -P|--match-dest-port PORT : Match destination TCP/UDP port" << std::endl - << " -r|--match-protocol PROTOCOL : Match protocol. Valid values are 'TCP' or 'UDP'" << std::endl - << " -t|--num-of-threads NUM_OF_THREADS : Number of capture threads to open. Should be in" << std::endl - << " the range of 1 to NUM_OF_CORES_ON_MACHINE-1." << std::endl - << " Default is using all machine cores except the core" << std::endl - << " the application is running on" << std::endl - << std::endl; +void printUsage() { + std::cout << std::endl + << "Usage:" << std::endl + << "------" << std::endl + << pcpp::AppName::get() + << " [-hvl] [-s INTERFACE_NAME] [-f FILENAME] [-i IPV4_ADDR] [-I " + "IPV4_ADDR] [-p PORT] [-P PORT] [-r PROTOCOL]" + << std::endl + << " [-c NUM_OF_THREADS] -n INTERFACE_NAME" + << std::endl + << std::endl + << "Options:" << std::endl + << std::endl + << " -h|--help : Displays this " + "help message and exits" + << std::endl + << " -v|--version : Displays the " + "current version and exits" + << std::endl + << " -l|--list : Print the " + "list of PF_RING devices and exists" + << std::endl + << " -n|--interface-name INTERFACE_NAME : A PF_RING " + "interface name to receive packets from." + << std::endl + << " To see all " + "available interfaces use the -l switch" + << std::endl + << " -s|--send-matched-packets INTERFACE_NAME : PF_RING " + "interface name to send matched packets to" + << std::endl + << " -f|--save-matched-packets FILEPATH : Save matched " + "packets to pcap files under FILEPATH." + << std::endl + << " Packets " + "matched by thread X will be saved under" + << std::endl + << " " + "'FILEPATH/ThreadX.pcap'" + << std::endl + << " -i|--match-source-ip IPV4_ADDR : Match source " + "IPv4 address" + << std::endl + << " -I|--match-dest-ip IPV4_ADDR : Match " + "destination IPv4 address" + << std::endl + << " -p|--match-source-port PORT : Match source " + "TCP/UDP port" + << std::endl + << " -P|--match-dest-port PORT : Match " + "destination TCP/UDP port" + << std::endl + << " -r|--match-protocol PROTOCOL : Match " + "protocol. Valid values are 'TCP' or 'UDP'" + << std::endl + << " -t|--num-of-threads NUM_OF_THREADS : Number of " + "capture threads to open. Should be in" + << std::endl + << " the range of " + "1 to NUM_OF_CORES_ON_MACHINE-1." + << std::endl + << " Default is " + "using all machine cores except the core" + << std::endl + << " the " + "application is running on" + << std::endl + << std::endl; } - /** * Print application version */ -void printAppVersion() -{ - std::cout - << pcpp::AppName::get() << " " << pcpp::getPcapPlusPlusVersionFull() << std::endl - << "Built: " << pcpp::getBuildDateTime() << std::endl - << "Built from: " << pcpp::getGitInfo() << std::endl; - exit(0); +void printAppVersion() { + std::cout << pcpp::AppName::get() << " " << pcpp::getPcapPlusPlusVersionFull() + << std::endl + << "Built: " << pcpp::getBuildDateTime() << std::endl + << "Built from: " << pcpp::getGitInfo() << std::endl; + exit(0); } - /** * Print to console all available PF_RING devices. Used by the -l switch */ -void listPfRingDevices() -{ - // suppress errors as there may be devices (like lo) that their MAC address can't be read, etc. - pcpp::Logger::getInstance().suppressLogs(); - - const std::vector& devList = pcpp::PfRingDeviceList::getInstance().getPfRingDevicesList(); - for (std::vector::const_iterator iter = devList.begin(); iter != devList.end(); iter++) - { - std::ostringstream interfaceIndex; - if ((*iter)->getInterfaceIndex() <= 9999) - { - interfaceIndex << (*iter)->getInterfaceIndex(); - } - else - { - interfaceIndex << "N/A"; - } - - std::cout - << " -> Name: " << std::left << std::setw(8) << (*iter)->getDeviceName() - << " Index: " << std::setw(5) << interfaceIndex.str() - << " MAC address: " << std::setw(19) << ((*iter)->getMacAddress() == pcpp::MacAddress::Zero ? "N/A" : (*iter)->getMacAddress().toString()) - << " Available RX channels: " << std::setw(3) << (int)(*iter)->getTotalNumOfRxChannels() - << " MTU: " << (*iter)->getMtu() - << std::endl; - } - - // re-enable errors - pcpp::Logger::getInstance().enableLogs(); +void listPfRingDevices() { + // suppress errors as there may be devices (like lo) that their MAC address + // can't be read, etc. + pcpp::Logger::getInstance().suppressLogs(); + + const std::vector& devList = + pcpp::PfRingDeviceList::getInstance().getPfRingDevicesList(); + for (std::vector::const_iterator iter = devList.begin(); + iter != devList.end(); iter++) { + std::ostringstream interfaceIndex; + if ((*iter)->getInterfaceIndex() <= 9999) { + interfaceIndex << (*iter)->getInterfaceIndex(); + } else { + interfaceIndex << "N/A"; + } + + std::cout << " -> Name: " << std::left << std::setw(8) + << (*iter)->getDeviceName() << " Index: " << std::setw(5) + << interfaceIndex.str() << " MAC address: " << std::setw(19) + << ((*iter)->getMacAddress() == pcpp::MacAddress::Zero + ? "N/A" + : (*iter)->getMacAddress().toString()) + << " Available RX channels: " << std::setw(3) + << (int)(*iter)->getTotalNumOfRxChannels() + << " MTU: " << (*iter)->getMtu() << std::endl; + } + + // re-enable errors + pcpp::Logger::getInstance().enableLogs(); } - /** - * The method that is called each time a packet is received on any of the threads. It collects all relevant stats for the packet and - * matches it with the matching engine. If packet is matched it sends it to the TX interface (if needed) or writes it to - * the thread's pcap file (if needed) + * The method that is called each time a packet is received on any of the + * threads. It collects all relevant stats for the packet and matches it with + * the matching engine. If packet is matched it sends it to the TX interface (if + * needed) or writes it to the thread's pcap file (if needed) */ -void packetArrived(pcpp::RawPacket* packets, uint32_t numOfPackets, uint8_t threadId, pcpp::PfRingDevice* device, void* userCookie) -{ - CaptureThreadArgs* args = (CaptureThreadArgs*)userCookie; - for (uint32_t i = 0; i < numOfPackets; i++) - { - // parse packet - pcpp::Packet packet(&packets[i]); - - // collect stats for packet - args->packetStatArr[threadId].collectStats(packet); - - bool packetMatched = false; - - // hash the packet by 5-tuple and look in the flow table to see whether this packet belongs to an existing or new flow - uint32_t hash = pcpp::hash5Tuple(&packet); - std::map::const_iterator iter = args->flowTables[threadId].find(hash); - - // if packet belongs to an already existing flow - if (iter !=args->flowTables[threadId].end() && iter->second) - { - packetMatched = true; - } - else // packet belongs to a new flow - { - packetMatched = args->matchingEngine->isMatched(packet); - if (packetMatched) - { - // put new flow in flow table - args->flowTables[threadId][hash] = true; - - //collect stats - if (packet.isPacketOfType(pcpp::TCP)) - { - args->packetStatArr[threadId].MatchedTcpFlows++; - } - else if (packet.isPacketOfType(pcpp::UDP)) - { - args->packetStatArr[threadId].MatchedUdpFlows++; - } - - } - } - - if (packetMatched) - { - // send packet to TX port if needed - if (args->sendPacketsTo != NULL) - { - args->sendPacketsTo->sendPacket(packet); - } - - // save packet to file if needed - if (args->pcapWriters != NULL) - { - args->pcapWriters[threadId]->writePacket(packets[i]); - } - - args->packetStatArr[threadId].MatchedPackets++; - } - } +void packetArrived(pcpp::RawPacket* packets, uint32_t numOfPackets, + uint8_t threadId, pcpp::PfRingDevice* device, + void* userCookie) { + CaptureThreadArgs* args = (CaptureThreadArgs*)userCookie; + for (uint32_t i = 0; i < numOfPackets; i++) { + // parse packet + pcpp::Packet packet(&packets[i]); + + // collect stats for packet + args->packetStatArr[threadId].collectStats(packet); + + bool packetMatched = false; + + // hash the packet by 5-tuple and look in the flow table to see whether this + // packet belongs to an existing or new flow + uint32_t hash = pcpp::hash5Tuple(&packet); + std::map::const_iterator iter = + args->flowTables[threadId].find(hash); + + // if packet belongs to an already existing flow + if (iter != args->flowTables[threadId].end() && iter->second) { + packetMatched = true; + } else // packet belongs to a new flow + { + packetMatched = args->matchingEngine->isMatched(packet); + if (packetMatched) { + // put new flow in flow table + args->flowTables[threadId][hash] = true; + + // collect stats + if (packet.isPacketOfType(pcpp::TCP)) { + args->packetStatArr[threadId].MatchedTcpFlows++; + } else if (packet.isPacketOfType(pcpp::UDP)) { + args->packetStatArr[threadId].MatchedUdpFlows++; + } + } + } + + if (packetMatched) { + // send packet to TX port if needed + if (args->sendPacketsTo != NULL) { + args->sendPacketsTo->sendPacket(packet); + } + + // save packet to file if needed + if (args->pcapWriters != NULL) { + args->pcapWriters[threadId]->writePacket(packets[i]); + } + + args->packetStatArr[threadId].MatchedPackets++; + } + } } - /** - * The callback to be called when application is terminated by ctrl-c. Do cleanup and print summary stats + * The callback to be called when application is terminated by ctrl-c. Do + * cleanup and print summary stats */ -void onApplicationInterrupted(void* cookie) -{ - bool* shouldStop = (bool*)cookie; +void onApplicationInterrupted(void* cookie) { + bool* shouldStop = (bool*)cookie; - *shouldStop = true; + *shouldStop = true; } - -int main(int argc, char* argv[]) -{ - pcpp::AppName::init(argc, argv); - - pcpp::PfRingDevice* dev = NULL; - - int totalNumOfCores = pcpp::getNumOfCores(); - int numOfCaptureThreads = totalNumOfCores-1; - - pcpp::PfRingDevice* sendPacketsToIface = NULL; - - std::string packetFilePath = ""; - bool writePacketsToDisk = true; - - pcpp::IPv4Address srcIPToMatch = pcpp::IPv4Address::Zero; - pcpp::IPv4Address dstIPToMatch = pcpp::IPv4Address::Zero; - uint16_t srcPortToMatch = 0; - uint16_t dstPortToMatch = 0; - pcpp::ProtocolType protocolToMatch = pcpp::UnknownProtocol; - - int optionIndex = 0; - int opt = 0; - - while((opt = getopt_long(argc, argv, "n:s:t:f:i:I:p:P:r:hvl", PfFilterTrafficOptions, &optionIndex)) != -1) - { - switch (opt) - { - case 0: - { - break; - } - case 'n': - { - std::string ifaceName = std::string(optarg); - dev = pcpp::PfRingDeviceList::getInstance().getPfRingDeviceByName(ifaceName); - if (dev == NULL) - EXIT_WITH_ERROR("Could not find PF_RING device '" << ifaceName << "'"); - break; - } - case 's': - { - std::string sendPacketsToIfaceName = std::string(optarg); - sendPacketsToIface = pcpp::PfRingDeviceList::getInstance().getPfRingDeviceByName(sendPacketsToIfaceName); - if (sendPacketsToIface == NULL) - EXIT_WITH_ERROR("Could not find PF_RING device '" << sendPacketsToIfaceName << "'"); - - break; - } - case 't': - { - numOfCaptureThreads = atoi(optarg); - if (numOfCaptureThreads < 1 || numOfCaptureThreads > totalNumOfCores-1) - EXIT_WITH_ERROR("Number of capture threads must be in the range of 1 to " << totalNumOfCores-1); - break; - } - case 'f': - { - packetFilePath = std::string(optarg); - // make sure the path ends with '/' - if (packetFilePath.length() > 1 && (0 != packetFilePath.compare(packetFilePath.length()-1, 1, "/"))) - packetFilePath += "/"; - - writePacketsToDisk = true; - break; - } - case 'i': - { - srcIPToMatch = pcpp::IPv4Address(optarg); - if (!srcIPToMatch.isValid()) - { - EXIT_WITH_ERROR_AND_PRINT_USAGE("Source IP to match isn't a valid IP address"); - } - break; - } - case 'I': - { - dstIPToMatch = pcpp::IPv4Address(optarg); - if (!dstIPToMatch.isValid()) - { - EXIT_WITH_ERROR_AND_PRINT_USAGE("Destination IP to match isn't a valid IP address"); - } - break; - } - case 'p': - { - int ret = atoi(optarg); - if (ret <= 0 || ret > 65535) - { - EXIT_WITH_ERROR_AND_PRINT_USAGE("Source port to match isn't a valid TCP/UDP port"); - } - srcPortToMatch = ret; - break; - } - case 'P': - { - int ret = atoi(optarg); - if (ret <= 0 || ret > 65535) - { - EXIT_WITH_ERROR_AND_PRINT_USAGE("Destination port to match isn't a valid TCP/UDP port"); - } - dstPortToMatch = ret; - break; - } - case 'r': - { - std::string protocol = std::string(optarg); - if (protocol == "TCP") - protocolToMatch = pcpp::TCP; - else if (protocol == "UDP") - protocolToMatch = pcpp::UDP; - else - { - EXIT_WITH_ERROR_AND_PRINT_USAGE("Protocol to match isn't TCP or UDP"); - } - break; - } - case 'h': - { - printUsage(); - exit(0); - } - case 'v': - { - printAppVersion(); - break; - } - case 'l': - { - listPfRingDevices(); - exit(0); - } - default: - { - printUsage(); - exit(0); - } - } - } - - if (dev == NULL) - EXIT_WITH_ERROR_AND_PRINT_USAGE("Interface name was not provided"); - - // open the PF_RING device in multi-thread mode. Distribution of packets between threads will be done per-flow (as opposed to - // round-robin) - if (!dev->openMultiRxChannels(numOfCaptureThreads, pcpp::PfRingDevice::PerFlow)) - EXIT_WITH_ERROR("Couldn't open " << numOfCaptureThreads << " RX channels on interface '" << dev->getDeviceName() << "'"); - - if (sendPacketsToIface != NULL && !sendPacketsToIface->open()) - EXIT_WITH_ERROR("Couldn't open PF_RING device '" << sendPacketsToIface->getDeviceName() << "' for sending matched packets"); - - pcpp::CoreMask coreMask = 0; - int threadId = 0; - int threadCount = 0; - - // create an array of packet stats with the size of all machine cores - PacketStats packetStatsArr[totalNumOfCores]; - - // init each packet stats instance with an illegal core ID - for (int coreId = 0; coreId < totalNumOfCores; coreId++) - packetStatsArr[coreId].ThreadId = MAX_NUM_OF_CORES+1; - - // mark only relevant cores by adding them to core mask - // mark only relevant packet stats instances by setting their core ID - while (threadCount < numOfCaptureThreads) - { - if (pcpp::SystemCores::IdToSystemCore[threadId].Id != dev->getCurrentCoreId().Id) - { - coreMask |= pcpp::SystemCores::IdToSystemCore[threadId].Mask; - packetStatsArr[threadId].ThreadId = pcpp::SystemCores::IdToSystemCore[threadId].Id; - threadCount++; - } - - threadId++; - } - - // create the matching engine instance - PacketMatchingEngine matchingEngine(srcIPToMatch, dstIPToMatch, srcPortToMatch, dstPortToMatch, protocolToMatch); - - // create a flow table for each core - std::map flowTables[totalNumOfCores]; - - pcpp::PcapFileWriterDevice** pcapWriters = NULL; - - // if needed, prepare pcap writers for all capturing threads - if (writePacketsToDisk) - { - pcapWriters = new pcpp::PcapFileWriterDevice*[totalNumOfCores]; - - for (int coreId = 0; coreId < totalNumOfCores; coreId++) - { - // if core doesn't participate in capturing, skip it - if ((coreMask & pcpp::SystemCores::IdToSystemCore[coreId].Mask) == 0) - { - pcapWriters[coreId] = NULL; - continue; - } - - std::stringstream packetFileName; - packetFileName << packetFilePath << "Thread" << coreId << ".pcap"; - pcapWriters[coreId] = new pcpp::PcapFileWriterDevice(packetFileName.str()); - if (!pcapWriters[coreId]->open()) - { - EXIT_WITH_ERROR("Couldn't open pcap writer device for core " << coreId); - } - } - } - - - std::cout << "Start capturing on " << numOfCaptureThreads << " threads core mask = 0x" << std::hex << coreMask << std::dec << std::endl; - - // prepare packet capture configuration - CaptureThreadArgs args; - args.packetStatArr = packetStatsArr; - args.matchingEngine = &matchingEngine; - args.flowTables = flowTables; - args.sendPacketsTo = sendPacketsToIface; - args.pcapWriters = pcapWriters; - - // start capturing packets on all threads - if (!dev->startCaptureMultiThread(packetArrived, &args, coreMask)) - { - EXIT_WITH_ERROR("Couldn't start capturing on core mask 0x" << std::hex << coreMask << " on interface '" << dev->getDeviceName() << "'"); - } - - bool shouldStop = false; - - // register the on app close event to print summary stats on app termination - pcpp::ApplicationEventHandler::getInstance().onApplicationInterrupted(onApplicationInterrupted, &shouldStop); - - // infinite loop (until program is terminated) - while (!shouldStop) - { - sleep(5); - } - - // stop capturing packets, close the device - dev->stopCapture(); - dev->close(); - - // close and delete pcap writers - if (writePacketsToDisk) - { - for (int coreId = 0; coreId < totalNumOfCores; coreId++) - { - if ((coreMask & pcpp::SystemCores::IdToSystemCore[coreId].Mask) == 0) - continue; - - pcapWriters[coreId]->close(); - delete pcapWriters[coreId]; - } - } - - std::cout << std::endl << std::endl - << "Application stopped" - << std::endl; - - // print final stats for every capture thread plus sum of all threads and free worker threads memory - PacketStats aggregatedStats; - - // create table printer - std::vector columnNames; - std::vector columnWidths; - PacketStats::getStatsColumns(columnNames, columnWidths); - pcpp::TablePrinter printer(columnNames, columnWidths); - - for (int i = 0; i < totalNumOfCores; i++) - { - if (packetStatsArr[i].ThreadId == MAX_NUM_OF_CORES+1) - continue; - - aggregatedStats.collectStats(packetStatsArr[i]); - printer.printRow(packetStatsArr[i].getStatValuesAsString("|"), '|'); - } - printer.printRow(aggregatedStats.getStatValuesAsString("|"), '|'); +int main(int argc, char* argv[]) { + pcpp::AppName::init(argc, argv); + + pcpp::PfRingDevice* dev = NULL; + + int totalNumOfCores = pcpp::getNumOfCores(); + int numOfCaptureThreads = totalNumOfCores - 1; + + pcpp::PfRingDevice* sendPacketsToIface = NULL; + + std::string packetFilePath = ""; + bool writePacketsToDisk = true; + + pcpp::IPv4Address srcIPToMatch = pcpp::IPv4Address::Zero; + pcpp::IPv4Address dstIPToMatch = pcpp::IPv4Address::Zero; + uint16_t srcPortToMatch = 0; + uint16_t dstPortToMatch = 0; + pcpp::ProtocolType protocolToMatch = pcpp::UnknownProtocol; + + int optionIndex = 0; + int opt = 0; + + while ((opt = getopt_long(argc, argv, "n:s:t:f:i:I:p:P:r:hvl", + PfFilterTrafficOptions, &optionIndex)) != -1) { + switch (opt) { + case 0: { + break; + } + case 'n': { + std::string ifaceName = std::string(optarg); + dev = pcpp::PfRingDeviceList::getInstance().getPfRingDeviceByName( + ifaceName); + if (dev == NULL) + EXIT_WITH_ERROR("Could not find PF_RING device '" << ifaceName << "'"); + break; + } + case 's': { + std::string sendPacketsToIfaceName = std::string(optarg); + sendPacketsToIface = + pcpp::PfRingDeviceList::getInstance().getPfRingDeviceByName( + sendPacketsToIfaceName); + if (sendPacketsToIface == NULL) + EXIT_WITH_ERROR("Could not find PF_RING device '" + << sendPacketsToIfaceName << "'"); + + break; + } + case 't': { + numOfCaptureThreads = atoi(optarg); + if (numOfCaptureThreads < 1 || numOfCaptureThreads > totalNumOfCores - 1) + EXIT_WITH_ERROR( + "Number of capture threads must be in the range of 1 to " + << totalNumOfCores - 1); + break; + } + case 'f': { + packetFilePath = std::string(optarg); + // make sure the path ends with '/' + if (packetFilePath.length() > 1 && + (0 != packetFilePath.compare(packetFilePath.length() - 1, 1, "/"))) + packetFilePath += "/"; + + writePacketsToDisk = true; + break; + } + case 'i': { + srcIPToMatch = pcpp::IPv4Address(optarg); + if (!srcIPToMatch.isValid()) { + EXIT_WITH_ERROR_AND_PRINT_USAGE( + "Source IP to match isn't a valid IP address"); + } + break; + } + case 'I': { + dstIPToMatch = pcpp::IPv4Address(optarg); + if (!dstIPToMatch.isValid()) { + EXIT_WITH_ERROR_AND_PRINT_USAGE( + "Destination IP to match isn't a valid IP address"); + } + break; + } + case 'p': { + int ret = atoi(optarg); + if (ret <= 0 || ret > 65535) { + EXIT_WITH_ERROR_AND_PRINT_USAGE( + "Source port to match isn't a valid TCP/UDP port"); + } + srcPortToMatch = ret; + break; + } + case 'P': { + int ret = atoi(optarg); + if (ret <= 0 || ret > 65535) { + EXIT_WITH_ERROR_AND_PRINT_USAGE( + "Destination port to match isn't a valid TCP/UDP port"); + } + dstPortToMatch = ret; + break; + } + case 'r': { + std::string protocol = std::string(optarg); + if (protocol == "TCP") + protocolToMatch = pcpp::TCP; + else if (protocol == "UDP") + protocolToMatch = pcpp::UDP; + else { + EXIT_WITH_ERROR_AND_PRINT_USAGE("Protocol to match isn't TCP or UDP"); + } + break; + } + case 'h': { + printUsage(); + exit(0); + } + case 'v': { + printAppVersion(); + break; + } + case 'l': { + listPfRingDevices(); + exit(0); + } + default: { + printUsage(); + exit(0); + } + } + } + + if (dev == NULL) + EXIT_WITH_ERROR_AND_PRINT_USAGE("Interface name was not provided"); + + // open the PF_RING device in multi-thread mode. Distribution of packets + // between threads will be done per-flow (as opposed to round-robin) + if (!dev->openMultiRxChannels(numOfCaptureThreads, + pcpp::PfRingDevice::PerFlow)) + EXIT_WITH_ERROR("Couldn't open " << numOfCaptureThreads + << " RX channels on interface '" + << dev->getDeviceName() << "'"); + + if (sendPacketsToIface != NULL && !sendPacketsToIface->open()) + EXIT_WITH_ERROR("Couldn't open PF_RING device '" + << sendPacketsToIface->getDeviceName() + << "' for sending matched packets"); + + pcpp::CoreMask coreMask = 0; + int threadId = 0; + int threadCount = 0; + + // create an array of packet stats with the size of all machine cores + PacketStats packetStatsArr[totalNumOfCores]; + + // init each packet stats instance with an illegal core ID + for (int coreId = 0; coreId < totalNumOfCores; coreId++) + packetStatsArr[coreId].ThreadId = MAX_NUM_OF_CORES + 1; + + // mark only relevant cores by adding them to core mask + // mark only relevant packet stats instances by setting their core ID + while (threadCount < numOfCaptureThreads) { + if (pcpp::SystemCores::IdToSystemCore[threadId].Id != + dev->getCurrentCoreId().Id) { + coreMask |= pcpp::SystemCores::IdToSystemCore[threadId].Mask; + packetStatsArr[threadId].ThreadId = + pcpp::SystemCores::IdToSystemCore[threadId].Id; + threadCount++; + } + + threadId++; + } + + // create the matching engine instance + PacketMatchingEngine matchingEngine(srcIPToMatch, dstIPToMatch, + srcPortToMatch, dstPortToMatch, + protocolToMatch); + + // create a flow table for each core + std::map flowTables[totalNumOfCores]; + + pcpp::PcapFileWriterDevice** pcapWriters = NULL; + + // if needed, prepare pcap writers for all capturing threads + if (writePacketsToDisk) { + pcapWriters = new pcpp::PcapFileWriterDevice*[totalNumOfCores]; + + for (int coreId = 0; coreId < totalNumOfCores; coreId++) { + // if core doesn't participate in capturing, skip it + if ((coreMask & pcpp::SystemCores::IdToSystemCore[coreId].Mask) == 0) { + pcapWriters[coreId] = NULL; + continue; + } + + std::stringstream packetFileName; + packetFileName << packetFilePath << "Thread" << coreId << ".pcap"; + pcapWriters[coreId] = + new pcpp::PcapFileWriterDevice(packetFileName.str()); + if (!pcapWriters[coreId]->open()) { + EXIT_WITH_ERROR("Couldn't open pcap writer device for core " << coreId); + } + } + } + + std::cout << "Start capturing on " << numOfCaptureThreads + << " threads core mask = 0x" << std::hex << coreMask << std::dec + << std::endl; + + // prepare packet capture configuration + CaptureThreadArgs args; + args.packetStatArr = packetStatsArr; + args.matchingEngine = &matchingEngine; + args.flowTables = flowTables; + args.sendPacketsTo = sendPacketsToIface; + args.pcapWriters = pcapWriters; + + // start capturing packets on all threads + if (!dev->startCaptureMultiThread(packetArrived, &args, coreMask)) { + EXIT_WITH_ERROR("Couldn't start capturing on core mask 0x" + << std::hex << coreMask << " on interface '" + << dev->getDeviceName() << "'"); + } + + bool shouldStop = false; + + // register the on app close event to print summary stats on app termination + pcpp::ApplicationEventHandler::getInstance().onApplicationInterrupted( + onApplicationInterrupted, &shouldStop); + + // infinite loop (until program is terminated) + while (!shouldStop) { + sleep(5); + } + + // stop capturing packets, close the device + dev->stopCapture(); + dev->close(); + + // close and delete pcap writers + if (writePacketsToDisk) { + for (int coreId = 0; coreId < totalNumOfCores; coreId++) { + if ((coreMask & pcpp::SystemCores::IdToSystemCore[coreId].Mask) == 0) + continue; + + pcapWriters[coreId]->close(); + delete pcapWriters[coreId]; + } + } + + std::cout << std::endl + << std::endl + << "Application stopped" << std::endl; + + // print final stats for every capture thread plus sum of all threads and free + // worker threads memory + PacketStats aggregatedStats; + + // create table printer + std::vector columnNames; + std::vector columnWidths; + PacketStats::getStatsColumns(columnNames, columnWidths); + pcpp::TablePrinter printer(columnNames, columnWidths); + + for (int i = 0; i < totalNumOfCores; i++) { + if (packetStatsArr[i].ThreadId == MAX_NUM_OF_CORES + 1) + continue; + + aggregatedStats.collectStats(packetStatsArr[i]); + printer.printRow(packetStatsArr[i].getStatValuesAsString("|"), '|'); + } + printer.printRow(aggregatedStats.getStatValuesAsString("|"), '|'); } diff --git a/Examples/SSLAnalyzer/SSLStatsCollector.h b/Examples/SSLAnalyzer/SSLStatsCollector.h index 20fb942d33..2301e43b7f 100644 --- a/Examples/SSLAnalyzer/SSLStatsCollector.h +++ b/Examples/SSLAnalyzer/SSLStatsCollector.h @@ -1,385 +1,387 @@ #pragma once -#include -#include -#include "TcpLayer.h" #include "IPv4Layer.h" -#include "PayloadLayer.h" #include "PacketUtils.h" +#include "PayloadLayer.h" #include "SSLLayer.h" #include "SystemUtils.h" - +#include "TcpLayer.h" +#include +#include /** * An auxiliary struct for encapsulating rate stats */ -struct Rate -{ - double currentRate; // periodic rate - double totalRate; // overall rate +struct Rate { + double currentRate; // periodic rate + double totalRate; // overall rate }; /** * A struct for collecting general SSL/TLS stats */ -struct SSLGeneralStats -{ - int numOfSSLFlows; // total number of SSL flows - Rate sslFlowRate; // rate of SSL flows - int numOfSSLPackets; // total number of SSL packets - Rate sslPacketRate; // rate of SSL packets - double averageNumOfPacketsPerFlow; // average number of SSL packets per flow - int amountOfSSLTraffic; // total SSL traffic in bytes - double averageAmountOfDataPerFlow; // average number of SSL traffic per flow - Rate sslTrafficRate; // rate of SSL traffic - double sampleTime; // total stats collection time - int numOfHandshakeCompleteFlows; // number of flows which handshake was complete - int numOfFlowsWithAlerts; // number of flows that were terminated because of SSL/TLS alert - std::map sslVersionCount; // number of flows per SSL/TLS version - std::map sslPortCount; // number of flows per TCP port - - void clear() - { - numOfSSLFlows = 0; - sslFlowRate.currentRate = 0; - sslFlowRate.totalRate = 0; - numOfSSLPackets = 0; - sslPacketRate.currentRate = 0; - sslPacketRate.totalRate = 0; - averageNumOfPacketsPerFlow = 0; - amountOfSSLTraffic = 0; - averageAmountOfDataPerFlow = 0; - sslTrafficRate.currentRate = 0; - sslTrafficRate.totalRate = 0; - sampleTime = 0; - numOfHandshakeCompleteFlows = 0; - numOfFlowsWithAlerts = 0; - sslVersionCount.clear(); - sslPortCount.clear(); - } +struct SSLGeneralStats { + int numOfSSLFlows; // total number of SSL flows + Rate sslFlowRate; // rate of SSL flows + int numOfSSLPackets; // total number of SSL packets + Rate sslPacketRate; // rate of SSL packets + double averageNumOfPacketsPerFlow; // average number of SSL packets per flow + int amountOfSSLTraffic; // total SSL traffic in bytes + double averageAmountOfDataPerFlow; // average number of SSL traffic per flow + Rate sslTrafficRate; // rate of SSL traffic + double sampleTime; // total stats collection time + int numOfHandshakeCompleteFlows; // number of flows which handshake was + // complete + int numOfFlowsWithAlerts; // number of flows that were terminated because of + // SSL/TLS alert + std::map + sslVersionCount; // number of flows per SSL/TLS version + std::map sslPortCount; // number of flows per TCP port + + void clear() { + numOfSSLFlows = 0; + sslFlowRate.currentRate = 0; + sslFlowRate.totalRate = 0; + numOfSSLPackets = 0; + sslPacketRate.currentRate = 0; + sslPacketRate.totalRate = 0; + averageNumOfPacketsPerFlow = 0; + amountOfSSLTraffic = 0; + averageAmountOfDataPerFlow = 0; + sslTrafficRate.currentRate = 0; + sslTrafficRate.totalRate = 0; + sampleTime = 0; + numOfHandshakeCompleteFlows = 0; + numOfFlowsWithAlerts = 0; + sslVersionCount.clear(); + sslPortCount.clear(); + } }; - /** * A base struct for collecting stats on client-hello messages */ -struct ClientHelloStats -{ - int numOfMessages; // total number of client-hello messages - Rate messageRate; // rate of client-hello messages - std::map serverNameCount; // a map for counting the server names seen in traffic - - virtual ~ClientHelloStats() {} - - virtual void clear() - { - numOfMessages = 0; - messageRate.currentRate = 0; - messageRate.totalRate = 0; - serverNameCount.clear(); - } +struct ClientHelloStats { + int numOfMessages; // total number of client-hello messages + Rate messageRate; // rate of client-hello messages + std::map + serverNameCount; // a map for counting the server names seen in traffic + + virtual ~ClientHelloStats() {} + + virtual void clear() { + numOfMessages = 0; + messageRate.currentRate = 0; + messageRate.totalRate = 0; + serverNameCount.clear(); + } }; /** * A base struct for collecting stats on server-hello messages */ -struct ServerHelloStats -{ - int numOfMessages; // total number of server-hello messages - Rate messageRate; // rate of server-hello messages - std::map cipherSuiteCount; // count of the different chosen cipher-suites - - virtual ~ServerHelloStats() {} - - virtual void clear() - { - numOfMessages = 0; - messageRate.currentRate = 0; - messageRate.totalRate = 0; - cipherSuiteCount.clear(); - } +struct ServerHelloStats { + int numOfMessages; // total number of server-hello messages + Rate messageRate; // rate of server-hello messages + std::map + cipherSuiteCount; // count of the different chosen cipher-suites + + virtual ~ServerHelloStats() {} + + virtual void clear() { + numOfMessages = 0; + messageRate.currentRate = 0; + messageRate.totalRate = 0; + cipherSuiteCount.clear(); + } }; - /** - * The SSL stats collector. Should be called for every packet arriving and also periodically to calculate rates + * The SSL stats collector. Should be called for every packet arriving and also + * periodically to calculate rates */ -class SSLStatsCollector -{ -public: - - /** - * C'tor - clear all structures - */ - SSLStatsCollector() - { - clear(); - } - - /** - * Collect stats for a single packet - */ - void collectStats(pcpp::Packet* sslPacket) - { - // verify packet is TCP and SSL/TLS - if (!sslPacket->isPacketOfType(pcpp::TCP) || !sslPacket->isPacketOfType(pcpp::SSL)) - return; - - // collect general SSL traffic stats on this packet - uint32_t hashVal = collectSSLTrafficStats(sslPacket); - - // if packet contains one or more SSL messages, collect stats on them - if (sslPacket->isPacketOfType(pcpp::SSL)) - { - collectSSLStats(sslPacket, hashVal); - } - - // calculate current sample time which is the time-span from start time until current time - m_GeneralStats.sampleTime = getCurTime() - m_StartTime; - } - - /** - * Calculate rates. Should be called periodically - */ - void calcRates() - { - // getting current machine time - double curTime = getCurTime(); - - // getting time from last rate calculation until now - double diffSec = curTime - m_LastCalcRateTime; - - // calculating current rates which are the changes from last rate calculation until now divided by the time passed from - // last rate calculation until now - if (diffSec != 0) - { - m_GeneralStats.sslTrafficRate.currentRate = (m_GeneralStats.amountOfSSLTraffic - m_PrevGeneralStats.amountOfSSLTraffic) / diffSec; - m_GeneralStats.sslPacketRate.currentRate = (m_GeneralStats.numOfSSLPackets - m_PrevGeneralStats.numOfSSLPackets) / diffSec; - m_GeneralStats.sslFlowRate.currentRate = (m_GeneralStats.numOfSSLFlows - m_PrevGeneralStats.numOfSSLFlows) / diffSec; - m_ClientHelloStats.messageRate.currentRate = (m_ClientHelloStats.numOfMessages - m_PrevClientHelloStats.numOfMessages) / diffSec; - m_ServerHelloStats.messageRate.currentRate = (m_ServerHelloStats.numOfMessages - m_PrevServerHelloStats.numOfMessages) / diffSec; - } - - // getting the time from the beginning of stats collection until now - double diffSecTotal = curTime - m_StartTime; - - // calculating total rate which is the change from beginning of stats collection until now divided by time passed from - // beginning of stats collection until now - if (diffSecTotal != 0) - { - m_GeneralStats.sslTrafficRate.totalRate = m_GeneralStats.amountOfSSLTraffic / diffSecTotal; - m_GeneralStats.sslPacketRate.totalRate = m_GeneralStats.numOfSSLPackets / diffSecTotal; - m_GeneralStats.sslFlowRate.totalRate = m_GeneralStats.numOfSSLFlows / diffSecTotal; - m_ClientHelloStats.messageRate.totalRate = m_ClientHelloStats.numOfMessages / diffSecTotal; - m_ServerHelloStats.messageRate.totalRate = m_ServerHelloStats.numOfMessages / diffSecTotal; - } - - // saving current numbers for using them in the next rate calculation - m_PrevGeneralStats = m_GeneralStats; - m_PrevClientHelloStats = m_ClientHelloStats; - m_PrevServerHelloStats = m_ServerHelloStats; - - // saving the current time for using in the next rate calculation - m_LastCalcRateTime = curTime; - } - - /** - * Clear all stats collected so far - */ - void clear() - { - m_GeneralStats.clear(); - m_PrevGeneralStats.clear(); - m_ClientHelloStats.clear(); - m_PrevClientHelloStats.clear(); - m_ServerHelloStats.clear(); - m_PrevServerHelloStats.clear(); - m_LastCalcRateTime = getCurTime(); - m_StartTime = m_LastCalcRateTime; - } - - /** - * Get SSL general stats - */ - SSLGeneralStats& getGeneralStats() { return m_GeneralStats; } - - /** - * Get client-hello stats - */ - ClientHelloStats& getClientHelloStats() { return m_ClientHelloStats; } - - /** - * Get server-hello stats - */ - ServerHelloStats& getServerHelloStats() { return m_ServerHelloStats; } - -private: - - /** - * Auxiliary data collected for each flow for help calculating stats on this flow - */ - struct SSLFlowData - { - bool seenAppDataPacket; // was SSL application data seen in this flow - bool seenAlertPacket; // was SSL alert packet seen in this flow - - void clear() - { - seenAppDataPacket = false; - seenAlertPacket = false; - } - }; - - - /** - * Collect stats relevant for every SSL packet (any SSL message) - * This method calculates and returns the flow key for this packet - */ - uint32_t collectSSLTrafficStats(pcpp::Packet* sslpPacket) - { - pcpp::TcpLayer* tcpLayer = sslpPacket->getLayerOfType(); - - // count traffic - m_GeneralStats.amountOfSSLTraffic += tcpLayer->getLayerPayloadSize(); - - // count packet num - m_GeneralStats.numOfSSLPackets++; - - // calculate a hash key for this flow to be used in the flow table - uint32_t hashVal = hash5Tuple(sslpPacket); - - // if flow is a new flow (meaning it's not already in the flow table) - if (m_FlowTable.find(hashVal) == m_FlowTable.end()) - { - // count this new flow - m_GeneralStats.numOfSSLFlows++; - - // find the SSL/TLS port and add it to the port count - uint16_t srcPort = tcpLayer->getSrcPort(); - uint16_t dstPort = tcpLayer->getDstPort(); - if (pcpp::SSLLayer::isSSLPort(srcPort)) - m_GeneralStats.sslPortCount[srcPort]++; - else - m_GeneralStats.sslPortCount[dstPort]++; - - m_FlowTable[hashVal].clear(); - } - - // calculate averages - if (m_FlowTable.size() != 0) - { - m_GeneralStats.averageAmountOfDataPerFlow = (double)m_GeneralStats.amountOfSSLTraffic / (double)m_FlowTable.size(); - m_GeneralStats.averageNumOfPacketsPerFlow = (double)m_GeneralStats.numOfSSLPackets / (double)m_FlowTable.size(); - } - - return hashVal; - } - - /** - * Collect stats relevant for several kinds SSL messages - */ - void collectSSLStats(pcpp::Packet* sslPacket, uint32_t flowKey) - { - // go over all SSL messages in this packet - pcpp::SSLLayer* sslLayer = sslPacket->getLayerOfType(); - while (sslLayer != NULL) - { - // check if the layer is an alert message - pcpp::SSLRecordType recType = sslLayer->getRecordType(); - if (recType == pcpp::SSL_ALERT) - { - // if it's the first alert seen in this flow - if (m_FlowTable[flowKey].seenAlertPacket == false) - { - m_GeneralStats.numOfFlowsWithAlerts++; - m_FlowTable[flowKey].seenAlertPacket = true; - } - } - - // check if the layer is an app data message - else if (recType == pcpp::SSL_APPLICATION_DATA) - { - // if it's the first app data message seen on this flow it means handshake was completed - if (m_FlowTable[flowKey].seenAppDataPacket == false) - { - m_GeneralStats.numOfHandshakeCompleteFlows++; - m_FlowTable[flowKey].seenAppDataPacket = true; - } - } - - // check if the layer is an handshake message - else if (recType == pcpp::SSL_HANDSHAKE) - { - pcpp::SSLHandshakeLayer* handshakeLayer = dynamic_cast(sslLayer); - if (handshakeLayer == NULL) - continue; - - // try to find client-hello message - pcpp::SSLClientHelloMessage* clientHelloMessage = handshakeLayer->getHandshakeMessageOfType(); - - // collect client-hello stats - if (clientHelloMessage != NULL) - { - collecClientHelloStats(clientHelloMessage); - } - - // try to find server-hello message - pcpp::SSLServerHelloMessage* serverHelloMessage = handshakeLayer->getHandshakeMessageOfType(); - - // collect server-hello stats - if (serverHelloMessage != NULL) - { - m_GeneralStats.sslVersionCount[serverHelloMessage->getHandshakeVersion().asUInt()]++; - collecServerHelloStats(serverHelloMessage); - } - } - - sslLayer = sslPacket->getNextLayerOfType(sslLayer); - } - } - - /** - * Collect stats relevant only to client-hello messages - */ - void collecClientHelloStats(pcpp::SSLClientHelloMessage* clientHelloMessage) - { - m_ClientHelloStats.numOfMessages++; - - pcpp::SSLServerNameIndicationExtension* sniExt = clientHelloMessage->getExtensionOfType(); - if (sniExt != NULL) - m_ClientHelloStats.serverNameCount[sniExt->getHostName()]++; - } - - /** - * Collect stats relevant only to server-hello messages - */ - void collecServerHelloStats(pcpp::SSLServerHelloMessage* serverHelloMessage) - { - m_ServerHelloStats.numOfMessages++; - - pcpp::SSLCipherSuite* cipherSuite = serverHelloMessage->getCipherSuite(); - if (cipherSuite != NULL) - m_ServerHelloStats.cipherSuiteCount[cipherSuite->asString()]++; - } - - double getCurTime(void) - { - struct timeval tv; - - gettimeofday(&tv, NULL); - - return (((double) tv.tv_sec) + (double) (tv.tv_usec / 1000000.0)); - } - - SSLGeneralStats m_GeneralStats; - SSLGeneralStats m_PrevGeneralStats; - ClientHelloStats m_ClientHelloStats; - ClientHelloStats m_PrevClientHelloStats; - ServerHelloStats m_ServerHelloStats; - ServerHelloStats m_PrevServerHelloStats; - - std::map m_FlowTable; - - double m_LastCalcRateTime; - double m_StartTime; +class SSLStatsCollector { + public: + /** + * C'tor - clear all structures + */ + SSLStatsCollector() { clear(); } + + /** + * Collect stats for a single packet + */ + void collectStats(pcpp::Packet* sslPacket) { + // verify packet is TCP and SSL/TLS + if (!sslPacket->isPacketOfType(pcpp::TCP) || + !sslPacket->isPacketOfType(pcpp::SSL)) + return; + + // collect general SSL traffic stats on this packet + uint32_t hashVal = collectSSLTrafficStats(sslPacket); + + // if packet contains one or more SSL messages, collect stats on them + if (sslPacket->isPacketOfType(pcpp::SSL)) { + collectSSLStats(sslPacket, hashVal); + } + + // calculate current sample time which is the time-span from start time + // until current time + m_GeneralStats.sampleTime = getCurTime() - m_StartTime; + } + + /** + * Calculate rates. Should be called periodically + */ + void calcRates() { + // getting current machine time + double curTime = getCurTime(); + + // getting time from last rate calculation until now + double diffSec = curTime - m_LastCalcRateTime; + + // calculating current rates which are the changes from last rate + // calculation until now divided by the time passed from last rate + // calculation until now + if (diffSec != 0) { + m_GeneralStats.sslTrafficRate.currentRate = + (m_GeneralStats.amountOfSSLTraffic - + m_PrevGeneralStats.amountOfSSLTraffic) / + diffSec; + m_GeneralStats.sslPacketRate.currentRate = + (m_GeneralStats.numOfSSLPackets - + m_PrevGeneralStats.numOfSSLPackets) / + diffSec; + m_GeneralStats.sslFlowRate.currentRate = + (m_GeneralStats.numOfSSLFlows - m_PrevGeneralStats.numOfSSLFlows) / + diffSec; + m_ClientHelloStats.messageRate.currentRate = + (m_ClientHelloStats.numOfMessages - + m_PrevClientHelloStats.numOfMessages) / + diffSec; + m_ServerHelloStats.messageRate.currentRate = + (m_ServerHelloStats.numOfMessages - + m_PrevServerHelloStats.numOfMessages) / + diffSec; + } + + // getting the time from the beginning of stats collection until now + double diffSecTotal = curTime - m_StartTime; + + // calculating total rate which is the change from beginning of stats + // collection until now divided by time passed from beginning of stats + // collection until now + if (diffSecTotal != 0) { + m_GeneralStats.sslTrafficRate.totalRate = + m_GeneralStats.amountOfSSLTraffic / diffSecTotal; + m_GeneralStats.sslPacketRate.totalRate = + m_GeneralStats.numOfSSLPackets / diffSecTotal; + m_GeneralStats.sslFlowRate.totalRate = + m_GeneralStats.numOfSSLFlows / diffSecTotal; + m_ClientHelloStats.messageRate.totalRate = + m_ClientHelloStats.numOfMessages / diffSecTotal; + m_ServerHelloStats.messageRate.totalRate = + m_ServerHelloStats.numOfMessages / diffSecTotal; + } + + // saving current numbers for using them in the next rate calculation + m_PrevGeneralStats = m_GeneralStats; + m_PrevClientHelloStats = m_ClientHelloStats; + m_PrevServerHelloStats = m_ServerHelloStats; + + // saving the current time for using in the next rate calculation + m_LastCalcRateTime = curTime; + } + + /** + * Clear all stats collected so far + */ + void clear() { + m_GeneralStats.clear(); + m_PrevGeneralStats.clear(); + m_ClientHelloStats.clear(); + m_PrevClientHelloStats.clear(); + m_ServerHelloStats.clear(); + m_PrevServerHelloStats.clear(); + m_LastCalcRateTime = getCurTime(); + m_StartTime = m_LastCalcRateTime; + } + + /** + * Get SSL general stats + */ + SSLGeneralStats& getGeneralStats() { return m_GeneralStats; } + + /** + * Get client-hello stats + */ + ClientHelloStats& getClientHelloStats() { return m_ClientHelloStats; } + + /** + * Get server-hello stats + */ + ServerHelloStats& getServerHelloStats() { return m_ServerHelloStats; } + + private: + /** + * Auxiliary data collected for each flow for help calculating stats on this + * flow + */ + struct SSLFlowData { + bool seenAppDataPacket; // was SSL application data seen in this flow + bool seenAlertPacket; // was SSL alert packet seen in this flow + + void clear() { + seenAppDataPacket = false; + seenAlertPacket = false; + } + }; + + /** + * Collect stats relevant for every SSL packet (any SSL message) + * This method calculates and returns the flow key for this packet + */ + uint32_t collectSSLTrafficStats(pcpp::Packet* sslpPacket) { + pcpp::TcpLayer* tcpLayer = sslpPacket->getLayerOfType(); + + // count traffic + m_GeneralStats.amountOfSSLTraffic += tcpLayer->getLayerPayloadSize(); + + // count packet num + m_GeneralStats.numOfSSLPackets++; + + // calculate a hash key for this flow to be used in the flow table + uint32_t hashVal = hash5Tuple(sslpPacket); + + // if flow is a new flow (meaning it's not already in the flow table) + if (m_FlowTable.find(hashVal) == m_FlowTable.end()) { + // count this new flow + m_GeneralStats.numOfSSLFlows++; + + // find the SSL/TLS port and add it to the port count + uint16_t srcPort = tcpLayer->getSrcPort(); + uint16_t dstPort = tcpLayer->getDstPort(); + if (pcpp::SSLLayer::isSSLPort(srcPort)) + m_GeneralStats.sslPortCount[srcPort]++; + else + m_GeneralStats.sslPortCount[dstPort]++; + + m_FlowTable[hashVal].clear(); + } + + // calculate averages + if (m_FlowTable.size() != 0) { + m_GeneralStats.averageAmountOfDataPerFlow = + (double)m_GeneralStats.amountOfSSLTraffic / + (double)m_FlowTable.size(); + m_GeneralStats.averageNumOfPacketsPerFlow = + (double)m_GeneralStats.numOfSSLPackets / (double)m_FlowTable.size(); + } + + return hashVal; + } + + /** + * Collect stats relevant for several kinds SSL messages + */ + void collectSSLStats(pcpp::Packet* sslPacket, uint32_t flowKey) { + // go over all SSL messages in this packet + pcpp::SSLLayer* sslLayer = sslPacket->getLayerOfType(); + while (sslLayer != NULL) { + // check if the layer is an alert message + pcpp::SSLRecordType recType = sslLayer->getRecordType(); + if (recType == pcpp::SSL_ALERT) { + // if it's the first alert seen in this flow + if (m_FlowTable[flowKey].seenAlertPacket == false) { + m_GeneralStats.numOfFlowsWithAlerts++; + m_FlowTable[flowKey].seenAlertPacket = true; + } + } + + // check if the layer is an app data message + else if (recType == pcpp::SSL_APPLICATION_DATA) { + // if it's the first app data message seen on this flow it means + // handshake was completed + if (m_FlowTable[flowKey].seenAppDataPacket == false) { + m_GeneralStats.numOfHandshakeCompleteFlows++; + m_FlowTable[flowKey].seenAppDataPacket = true; + } + } + + // check if the layer is an handshake message + else if (recType == pcpp::SSL_HANDSHAKE) { + pcpp::SSLHandshakeLayer* handshakeLayer = + dynamic_cast(sslLayer); + if (handshakeLayer == NULL) + continue; + + // try to find client-hello message + pcpp::SSLClientHelloMessage* clientHelloMessage = + handshakeLayer + ->getHandshakeMessageOfType(); + + // collect client-hello stats + if (clientHelloMessage != NULL) { + collecClientHelloStats(clientHelloMessage); + } + + // try to find server-hello message + pcpp::SSLServerHelloMessage* serverHelloMessage = + handshakeLayer + ->getHandshakeMessageOfType(); + + // collect server-hello stats + if (serverHelloMessage != NULL) { + m_GeneralStats.sslVersionCount + [serverHelloMessage->getHandshakeVersion().asUInt()]++; + collecServerHelloStats(serverHelloMessage); + } + } + + sslLayer = sslPacket->getNextLayerOfType(sslLayer); + } + } + + /** + * Collect stats relevant only to client-hello messages + */ + void collecClientHelloStats(pcpp::SSLClientHelloMessage* clientHelloMessage) { + m_ClientHelloStats.numOfMessages++; + + pcpp::SSLServerNameIndicationExtension* sniExt = + clientHelloMessage + ->getExtensionOfType(); + if (sniExt != NULL) + m_ClientHelloStats.serverNameCount[sniExt->getHostName()]++; + } + + /** + * Collect stats relevant only to server-hello messages + */ + void collecServerHelloStats(pcpp::SSLServerHelloMessage* serverHelloMessage) { + m_ServerHelloStats.numOfMessages++; + + pcpp::SSLCipherSuite* cipherSuite = serverHelloMessage->getCipherSuite(); + if (cipherSuite != NULL) + m_ServerHelloStats.cipherSuiteCount[cipherSuite->asString()]++; + } + + double getCurTime(void) { + struct timeval tv; + + gettimeofday(&tv, NULL); + + return (((double)tv.tv_sec) + (double)(tv.tv_usec / 1000000.0)); + } + + SSLGeneralStats m_GeneralStats; + SSLGeneralStats m_PrevGeneralStats; + ClientHelloStats m_ClientHelloStats; + ClientHelloStats m_PrevClientHelloStats; + ServerHelloStats m_ServerHelloStats; + ServerHelloStats m_PrevServerHelloStats; + + std::map m_FlowTable; + + double m_LastCalcRateTime; + double m_StartTime; }; diff --git a/Examples/SSLAnalyzer/main.cpp b/Examples/SSLAnalyzer/main.cpp index 8ed220c976..f063a02ba0 100644 --- a/Examples/SSLAnalyzer/main.cpp +++ b/Examples/SSLAnalyzer/main.cpp @@ -1,15 +1,18 @@ /** * SSLAnalyzer application * ======================== - * This application analyzes SSL/TLS traffic and presents detailed and diverse information about it. It can operate in live traffic - * mode where this information is collected on live packets or in file mode where packets are being read from a pcap/pcapng file. The - * information collected by this application includes: + * This application analyzes SSL/TLS traffic and presents detailed and diverse + * information about it. It can operate in live traffic mode where this + * information is collected on live packets or in file mode where packets are + * being read from a pcap/pcapng file. The information collected by this + * application includes: * - general data: number of packets, packet rate, amount of traffic, bandwidth - * - flow data: number of flow, flow rate, average packets per flow, average data per flow - * - SSL/TLS data: number of client-hello and server-hello messages, number of flows ended with successful handshake, - * number of flows ended with SSL alert - * - hostname map (which hostnames were used and how much. Taken from the server-name-indication extension in the - * client-hello message) + * - flow data: number of flow, flow rate, average packets per flow, average + * data per flow + * - SSL/TLS data: number of client-hello and server-hello messages, number of + * flows ended with successful handshake, number of flows ended with SSL alert + * - hostname map (which hostnames were used and how much. Taken from the + * server-name-indication extension in the client-hello message) * - cipher-suite map (which cipher-suites were used and how much) * - SSL/TLS versions map (which SSL/TLS versions were used and how much) * - SSL/TLS ports map (which SSL/TLS TCP ports were used and how much) @@ -17,552 +20,575 @@ * For more details about modes of operation and parameters run SSLAnalyzer -h */ -#include -#include -#include -#include -#include -#include "PcapLiveDeviceList.h" -#include "PcapFilter.h" #include "PcapFileDevice.h" +#include "PcapFilter.h" +#include "PcapLiveDeviceList.h" +#include "PcapPlusPlusVersion.h" #include "SSLStatsCollector.h" -#include "TablePrinter.h" #include "SystemUtils.h" -#include "PcapPlusPlusVersion.h" +#include "TablePrinter.h" +#include #include +#include +#include +#include +#include - -#define EXIT_WITH_ERROR(reason) do { \ - printUsage(); \ - std::cout << std::endl << "ERROR: " << reason << std::endl << std::endl; \ - exit(1); \ - } while(0) - - -#define PRINT_STAT_LINE(description, counter, measurement) \ - std::cout \ - << std::left << std::setw(46) << (std::string(description) + ":") \ - << std::right << std::setw(15) << std::fixed << std::showpoint << std::setprecision(3) << counter \ - << " [" << measurement << "]" << std::endl; - +#define EXIT_WITH_ERROR(reason) \ + do { \ + printUsage(); \ + std::cout << std::endl \ + << "ERROR: " << reason << std::endl \ + << std::endl; \ + exit(1); \ + } while (0) + +#define PRINT_STAT_LINE(description, counter, measurement) \ + std::cout << std::left << std::setw(46) << (std::string(description) + ":") \ + << std::right << std::setw(15) << std::fixed << std::showpoint \ + << std::setprecision(3) << counter << " [" << measurement << "]" \ + << std::endl; #define DEFAULT_CALC_RATES_PERIOD_SEC 2 - -static struct option SSLAnalyzerOptions[] = -{ - {"interface", required_argument, nullptr, 'i'}, - {"input-file", required_argument, nullptr, 'f'}, - {"output-file", required_argument, nullptr, 'o'}, - {"rate-calc-period", required_argument, nullptr, 'r'}, - {"disable-rates-print", no_argument, nullptr, 'd'}, - {"list-interfaces", no_argument, nullptr, 'l'}, - {"help", no_argument, nullptr, 'h'}, - {"version", no_argument, nullptr, 'v'}, - {nullptr, 0, nullptr, 0} -}; - - -struct SSLPacketArrivedData -{ - SSLStatsCollector* statsCollector; - pcpp::PcapFileWriterDevice* pcapWriter; +static struct option SSLAnalyzerOptions[] = { + {"interface", required_argument, nullptr, 'i'}, + {"input-file", required_argument, nullptr, 'f'}, + {"output-file", required_argument, nullptr, 'o'}, + {"rate-calc-period", required_argument, nullptr, 'r'}, + {"disable-rates-print", no_argument, nullptr, 'd'}, + {"list-interfaces", no_argument, nullptr, 'l'}, + {"help", no_argument, nullptr, 'h'}, + {"version", no_argument, nullptr, 'v'}, + {nullptr, 0, nullptr, 0}}; + +struct SSLPacketArrivedData { + SSLStatsCollector* statsCollector; + pcpp::PcapFileWriterDevice* pcapWriter; }; - /** * Print application usage */ -void printUsage() -{ - std::cout << std::endl - << "Usage: PCAP file mode:" << std::endl - << "----------------------" << std::endl - << pcpp::AppName::get() << " [-hv] -f input_file" << std::endl - << std::endl - << "Options:" << std::endl - << std::endl - << " -f : The input pcap/pcapng file to analyze. Required argument for this mode" << std::endl - << " -v : Displays the current version and exists" << std::endl - << " -h : Displays this help message and exits" << std::endl - << std::endl - << "Usage: Live traffic mode:" << std::endl - << "-------------------------" << std::endl - << pcpp::AppName::get() << " [-hvld] [-o output_file] [-r calc_period] -i interface" << std::endl - << std::endl - << "Options:" << std::endl - << std::endl - << " -i interface : Use the specified interface. Can be interface name (e.g eth0) or interface IPv4 address" << std::endl - << " -o output_file : Save all captured SSL packets to a pcap file. Notice this may cause performance degradation" << std::endl - << " -r calc_period : The period in seconds to calculate rates. If not provided default is 2 seconds" << std::endl - << " -d : Disable periodic rates calculation" << std::endl - << " -v : Displays the current version and exists" << std::endl - << " -h : Displays this help message and exits" << std::endl - << " -l : Print the list of interfaces and exists" << std::endl - << std::endl; +void printUsage() { + std::cout << std::endl + << "Usage: PCAP file mode:" << std::endl + << "----------------------" << std::endl + << pcpp::AppName::get() << " [-hv] -f input_file" << std::endl + << std::endl + << "Options:" << std::endl + << std::endl + << " -f : The input pcap/pcapng file to analyze. " + "Required argument for this mode" + << std::endl + << " -v : Displays the current version and exists" + << std::endl + << " -h : Displays this help message and exits" + << std::endl + << std::endl + << "Usage: Live traffic mode:" << std::endl + << "-------------------------" << std::endl + << pcpp::AppName::get() + << " [-hvld] [-o output_file] [-r calc_period] -i interface" + << std::endl + << std::endl + << "Options:" << std::endl + << std::endl + << " -i interface : Use the specified interface. Can be " + "interface name (e.g eth0) or interface IPv4 address" + << std::endl + << " -o output_file : Save all captured SSL packets to a pcap " + "file. Notice this may cause performance degradation" + << std::endl + << " -r calc_period : The period in seconds to calculate rates. " + "If not provided default is 2 seconds" + << std::endl + << " -d : Disable periodic rates calculation" + << std::endl + << " -v : Displays the current version and exists" + << std::endl + << " -h : Displays this help message and exits" + << std::endl + << " -l : Print the list of interfaces and exists" + << std::endl + << std::endl; } - /** * Print application version */ -void printAppVersion() -{ - std::cout - << pcpp::AppName::get() << " " << pcpp::getPcapPlusPlusVersionFull() << std::endl - << "Built: " << pcpp::getBuildDateTime() << std::endl - << "Built from: " << pcpp::getGitInfo() << std::endl; - exit(0); +void printAppVersion() { + std::cout << pcpp::AppName::get() << " " << pcpp::getPcapPlusPlusVersionFull() + << std::endl + << "Built: " << pcpp::getBuildDateTime() << std::endl + << "Built from: " << pcpp::getGitInfo() << std::endl; + exit(0); } - /** * Go over all interfaces and output their names */ -void listInterfaces() -{ - const std::vector& devList = pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDevicesList(); - - std::cout << std::endl << "Network interfaces:" << std::endl; - for (std::vector::const_iterator iter = devList.begin(); iter != devList.end(); iter++) - { - std::cout << " -> Name: '" << (*iter)->getName() << "' IP address: " << (*iter)->getIPv4Address().toString() << std::endl; - } - exit(0); +void listInterfaces() { + const std::vector& devList = + pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDevicesList(); + + std::cout << std::endl + << "Network interfaces:" << std::endl; + for (std::vector::const_iterator iter = + devList.begin(); + iter != devList.end(); iter++) { + std::cout << " -> Name: '" << (*iter)->getName() + << "' IP address: " << (*iter)->getIPv4Address().toString() + << std::endl; + } + exit(0); } +void printStatsHeadline(const std::string& description) { + std::string underline; + for (size_t i = 0; i < description.length(); i++) { + underline += "-"; + } -void printStatsHeadline(const std::string &description) -{ - std::string underline; - for (size_t i = 0; i < description.length(); i++) - { - underline += "-"; - } - - std::cout << std::endl << description << std::endl << underline << std::endl << std::endl; + std::cout << std::endl + << description << std::endl + << underline << std::endl + << std::endl; } - /** * packet capture callback - called whenever a packet arrives */ -void sslPacketArrive(pcpp::RawPacket* packet, pcpp::PcapLiveDevice* dev, void* cookie) -{ - // parse the packet - pcpp::Packet parsedPacket(packet); +void sslPacketArrive(pcpp::RawPacket* packet, pcpp::PcapLiveDevice* dev, + void* cookie) { + // parse the packet + pcpp::Packet parsedPacket(packet); - SSLPacketArrivedData* data = (SSLPacketArrivedData*)cookie; + SSLPacketArrivedData* data = (SSLPacketArrivedData*)cookie; - // give the packet to the collector - data->statsCollector->collectStats(&parsedPacket); + // give the packet to the collector + data->statsCollector->collectStats(&parsedPacket); - // if needed - write the packet to the output pcap file - if (data->pcapWriter != nullptr) - { - data->pcapWriter->writePacket(*packet); - } + // if needed - write the packet to the output pcap file + if (data->pcapWriter != nullptr) { + data->pcapWriter->writePacket(*packet); + } } - /** - * An auxiliary method for sorting the string count map. Used in printServerNames() and in printCipherSuites() + * An auxiliary method for sorting the string count map. Used in + * printServerNames() and in printCipherSuites() */ -bool stringCountComparer(const std::pair& first, const std::pair& second) -{ - if (first.second == second.second) - { - return first.first > second.first; - } - return first.second > second.second; +bool stringCountComparer(const std::pair& first, + const std::pair& second) { + if (first.second == second.second) { + return first.first > second.first; + } + return first.second > second.second; } - /** * An auxiliary method for sorting the uint16_t count map. Used in printPorts() */ -bool uint16CountComparer(std::pair first, std::pair second) -{ - if (first.second == second.second) - { - return first.first > second.first; - } - return first.second > second.second; +bool uint16CountComparer(std::pair first, + std::pair second) { + if (first.second == second.second) { + return first.first > second.first; + } + return first.second > second.second; } - /** - * Print the server-name count map to a table sorted by popularity (most popular names will be first) + * Print the server-name count map to a table sorted by popularity (most popular + * names will be first) */ -void printServerNames(ClientHelloStats& clientHelloStatsCollector) -{ - // create the table - std::vector columnNames; - columnNames.push_back("Hostname"); - columnNames.push_back("Count"); - std::vector columnsWidths; - columnsWidths.push_back(40); - columnsWidths.push_back(5); - pcpp::TablePrinter printer(columnNames, columnsWidths); - - // sort the server-name count map so the most popular names will be first - // since it's not possible to sort a std::map you must copy it to a std::vector and sort it then - std::vector > map2vec(clientHelloStatsCollector.serverNameCount.begin(), clientHelloStatsCollector.serverNameCount.end()); - std::sort(map2vec.begin(),map2vec.end(), &stringCountComparer); - - // go over all items (names + count) in the sorted vector and print them - for(std::vector >::iterator iter = map2vec.begin(); - iter != map2vec.end(); - iter++) - { - std::stringstream values; - values << iter->first << "|" << iter->second; - printer.printRow(values.str(), '|'); - } +void printServerNames(ClientHelloStats& clientHelloStatsCollector) { + // create the table + std::vector columnNames; + columnNames.push_back("Hostname"); + columnNames.push_back("Count"); + std::vector columnsWidths; + columnsWidths.push_back(40); + columnsWidths.push_back(5); + pcpp::TablePrinter printer(columnNames, columnsWidths); + + // sort the server-name count map so the most popular names will be first + // since it's not possible to sort a std::map you must copy it to a + // std::vector and sort it then + std::vector> map2vec( + clientHelloStatsCollector.serverNameCount.begin(), + clientHelloStatsCollector.serverNameCount.end()); + std::sort(map2vec.begin(), map2vec.end(), &stringCountComparer); + + // go over all items (names + count) in the sorted vector and print them + for (std::vector>::iterator iter = + map2vec.begin(); + iter != map2vec.end(); iter++) { + std::stringstream values; + values << iter->first << "|" << iter->second; + printer.printRow(values.str(), '|'); + } } - /** * Print SSL record version map */ -void printVersions(std::map& versionMap, const std::string& headline) -{ - // create the table - std::vector columnNames; - columnNames.push_back(headline); - columnNames.push_back("Count"); - std::vector columnsWidths; - columnsWidths.push_back(28); - columnsWidths.push_back(5); - pcpp::TablePrinter printer(columnNames, columnsWidths); - - // sort the version map so the most popular version will be first - // since it's not possible to sort a std::map you must copy it to a std::vector and sort it then - std::vector > map2vec(versionMap.begin(), versionMap.end()); - std::sort(map2vec.begin(),map2vec.end(), &uint16CountComparer); - - // go over all items (names + count) in the sorted vector and print them - for(std::vector >::iterator iter = map2vec.begin(); - iter != map2vec.end(); - iter++) - { - std::stringstream values; - values << pcpp::SSLVersion(iter->first).toString() << "|" << iter->second; - printer.printRow(values.str(), '|'); - } +void printVersions(std::map& versionMap, + const std::string& headline) { + // create the table + std::vector columnNames; + columnNames.push_back(headline); + columnNames.push_back("Count"); + std::vector columnsWidths; + columnsWidths.push_back(28); + columnsWidths.push_back(5); + pcpp::TablePrinter printer(columnNames, columnsWidths); + + // sort the version map so the most popular version will be first + // since it's not possible to sort a std::map you must copy it to a + // std::vector and sort it then + std::vector> map2vec(versionMap.begin(), + versionMap.end()); + std::sort(map2vec.begin(), map2vec.end(), &uint16CountComparer); + + // go over all items (names + count) in the sorted vector and print them + for (std::vector>::iterator iter = map2vec.begin(); + iter != map2vec.end(); iter++) { + std::stringstream values; + values << pcpp::SSLVersion(iter->first).toString() << "|" << iter->second; + printer.printRow(values.str(), '|'); + } } - /** - * Print used cipher-suite map to a table sorted by popularity (most popular cipher-suite will be first) + * Print used cipher-suite map to a table sorted by popularity (most popular + * cipher-suite will be first) */ -void printCipherSuites(ServerHelloStats& serverHelloStats) -{ - // create the table - std::vector columnNames; - columnNames.push_back("Cipher-suite"); - columnNames.push_back("Count"); - std::vector columnsWidths; - columnsWidths.push_back(50); - columnsWidths.push_back(5); - pcpp::TablePrinter printer(columnNames, columnsWidths); - - // sort the cipher-suite count map so the most popular names will be first - // since it's not possible to sort a std::map you must copy it to a std::vector and sort it then - std::vector > map2vec(serverHelloStats.cipherSuiteCount.begin(), serverHelloStats.cipherSuiteCount.end()); - std::sort(map2vec.begin(),map2vec.end(), &stringCountComparer); - - // go over all items (names + count) in the sorted vector and print them - for(std::vector >::iterator iter = map2vec.begin(); - iter != map2vec.end(); - iter++) - { - std::stringstream values; - values << iter->first << "|" << iter->second; - printer.printRow(values.str(), '|'); - } +void printCipherSuites(ServerHelloStats& serverHelloStats) { + // create the table + std::vector columnNames; + columnNames.push_back("Cipher-suite"); + columnNames.push_back("Count"); + std::vector columnsWidths; + columnsWidths.push_back(50); + columnsWidths.push_back(5); + pcpp::TablePrinter printer(columnNames, columnsWidths); + + // sort the cipher-suite count map so the most popular names will be first + // since it's not possible to sort a std::map you must copy it to a + // std::vector and sort it then + std::vector> map2vec( + serverHelloStats.cipherSuiteCount.begin(), + serverHelloStats.cipherSuiteCount.end()); + std::sort(map2vec.begin(), map2vec.end(), &stringCountComparer); + + // go over all items (names + count) in the sorted vector and print them + for (std::vector>::iterator iter = + map2vec.begin(); + iter != map2vec.end(); iter++) { + std::stringstream values; + values << iter->first << "|" << iter->second; + printer.printRow(values.str(), '|'); + } } - -void printPorts(SSLGeneralStats& stats) -{ - // create the table - std::vector columnNames; - columnNames.push_back("SSL/TLS ports"); - columnNames.push_back("Count"); - std::vector columnsWidths; - columnsWidths.push_back(13); - columnsWidths.push_back(5); - pcpp::TablePrinter printer(columnNames, columnsWidths); - - // sort the port count map so the most popular names will be first - // since it's not possible to sort a std::map you must copy it to a std::vector and sort it then - std::vector > map2vec(stats.sslPortCount.begin(), stats.sslPortCount.end()); - std::sort(map2vec.begin(),map2vec.end(), &uint16CountComparer); - - // go over all items (names + count) in the sorted vector and print them - for(std::vector >::iterator iter = map2vec.begin(); - iter != map2vec.end(); - iter++) - { - std::stringstream values; - values << iter->first << "|" << iter->second; - printer.printRow(values.str(), '|'); - } +void printPorts(SSLGeneralStats& stats) { + // create the table + std::vector columnNames; + columnNames.push_back("SSL/TLS ports"); + columnNames.push_back("Count"); + std::vector columnsWidths; + columnsWidths.push_back(13); + columnsWidths.push_back(5); + pcpp::TablePrinter printer(columnNames, columnsWidths); + + // sort the port count map so the most popular names will be first + // since it's not possible to sort a std::map you must copy it to a + // std::vector and sort it then + std::vector> map2vec(stats.sslPortCount.begin(), + stats.sslPortCount.end()); + std::sort(map2vec.begin(), map2vec.end(), &uint16CountComparer); + + // go over all items (names + count) in the sorted vector and print them + for (std::vector>::iterator iter = map2vec.begin(); + iter != map2vec.end(); iter++) { + std::stringstream values; + values << iter->first << "|" << iter->second; + printer.printRow(values.str(), '|'); + } } - /** - * Print a summary of all statistics collected by the SSLStatsCollector. Should be called when traffic capture was finished + * Print a summary of all statistics collected by the SSLStatsCollector. Should + * be called when traffic capture was finished */ -void printStatsSummary(SSLStatsCollector& collector) -{ - printStatsHeadline("General stats"); - PRINT_STAT_LINE("Sample time", collector.getGeneralStats().sampleTime, "Seconds"); - PRINT_STAT_LINE("Number of SSL packets", collector.getGeneralStats().numOfSSLPackets, "Packets"); - PRINT_STAT_LINE("Rate of SSL packets", collector.getGeneralStats().sslPacketRate.totalRate, "Packets/sec"); - PRINT_STAT_LINE("Number of SSL flows", collector.getGeneralStats().numOfSSLFlows, "Flows"); - PRINT_STAT_LINE("Rate of SSL flows", collector.getGeneralStats().sslFlowRate.totalRate, "Flows/sec"); - PRINT_STAT_LINE("Total SSL data", collector.getGeneralStats().amountOfSSLTraffic, "Bytes"); - PRINT_STAT_LINE("Rate of SSL data", collector.getGeneralStats().sslTrafficRate.totalRate, "Bytes/sec"); - PRINT_STAT_LINE("Average packets per flow", collector.getGeneralStats().averageNumOfPacketsPerFlow, "Packets"); - PRINT_STAT_LINE("Average data per flow", collector.getGeneralStats().averageAmountOfDataPerFlow, "Bytes"); - PRINT_STAT_LINE("Client-hello message", collector.getClientHelloStats().numOfMessages, "Messages"); - PRINT_STAT_LINE("Server-hello message", collector.getServerHelloStats().numOfMessages, "Messages"); - PRINT_STAT_LINE("Number of SSL flows with successful handshake", collector.getGeneralStats().numOfHandshakeCompleteFlows, "Flows"); - PRINT_STAT_LINE("Number of SSL flows ended with alert", collector.getGeneralStats().numOfFlowsWithAlerts, "Flows"); - - printStatsHeadline("SSL/TLS ports count"); - printPorts(collector.getGeneralStats()); - - printStatsHeadline("SSL/TLS versions count"); - printVersions(collector.getGeneralStats().sslVersionCount, std::string("SSL/TLS version")); - - printStatsHeadline("Cipher-suite count"); - printCipherSuites(collector.getServerHelloStats()); - - printStatsHeadline("Server-name count"); - printServerNames(collector.getClientHelloStats()); - +void printStatsSummary(SSLStatsCollector& collector) { + printStatsHeadline("General stats"); + PRINT_STAT_LINE("Sample time", collector.getGeneralStats().sampleTime, + "Seconds"); + PRINT_STAT_LINE("Number of SSL packets", + collector.getGeneralStats().numOfSSLPackets, "Packets"); + PRINT_STAT_LINE("Rate of SSL packets", + collector.getGeneralStats().sslPacketRate.totalRate, + "Packets/sec"); + PRINT_STAT_LINE("Number of SSL flows", + collector.getGeneralStats().numOfSSLFlows, "Flows"); + PRINT_STAT_LINE("Rate of SSL flows", + collector.getGeneralStats().sslFlowRate.totalRate, + "Flows/sec"); + PRINT_STAT_LINE("Total SSL data", + collector.getGeneralStats().amountOfSSLTraffic, "Bytes"); + PRINT_STAT_LINE("Rate of SSL data", + collector.getGeneralStats().sslTrafficRate.totalRate, + "Bytes/sec"); + PRINT_STAT_LINE("Average packets per flow", + collector.getGeneralStats().averageNumOfPacketsPerFlow, + "Packets"); + PRINT_STAT_LINE("Average data per flow", + collector.getGeneralStats().averageAmountOfDataPerFlow, + "Bytes"); + PRINT_STAT_LINE("Client-hello message", + collector.getClientHelloStats().numOfMessages, "Messages"); + PRINT_STAT_LINE("Server-hello message", + collector.getServerHelloStats().numOfMessages, "Messages"); + PRINT_STAT_LINE("Number of SSL flows with successful handshake", + collector.getGeneralStats().numOfHandshakeCompleteFlows, + "Flows"); + PRINT_STAT_LINE("Number of SSL flows ended with alert", + collector.getGeneralStats().numOfFlowsWithAlerts, "Flows"); + + printStatsHeadline("SSL/TLS ports count"); + printPorts(collector.getGeneralStats()); + + printStatsHeadline("SSL/TLS versions count"); + printVersions(collector.getGeneralStats().sslVersionCount, + std::string("SSL/TLS version")); + + printStatsHeadline("Cipher-suite count"); + printCipherSuites(collector.getServerHelloStats()); + + printStatsHeadline("Server-name count"); + printServerNames(collector.getClientHelloStats()); } - /** * Print the current rates. Should be called periodically during traffic capture */ -void printCurrentRates(SSLStatsCollector& collector) -{ - printStatsHeadline("Current SSL rates"); - PRINT_STAT_LINE("Rate of SSL packets", collector.getGeneralStats().sslPacketRate.currentRate, "Packets/sec"); - PRINT_STAT_LINE("Rate of SSL flows", collector.getGeneralStats().sslFlowRate.currentRate, "Flows/sec"); - PRINT_STAT_LINE("Rate of SSL data", collector.getGeneralStats().sslTrafficRate.currentRate, "Bytes/sec"); - PRINT_STAT_LINE("Rate of SSL requests", collector.getClientHelloStats().messageRate.currentRate, "Requests/sec"); - PRINT_STAT_LINE("Rate of SSL responses", collector.getServerHelloStats().messageRate.currentRate, "Responses/sec"); +void printCurrentRates(SSLStatsCollector& collector) { + printStatsHeadline("Current SSL rates"); + PRINT_STAT_LINE("Rate of SSL packets", + collector.getGeneralStats().sslPacketRate.currentRate, + "Packets/sec"); + PRINT_STAT_LINE("Rate of SSL flows", + collector.getGeneralStats().sslFlowRate.currentRate, + "Flows/sec"); + PRINT_STAT_LINE("Rate of SSL data", + collector.getGeneralStats().sslTrafficRate.currentRate, + "Bytes/sec"); + PRINT_STAT_LINE("Rate of SSL requests", + collector.getClientHelloStats().messageRate.currentRate, + "Requests/sec"); + PRINT_STAT_LINE("Rate of SSL responses", + collector.getServerHelloStats().messageRate.currentRate, + "Responses/sec"); } - /** - * The callback to be called when application is terminated by ctrl-c. Stops the endless while loop + * The callback to be called when application is terminated by ctrl-c. Stops the + * endless while loop */ -void onApplicationInterrupted(void* cookie) -{ - bool* shouldStop = (bool*)cookie; - *shouldStop = true; +void onApplicationInterrupted(void* cookie) { + bool* shouldStop = (bool*)cookie; + *shouldStop = true; } - /** * activate SSL/TLS analysis from pcap file */ -void analyzeSSLFromPcapFile(const std::string& pcapFileName) -{ - // open input file (pcap or pcapng file) - pcpp::IFileReaderDevice* reader = pcpp::IFileReaderDevice::getReader(pcapFileName); - - if (!reader->open()) - EXIT_WITH_ERROR("Could not open input pcap file"); - - // read the input file packet by packet and give it to the SSLStatsCollector for collecting stats - SSLStatsCollector collector; - pcpp::RawPacket rawPacket; - while(reader->getNextPacket(rawPacket)) - { - pcpp::Packet parsedPacket(&rawPacket); - collector.collectStats(&parsedPacket); - } - - // print stats summary - std::cout << std::endl << std::endl - << "STATS SUMMARY" << std::endl - << "=============" << std::endl; - printStatsSummary(collector); - - // close input file - reader->close(); - - // free reader memory - delete reader; +void analyzeSSLFromPcapFile(const std::string& pcapFileName) { + // open input file (pcap or pcapng file) + pcpp::IFileReaderDevice* reader = + pcpp::IFileReaderDevice::getReader(pcapFileName); + + if (!reader->open()) + EXIT_WITH_ERROR("Could not open input pcap file"); + + // read the input file packet by packet and give it to the SSLStatsCollector + // for collecting stats + SSLStatsCollector collector; + pcpp::RawPacket rawPacket; + while (reader->getNextPacket(rawPacket)) { + pcpp::Packet parsedPacket(&rawPacket); + collector.collectStats(&parsedPacket); + } + + // print stats summary + std::cout << std::endl + << std::endl + << "STATS SUMMARY" << std::endl + << "=============" << std::endl; + printStatsSummary(collector); + + // close input file + reader->close(); + + // free reader memory + delete reader; } - /** * activate SSL analysis from live traffic */ -void analyzeSSLFromLiveTraffic(pcpp::PcapLiveDevice* dev, bool printRatesPeriodically, int printRatePeriod, const std::string& savePacketsToFileName) -{ - // open the device - if (!dev->open()) - EXIT_WITH_ERROR("Could not open the device"); - - // set SSL/TLS ports filter on the live device to capture only SSL/TLS packets - std::vector portFilterVec; - - // Detect all ports considered as SSL/TLS traffic and add them to the filter. - // The check is made for well known ports because currently SSLLayer does not support customizing of ports considered as SSL/TLS. - for (uint16_t port = 0; port < 1024; ++port) - if (pcpp::SSLLayer::isSSLPort(port)) - portFilterVec.push_back(new pcpp::PortFilter(port, pcpp::SRC_OR_DST)); - - // make an OR filter out of all port filters - pcpp::OrFilter orFilter(portFilterVec); - - // set the filter for the device - if (!dev->setFilter(orFilter)) - { - std::string filterAsString; - orFilter.parseToString(filterAsString); - EXIT_WITH_ERROR("Couldn't set the filter '" << filterAsString << "' for the device"); - } - - - // if needed to save the captured packets to file - open a writer device - pcpp::PcapFileWriterDevice* pcapWriter = nullptr; - if (savePacketsToFileName != "") - { - pcapWriter = new pcpp::PcapFileWriterDevice(savePacketsToFileName); - if (!pcapWriter->open()) - { - EXIT_WITH_ERROR("Could not open pcap file for writing"); - } - } - - // start capturing packets and collecting stats - SSLPacketArrivedData data; - SSLStatsCollector collector; - data.statsCollector = &collector; - data.pcapWriter = pcapWriter; - dev->startCapture(sslPacketArrive, &data); - - - // register the on app close event to print summary stats on app termination - bool shouldStop = false; - pcpp::ApplicationEventHandler::getInstance().onApplicationInterrupted(onApplicationInterrupted, &shouldStop); - - while(!shouldStop) - { - pcpp::multiPlatformSleep(printRatePeriod); - - // calculate rates - if (printRatesPeriodically) - { - collector.calcRates(); - printCurrentRates(collector); - } - } - - // stop capturing and close the live device - dev->stopCapture(); - dev->close(); - - // calculate final rates - collector.calcRates(); - - // print stats summary - std::cout << std::endl << std::endl - << "STATS SUMMARY" << std::endl - << "=============" << std::endl; - printStatsSummary(collector); - - // close and free the writer device - if (pcapWriter != nullptr) - { - pcapWriter->close(); - delete pcapWriter; - } +void analyzeSSLFromLiveTraffic(pcpp::PcapLiveDevice* dev, + bool printRatesPeriodically, int printRatePeriod, + const std::string& savePacketsToFileName) { + // open the device + if (!dev->open()) + EXIT_WITH_ERROR("Could not open the device"); + + // set SSL/TLS ports filter on the live device to capture only SSL/TLS packets + std::vector portFilterVec; + + // Detect all ports considered as SSL/TLS traffic and add them to the filter. + // The check is made for well known ports because currently SSLLayer does not + // support customizing of ports considered as SSL/TLS. + for (uint16_t port = 0; port < 1024; ++port) + if (pcpp::SSLLayer::isSSLPort(port)) + portFilterVec.push_back(new pcpp::PortFilter(port, pcpp::SRC_OR_DST)); + + // make an OR filter out of all port filters + pcpp::OrFilter orFilter(portFilterVec); + + // set the filter for the device + if (!dev->setFilter(orFilter)) { + std::string filterAsString; + orFilter.parseToString(filterAsString); + EXIT_WITH_ERROR("Couldn't set the filter '" << filterAsString + << "' for the device"); + } + + // if needed to save the captured packets to file - open a writer device + pcpp::PcapFileWriterDevice* pcapWriter = nullptr; + if (savePacketsToFileName != "") { + pcapWriter = new pcpp::PcapFileWriterDevice(savePacketsToFileName); + if (!pcapWriter->open()) { + EXIT_WITH_ERROR("Could not open pcap file for writing"); + } + } + + // start capturing packets and collecting stats + SSLPacketArrivedData data; + SSLStatsCollector collector; + data.statsCollector = &collector; + data.pcapWriter = pcapWriter; + dev->startCapture(sslPacketArrive, &data); + + // register the on app close event to print summary stats on app termination + bool shouldStop = false; + pcpp::ApplicationEventHandler::getInstance().onApplicationInterrupted( + onApplicationInterrupted, &shouldStop); + + while (!shouldStop) { + pcpp::multiPlatformSleep(printRatePeriod); + + // calculate rates + if (printRatesPeriodically) { + collector.calcRates(); + printCurrentRates(collector); + } + } + + // stop capturing and close the live device + dev->stopCapture(); + dev->close(); + + // calculate final rates + collector.calcRates(); + + // print stats summary + std::cout << std::endl + << std::endl + << "STATS SUMMARY" << std::endl + << "=============" << std::endl; + printStatsSummary(collector); + + // close and free the writer device + if (pcapWriter != nullptr) { + pcapWriter->close(); + delete pcapWriter; + } } /** * main method of this utility */ -int main(int argc, char* argv[]) -{ - pcpp::AppName::init(argc, argv); - - std::string interfaceNameOrIP = ""; - bool printRatesPeriodically = true; - int printRatePeriod = DEFAULT_CALC_RATES_PERIOD_SEC; - std::string savePacketsToFileName = ""; - - std::string readPacketsFromPcapFileName = ""; - - - int optionIndex = 0; - int opt = 0; - - while((opt = getopt_long(argc, argv, "i:f:o:r:hvld", SSLAnalyzerOptions, &optionIndex)) != -1) - { - switch (opt) - { - case 0: - break; - case 'i': - interfaceNameOrIP = optarg; - break; - case 'f': - readPacketsFromPcapFileName = optarg; - break; - case 'o': - savePacketsToFileName = optarg; - break; - case 'r': - printRatePeriod = atoi(optarg); - break; - case 'd': - printRatesPeriodically = false; - break; - case 'v': - printAppVersion(); - break; - case 'h': - printUsage(); - exit(0); - break; - case 'l': - listInterfaces(); - break; - default: - printUsage(); - exit(-1); - } - } - - // if no interface nor input pcap file were provided - exit with error - if (readPacketsFromPcapFileName == "" && interfaceNameOrIP == "") - EXIT_WITH_ERROR("Neither interface nor input pcap file were provided"); - - // analyze in pcap file mode - if (readPacketsFromPcapFileName != "") - { - analyzeSSLFromPcapFile(readPacketsFromPcapFileName); - } - else // analyze in live traffic mode - { - // extract pcap live device by interface name or IP address - pcpp::PcapLiveDevice* dev = pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDeviceByIpOrName(interfaceNameOrIP); - if (dev == nullptr) - EXIT_WITH_ERROR("Couldn't find interface by provided IP address or name"); - - // start capturing and analyzing traffic - analyzeSSLFromLiveTraffic(dev, printRatesPeriodically, printRatePeriod, savePacketsToFileName); - } +int main(int argc, char* argv[]) { + pcpp::AppName::init(argc, argv); + + std::string interfaceNameOrIP = ""; + bool printRatesPeriodically = true; + int printRatePeriod = DEFAULT_CALC_RATES_PERIOD_SEC; + std::string savePacketsToFileName = ""; + + std::string readPacketsFromPcapFileName = ""; + + int optionIndex = 0; + int opt = 0; + + while ((opt = getopt_long(argc, argv, "i:f:o:r:hvld", SSLAnalyzerOptions, + &optionIndex)) != -1) { + switch (opt) { + case 0: + break; + case 'i': + interfaceNameOrIP = optarg; + break; + case 'f': + readPacketsFromPcapFileName = optarg; + break; + case 'o': + savePacketsToFileName = optarg; + break; + case 'r': + printRatePeriod = atoi(optarg); + break; + case 'd': + printRatesPeriodically = false; + break; + case 'v': + printAppVersion(); + break; + case 'h': + printUsage(); + exit(0); + break; + case 'l': + listInterfaces(); + break; + default: + printUsage(); + exit(-1); + } + } + + // if no interface nor input pcap file were provided - exit with error + if (readPacketsFromPcapFileName == "" && interfaceNameOrIP == "") + EXIT_WITH_ERROR("Neither interface nor input pcap file were provided"); + + // analyze in pcap file mode + if (readPacketsFromPcapFileName != "") { + analyzeSSLFromPcapFile(readPacketsFromPcapFileName); + } else // analyze in live traffic mode + { + // extract pcap live device by interface name or IP address + pcpp::PcapLiveDevice* dev = + pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDeviceByIpOrName( + interfaceNameOrIP); + if (dev == nullptr) + EXIT_WITH_ERROR("Couldn't find interface by provided IP address or name"); + + // start capturing and analyzing traffic + analyzeSSLFromLiveTraffic(dev, printRatesPeriodically, printRatePeriod, + savePacketsToFileName); + } } diff --git a/Examples/TLSFingerprinting/main.cpp b/Examples/TLSFingerprinting/main.cpp index b6913f8b7f..9823c2d081 100644 --- a/Examples/TLSFingerprinting/main.cpp +++ b/Examples/TLSFingerprinting/main.cpp @@ -2,621 +2,661 @@ * TLS Fingerprinting application * ============================== * - * This application demonstrates how to extract and use TLS fingerprinting data using PcapPlusPlus. - * Please read the README.md file for more information. + * This application demonstrates how to extract and use TLS fingerprinting data + * using PcapPlusPlus. Please read the README.md file for more information. * - * You can also run `TLSFingerprinting -h` for modes of operation and parameters. + * You can also run `TLSFingerprinting -h` for modes of operation and + * parameters. */ -#include -#include -#include -#include -#include -#include -#include +#include "IPLayer.h" +#include "PcapFileDevice.h" +#include "PcapLiveDeviceList.h" +#include "PcapPlusPlusVersion.h" +#include "SSLHandshake.h" +#include "SSLLayer.h" #include "SystemUtils.h" #include "TablePrinter.h" -#include "IPLayer.h" #include "TcpLayer.h" -#include "SSLLayer.h" -#include "SSLHandshake.h" -#include "PcapPlusPlusVersion.h" -#include "PcapLiveDeviceList.h" -#include "PcapFileDevice.h" +#include +#include +#include #include +#include +#include +#include +#include -static struct option TLSFingerprintingOptions[] = -{ - {"interface", required_argument, nullptr, 'i'}, - {"input-file", required_argument, nullptr, 'r'}, - {"output-file", required_argument, nullptr, 'o'}, - {"separator", required_argument, nullptr, 's'}, - {"tls-fp-type", required_argument, nullptr, 't'}, - {"filter", required_argument, nullptr, 'f'}, - {"list-interfaces", no_argument, nullptr, 'l'}, - {"help", no_argument, nullptr, 'h'}, - {"version", no_argument, nullptr, 'v'}, - {nullptr, 0, nullptr, 0} -}; - -#define EXIT_WITH_ERROR(reason) do { \ - printUsage(); \ - std::cout << std::endl << "ERROR: " << reason << std::endl << std::endl; \ - exit(1); \ - } while(0) - -#define TLS_FP_CH_ONLY "ch" -#define TLS_FP_SH_ONLY "sh" -#define TLS_FP_CH_AND_SH "ch_sh" - -bool isNotAlphanumeric(char c) -{ - return std::isalnum(c) == 0; -} - +static struct option TLSFingerprintingOptions[] = { + {"interface", required_argument, nullptr, 'i'}, + {"input-file", required_argument, nullptr, 'r'}, + {"output-file", required_argument, nullptr, 'o'}, + {"separator", required_argument, nullptr, 's'}, + {"tls-fp-type", required_argument, nullptr, 't'}, + {"filter", required_argument, nullptr, 'f'}, + {"list-interfaces", no_argument, nullptr, 'l'}, + {"help", no_argument, nullptr, 'h'}, + {"version", no_argument, nullptr, 'v'}, + {nullptr, 0, nullptr, 0}}; + +#define EXIT_WITH_ERROR(reason) \ + do { \ + printUsage(); \ + std::cout << std::endl \ + << "ERROR: " << reason << std::endl \ + << std::endl; \ + exit(1); \ + } while (0) + +#define TLS_FP_CH_ONLY "ch" +#define TLS_FP_SH_ONLY "sh" +#define TLS_FP_CH_AND_SH "ch_sh" + +bool isNotAlphanumeric(char c) { return std::isalnum(c) == 0; } /** - * An auxiliary method for sorting the TLS fingerprint count map. Used in printCommonTLSFingerprints() + * An auxiliary method for sorting the TLS fingerprint count map. Used in + * printCommonTLSFingerprints() */ -bool stringCountComparer(const std::pair& first, const std::pair& second) -{ - if (first.second == second.second) - { - return first.first > second.first; - } - return first.second > second.second; +bool stringCountComparer(const std::pair& first, + const std::pair& second) { + if (first.second == second.second) { + return first.first > second.first; + } + return first.second > second.second; } - /** * Print application usage */ -void printUsage() -{ - std::cout << std::endl - << "Usage:" << std::endl - << "------" << std::endl - << pcpp::AppName::get() << " [-hvlcms] [-r input_file] [-i interface] [-o output_file_name] [-s separator] [-t tls_fp_type] [-f bpf_filter]" << std::endl - << std::endl - << "Options:" << std::endl - << std::endl - << " -r input_file : Input pcap/pcapng file to analyze. Required argument for reading from file" << std::endl - << " -i interface : Use the specified interface. Can be interface name (e.g eth0) or IP address." << std::endl - << " Required argument for capturing from live interface" << std::endl - << " -o output_file_name : Output file name. This is a csv file (where 'tab' is the default separator)" << std::endl - << " which contains information about all of the TLS fingerprints found in the" << std::endl - << " capture file or live interface. It includes the TLS fingerprint itself" << std::endl - << " (raw string and MD5), IP addresses, TCP ports and SSL message type (ClientHello" << std::endl - << " or ServerHello). If this argument is not specified the output file name is the" << std::endl - << " name of capture file or the live interface and it is written to the current" << std::endl - << " directory ('.')" << std::endl - << " -s separator : The separator to use in the csv output file. Valid values are a single character" << std::endl - << " which is not alphanumeric and not one of the following: '.', ',', ':', '-'." << std::endl - << " If this argument is not specified the default separator is 'tab' ('\\t')" << std::endl - << " -t tls_fp_type : Specify whether to calculate TLS fingerprints for ClientHello packets only ('ch')," << std::endl - << " ServerHello packets only ('sh') or both ('ch_sh'). The only valid values are" << std::endl - << " 'ch', 'sh', 'ch_sh'. If this argument is not specified the default value is" << std::endl - << " ClientHello ('ch')" << std::endl - << " -f bpf_filter : Apply a BPF filter to the capture file or live interface, meaning TLS fingerprint" << std::endl - << " will only be generated for the filtered packets" << std::endl - << " -l : Print the list of interfaces and exit" << std::endl - << " -v : Display the current version and exit" << std::endl - << " -h : Display this help message and exit" << std::endl - << std::endl; +void printUsage() { + std::cout + << std::endl + << "Usage:" << std::endl + << "------" << std::endl + << pcpp::AppName::get() + << " [-hvlcms] [-r input_file] [-i interface] [-o output_file_name] [-s " + "separator] [-t tls_fp_type] [-f bpf_filter]" + << std::endl + << std::endl + << "Options:" << std::endl + << std::endl + << " -r input_file : Input pcap/pcapng file to analyze. " + "Required argument for reading from file" + << std::endl + << " -i interface : Use the specified interface. Can be " + "interface name (e.g eth0) or IP address." + << std::endl + << " Required argument for capturing from live " + "interface" + << std::endl + << " -o output_file_name : Output file name. This is a csv file " + "(where 'tab' is the default separator)" + << std::endl + << " which contains information about all of " + "the TLS fingerprints found in the" + << std::endl + << " capture file or live interface. It " + "includes the TLS fingerprint itself" + << std::endl + << " (raw string and MD5), IP addresses, TCP " + "ports and SSL message type (ClientHello" + << std::endl + << " or ServerHello). If this argument is not " + "specified the output file name is the" + << std::endl + << " name of capture file or the live interface " + "and it is written to the current" + << std::endl + << " directory ('.')" << std::endl + << " -s separator : The separator to use in the csv output " + "file. Valid values are a single character" + << std::endl + << " which is not alphanumeric and not one of " + "the following: '.', ',', ':', '-'." + << std::endl + << " If this argument is not specified the " + "default separator is 'tab' ('\\t')" + << std::endl + << " -t tls_fp_type : Specify whether to calculate TLS " + "fingerprints for ClientHello packets only ('ch')," + << std::endl + << " ServerHello packets only ('sh') or both " + "('ch_sh'). The only valid values are" + << std::endl + << " 'ch', 'sh', 'ch_sh'. If this argument is " + "not specified the default value is" + << std::endl + << " ClientHello ('ch')" << std::endl + << " -f bpf_filter : Apply a BPF filter to the capture file or " + "live interface, meaning TLS fingerprint" + << std::endl + << " will only be generated for the filtered " + "packets" + << std::endl + << " -l : Print the list of interfaces and exit" + << std::endl + << " -v : Display the current version and exit" + << std::endl + << " -h : Display this help message and exit" + << std::endl + << std::endl; } - /** * Print application version */ -void printAppVersion() -{ - std::cout - << pcpp::AppName::get() << " " << pcpp::getPcapPlusPlusVersionFull() << std::endl - << "Built: " << pcpp::getBuildDateTime() << std::endl - << "Built from: " << pcpp::getGitInfo() << std::endl; - exit(0); +void printAppVersion() { + std::cout << pcpp::AppName::get() << " " << pcpp::getPcapPlusPlusVersionFull() + << std::endl + << "Built: " << pcpp::getBuildDateTime() << std::endl + << "Built from: " << pcpp::getGitInfo() << std::endl; + exit(0); } - /** * Go over all interfaces and output their names */ -void listInterfaces() -{ - const std::vector& devList = pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDevicesList(); - - std::cout << std::endl << "Network interfaces:" << std::endl; - for (std::vector::const_iterator iter = devList.begin(); iter != devList.end(); iter++) - { - std::cout << " -> Name: '" << (*iter)->getName() << "' IP address: " << (*iter)->getIPv4Address().toString() << std::endl; - } - exit(0); +void listInterfaces() { + const std::vector& devList = + pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDevicesList(); + + std::cout << std::endl + << "Network interfaces:" << std::endl; + for (std::vector::const_iterator iter = + devList.begin(); + iter != devList.end(); iter++) { + std::cout << " -> Name: '" << (*iter)->getName() + << "' IP address: " << (*iter)->getIPv4Address().toString() + << std::endl; + } + exit(0); } - /** * The callback to be called when application is terminated by ctrl-c */ -static void onApplicationInterrupted(void* cookie) -{ - bool* shouldStop = (bool*)cookie; - *shouldStop = true; +static void onApplicationInterrupted(void* cookie) { + bool* shouldStop = (bool*)cookie; + *shouldStop = true; } - /** * Return a packet source and dest IP addresses */ -std::pair getIPs(const pcpp::Packet& packet) -{ - pcpp::IPAddress srcIP, dstIP; - if (packet.isPacketOfType(pcpp::IP)) - { - const pcpp::IPLayer* ipLayer = packet.getLayerOfType(); - srcIP = ipLayer->getSrcIPAddress(); - dstIP = ipLayer->getDstIPAddress(); - } - return std::pair(srcIP, dstIP); +std::pair getIPs(const pcpp::Packet& packet) { + pcpp::IPAddress srcIP, dstIP; + if (packet.isPacketOfType(pcpp::IP)) { + const pcpp::IPLayer* ipLayer = packet.getLayerOfType(); + srcIP = ipLayer->getSrcIPAddress(); + dstIP = ipLayer->getDstIPAddress(); + } + return std::pair(srcIP, dstIP); } - /** * Return a packet source and dest TCP ports */ -std::pair getTcpPorts(const pcpp::Packet& packet) -{ - uint16_t srcPort = 0, dstPort = 0; - if (packet.isPacketOfType(pcpp::TCP)) - { - pcpp::TcpLayer* tcpLayer = packet.getLayerOfType(); - srcPort = tcpLayer->getSrcPort(); - dstPort = tcpLayer->getDstPort(); - } - - return std::pair(srcPort, dstPort); +std::pair getTcpPorts(const pcpp::Packet& packet) { + uint16_t srcPort = 0, dstPort = 0; + if (packet.isPacketOfType(pcpp::TCP)) { + pcpp::TcpLayer* tcpLayer = packet.getLayerOfType(); + srcPort = tcpLayer->getSrcPort(); + dstPort = tcpLayer->getDstPort(); + } + + return std::pair(srcPort, dstPort); } - /** * Write data about a single ClientHello/ServerHello packet to the output file. - * This method takes the parsed packets and the TLS fingerprint as inputs, extracts the rest of the data such as IP addresses and TCP ports, - * and writes a single row to the output file + * This method takes the parsed packets and the TLS fingerprint as inputs, + * extracts the rest of the data such as IP addresses and TCP ports, and writes + * a single row to the output file */ -void writeToOutputFile(std::ofstream* outputFile, const pcpp::Packet& parsedPacket, const std::string &tlsFPString, const std::string &tlsFP_MD5, const std::string &tlsFPType, const std::string &separator) -{ - std::pair ipSrcDest = getIPs(parsedPacket); - std::pair tcpPorts = getTcpPorts(parsedPacket); - - *outputFile << - tlsFP_MD5 << separator << - tlsFPString << separator << - tlsFPType << separator << - ipSrcDest.first.toString() << separator << - tcpPorts.first << separator << - ipSrcDest.second.toString() << separator << - tcpPorts.second << std::endl; +void writeToOutputFile(std::ofstream* outputFile, + const pcpp::Packet& parsedPacket, + const std::string& tlsFPString, + const std::string& tlsFP_MD5, + const std::string& tlsFPType, + const std::string& separator) { + std::pair ipSrcDest = getIPs(parsedPacket); + std::pair tcpPorts = getTcpPorts(parsedPacket); + + *outputFile << tlsFP_MD5 << separator << tlsFPString << separator << tlsFPType + << separator << ipSrcDest.first.toString() << separator + << tcpPorts.first << separator << ipSrcDest.second.toString() + << separator << tcpPorts.second << std::endl; } - /** * Write the column headers to the output file */ -void writeHeaderToOutputFile(std::ofstream& outputFile, const std::string &separator) -{ - outputFile << - "TLS Fingerprint (MD5)" << separator << - "TLS Fingerprint" << separator << - "TLS Fingerprint type" << separator << - "IP Source" << separator << - "TCP Source Port" << separator << - "IP Dest" << separator << - "TCP Dest Port" << std::endl; +void writeHeaderToOutputFile(std::ofstream& outputFile, + const std::string& separator) { + outputFile << "TLS Fingerprint (MD5)" << separator << "TLS Fingerprint" + << separator << "TLS Fingerprint type" << separator << "IP Source" + << separator << "TCP Source Port" << separator << "IP Dest" + << separator << "TCP Dest Port" << std::endl; } - -struct TLSFingerprintingStats -{ - TLSFingerprintingStats(): numOfPacketsTotal(0), numOfCHPackets(0), numOfSHPackets(0) { } - uint64_t numOfPacketsTotal; - uint64_t numOfCHPackets; - uint64_t numOfSHPackets; - std::map chFingerprints; - std::map shFingerprints; +struct TLSFingerprintingStats { + TLSFingerprintingStats() + : numOfPacketsTotal(0), numOfCHPackets(0), numOfSHPackets(0) {} + uint64_t numOfPacketsTotal; + uint64_t numOfCHPackets; + uint64_t numOfSHPackets; + std::map chFingerprints; + std::map shFingerprints; }; -struct HandlePacketData -{ - bool chFP; - bool shFP; - std::ofstream* outputFile; - std::string separator; - TLSFingerprintingStats* stats; +struct HandlePacketData { + bool chFP; + bool shFP; + std::ofstream* outputFile; + std::string separator; + TLSFingerprintingStats* stats; }; - /** - * Print cipher-suite map in a table sorted by number of occurrences (most common cipher-suite will be first) + * Print cipher-suite map in a table sorted by number of occurrences (most + * common cipher-suite will be first) */ -void printCommonTLSFingerprints(const std::map& tlsFingerprintMap, int printCountItems) -{ - // create the table - std::vector columnNames; - columnNames.push_back("TLS Fingerprint"); - columnNames.push_back("Count"); - std::vector columnsWidths; - columnsWidths.push_back(32); - columnsWidths.push_back(7); - pcpp::TablePrinter printer(columnNames, columnsWidths); - - // sort the TLS fingerprint map so the most popular will be first - // since it's not possible to sort a std::map you must copy it to a std::vector and sort it then - std::vector > map2vec(tlsFingerprintMap.begin(), tlsFingerprintMap.end()); - std::sort(map2vec.begin(),map2vec.end(), &stringCountComparer); - - // go over all items (fingerprints + count) in the sorted vector and print them - for(std::vector >::iterator iter = map2vec.begin(); - iter != map2vec.end(); - iter++) - { - if (iter - map2vec.begin() >= printCountItems) - break; - - std::stringstream values; - values << iter->first << "|" << iter->second; - printer.printRow(values.str(), '|'); - } +void printCommonTLSFingerprints( + const std::map& tlsFingerprintMap, + int printCountItems) { + // create the table + std::vector columnNames; + columnNames.push_back("TLS Fingerprint"); + columnNames.push_back("Count"); + std::vector columnsWidths; + columnsWidths.push_back(32); + columnsWidths.push_back(7); + pcpp::TablePrinter printer(columnNames, columnsWidths); + + // sort the TLS fingerprint map so the most popular will be first + // since it's not possible to sort a std::map you must copy it to a + // std::vector and sort it then + std::vector> map2vec(tlsFingerprintMap.begin(), + tlsFingerprintMap.end()); + std::sort(map2vec.begin(), map2vec.end(), &stringCountComparer); + + // go over all items (fingerprints + count) in the sorted vector and print + // them + for (std::vector>::iterator iter = + map2vec.begin(); + iter != map2vec.end(); iter++) { + if (iter - map2vec.begin() >= printCountItems) + break; + + std::stringstream values; + values << iter->first << "|" << iter->second; + printer.printRow(values.str(), '|'); + } } - /** * Print TLS fingerprinting stats */ -void printStats(const TLSFingerprintingStats& stats, bool chFP, bool shFP) -{ - std::stringstream stream; - stream << std::endl; - stream << "Summary:" << std::endl; - stream << "========" << std::endl; - stream << "Total packets read: " << stats.numOfPacketsTotal << std::endl; - if (chFP) - { - stream << "TLS ClientHello packets: " << stats.numOfCHPackets << std::endl; - stream << "Unique ClientHello TLS fingerprints: " << stats.chFingerprints.size() << std::endl; - } - if (shFP) - { - stream << "TLS ServerHello packets: " << stats.numOfSHPackets << std::endl; - stream << "Unique ServerHello TLS fingerprints: " << stats.shFingerprints.size() << std::endl; - } - - std::cout << stream.str() << std::endl; - - // write a table of the 10 most common TLS fingerprints - - // if user requested to extract ClientHello TLS fingerprints and there is data to show - if (chFP && stats.chFingerprints.size() > 0) - { - if (stats.chFingerprints.size() > 10) - std::cout << "Top 10 "; - std::cout << "ClientHello TLS fingerprints:" << std::endl; - - // write no more than 10 most common TLS fingerprints - printCommonTLSFingerprints(stats.chFingerprints, 10); - std::cout << std::endl; - } - - // if user requested to extract ServerHello TLS fingerprints and there is data to show - if (shFP && stats.shFingerprints.size() > 0) - { - if (stats.shFingerprints.size() > 10) - std::cout << "Top 10 "; - std::cout << "ServerHello TLS fingerprints:" << std::endl; - - // write no more than 10 most common TLS fingerprints - printCommonTLSFingerprints(stats.shFingerprints, 10); - std::cout << std::endl; - } +void printStats(const TLSFingerprintingStats& stats, bool chFP, bool shFP) { + std::stringstream stream; + stream << std::endl; + stream << "Summary:" << std::endl; + stream << "========" << std::endl; + stream << "Total packets read: " << stats.numOfPacketsTotal + << std::endl; + if (chFP) { + stream << "TLS ClientHello packets: " << stats.numOfCHPackets + << std::endl; + stream << "Unique ClientHello TLS fingerprints: " + << stats.chFingerprints.size() << std::endl; + } + if (shFP) { + stream << "TLS ServerHello packets: " << stats.numOfSHPackets + << std::endl; + stream << "Unique ServerHello TLS fingerprints: " + << stats.shFingerprints.size() << std::endl; + } + + std::cout << stream.str() << std::endl; + + // write a table of the 10 most common TLS fingerprints + + // if user requested to extract ClientHello TLS fingerprints and there is data + // to show + if (chFP && stats.chFingerprints.size() > 0) { + if (stats.chFingerprints.size() > 10) + std::cout << "Top 10 "; + std::cout << "ClientHello TLS fingerprints:" << std::endl; + + // write no more than 10 most common TLS fingerprints + printCommonTLSFingerprints(stats.chFingerprints, 10); + std::cout << std::endl; + } + + // if user requested to extract ServerHello TLS fingerprints and there is data + // to show + if (shFP && stats.shFingerprints.size() > 0) { + if (stats.shFingerprints.size() > 10) + std::cout << "Top 10 "; + std::cout << "ServerHello TLS fingerprints:" << std::endl; + + // write no more than 10 most common TLS fingerprints + printCommonTLSFingerprints(stats.shFingerprints, 10); + std::cout << std::endl; + } } - /** - * Handle an intercepted packet: identify if it's a ClientHello or ServerHello packets, extract the TLS fingerprint and write it to the output file + * Handle an intercepted packet: identify if it's a ClientHello or ServerHello + * packets, extract the TLS fingerprint and write it to the output file */ -void handlePacket(pcpp::RawPacket* rawPacket, const HandlePacketData* data) -{ - pcpp::Packet parsedPacket(rawPacket); - data->stats->numOfPacketsTotal++; - if (parsedPacket.isPacketOfType(pcpp::SSL)) - { - // extract the SSL/TLS handhsake layer - pcpp::SSLHandshakeLayer* sslHandshakeLayer = parsedPacket.getLayerOfType(); - if (sslHandshakeLayer != nullptr) - { - // if user requested to extract ClientHello TLS fingerprint - if (data->chFP) - { - // check if the SSL/TLS handhsake layer contains a ClientHello message - pcpp::SSLClientHelloMessage* clientHelloMessage = sslHandshakeLayer->getHandshakeMessageOfType(); - if (clientHelloMessage != nullptr) - { - data->stats->numOfCHPackets++; - - // extract the TLS fingerprint - pcpp::SSLClientHelloMessage::ClientHelloTLSFingerprint tlsFingerprint = clientHelloMessage->generateTLSFingerprint(); - std::pair tlsFingerprintStringAndMD5 = tlsFingerprint.toStringAndMD5(); - data->stats->chFingerprints[tlsFingerprintStringAndMD5.second]++; - // write data to output file - writeToOutputFile(data->outputFile, parsedPacket, tlsFingerprintStringAndMD5.first, tlsFingerprintStringAndMD5.second, "ClientHello", data->separator); - return; - } - } - // if user requested to extract ServerHello TLS fingerprint - if (data->shFP) - { - // check if the SSL/TLS handhsake layer contains a ServerHello message - pcpp::SSLServerHelloMessage* servertHelloMessage = sslHandshakeLayer->getHandshakeMessageOfType(); - if (servertHelloMessage != nullptr) - { - data->stats->numOfSHPackets++; - - // extract the TLS fingerprint - pcpp::SSLServerHelloMessage::ServerHelloTLSFingerprint tlsFingerprint = servertHelloMessage->generateTLSFingerprint(); - std::pair tlsFingerprintStringAndMD5 = tlsFingerprint.toStringAndMD5(); - data->stats->shFingerprints[tlsFingerprintStringAndMD5.second]++; - // write data to output file - writeToOutputFile(data->outputFile, parsedPacket, tlsFingerprintStringAndMD5.first, tlsFingerprintStringAndMD5.second, "ServerHello", data->separator); - } - } - } - } +void handlePacket(pcpp::RawPacket* rawPacket, const HandlePacketData* data) { + pcpp::Packet parsedPacket(rawPacket); + data->stats->numOfPacketsTotal++; + if (parsedPacket.isPacketOfType(pcpp::SSL)) { + // extract the SSL/TLS handhsake layer + pcpp::SSLHandshakeLayer* sslHandshakeLayer = + parsedPacket.getLayerOfType(); + if (sslHandshakeLayer != nullptr) { + // if user requested to extract ClientHello TLS fingerprint + if (data->chFP) { + // check if the SSL/TLS handhsake layer contains a ClientHello message + pcpp::SSLClientHelloMessage* clientHelloMessage = + sslHandshakeLayer + ->getHandshakeMessageOfType(); + if (clientHelloMessage != nullptr) { + data->stats->numOfCHPackets++; + + // extract the TLS fingerprint + pcpp::SSLClientHelloMessage::ClientHelloTLSFingerprint + tlsFingerprint = clientHelloMessage->generateTLSFingerprint(); + std::pair tlsFingerprintStringAndMD5 = + tlsFingerprint.toStringAndMD5(); + data->stats->chFingerprints[tlsFingerprintStringAndMD5.second]++; + // write data to output file + writeToOutputFile(data->outputFile, parsedPacket, + tlsFingerprintStringAndMD5.first, + tlsFingerprintStringAndMD5.second, "ClientHello", + data->separator); + return; + } + } + // if user requested to extract ServerHello TLS fingerprint + if (data->shFP) { + // check if the SSL/TLS handhsake layer contains a ServerHello message + pcpp::SSLServerHelloMessage* servertHelloMessage = + sslHandshakeLayer + ->getHandshakeMessageOfType(); + if (servertHelloMessage != nullptr) { + data->stats->numOfSHPackets++; + + // extract the TLS fingerprint + pcpp::SSLServerHelloMessage::ServerHelloTLSFingerprint + tlsFingerprint = servertHelloMessage->generateTLSFingerprint(); + std::pair tlsFingerprintStringAndMD5 = + tlsFingerprint.toStringAndMD5(); + data->stats->shFingerprints[tlsFingerprintStringAndMD5.second]++; + // write data to output file + writeToOutputFile(data->outputFile, parsedPacket, + tlsFingerprintStringAndMD5.first, + tlsFingerprintStringAndMD5.second, "ServerHello", + data->separator); + } + } + } + } } - /** * Extract TLS fingerprints from a pcap/pcapng file */ -void doTlsFingerprintingOnPcapFile(const std::string& inputPcapFileName, std::string& outputFileName, const std::string& separator, bool chFP, bool shFP, const std::string& bpfFilter) -{ - // open input file (pcap or pcapng file) - pcpp::IFileReaderDevice* reader = pcpp::IFileReaderDevice::getReader(inputPcapFileName.c_str()); - - // try to open the file device - if (!reader->open()) - EXIT_WITH_ERROR("Cannot open pcap/pcapng file"); - - // set output file name to input file name if not provided by the user - if (outputFileName.empty()) - { - size_t fileNameOffset = inputPcapFileName.find_last_of("\\/") + 1; - size_t extensionOffset = inputPcapFileName.find_last_of("."); - std::string fileNameWithoutExtension = inputPcapFileName.substr(fileNameOffset, extensionOffset - fileNameOffset); - outputFileName = fileNameWithoutExtension + ".txt"; - } - - // open output file - std::ofstream outputFile(outputFileName.c_str()); - if (!outputFile) - { - EXIT_WITH_ERROR("Cannot open output file '" << outputFileName << "'"); - } - - // write the column headers to the output file - writeHeaderToOutputFile(outputFile, separator); - - // set BPF filter if provided by the user - if (!bpfFilter.empty()) - { - if (!reader->setFilter(bpfFilter)) - EXIT_WITH_ERROR("Error in setting BPF filter to the pcap file"); - } - - std::cout << "Start reading '" << inputPcapFileName << "'..." << std::endl; - - - TLSFingerprintingStats stats; - HandlePacketData data; - data.chFP = chFP; - data.shFP = shFP; - data.outputFile = &outputFile; - data.separator = separator; - data.stats = &stats; - - pcpp::RawPacket rawPacket; - - // iterate over all packets in the file - while (reader->getNextPacket(rawPacket)) - { - handlePacket(&rawPacket, &data); - } - - // close the reader and free its memory - reader->close(); - delete reader; - - printStats(stats, chFP, shFP); - - std::cout << "Output file was written to: '" << outputFileName << "'" << std::endl; +void doTlsFingerprintingOnPcapFile(const std::string& inputPcapFileName, + std::string& outputFileName, + const std::string& separator, bool chFP, + bool shFP, const std::string& bpfFilter) { + // open input file (pcap or pcapng file) + pcpp::IFileReaderDevice* reader = + pcpp::IFileReaderDevice::getReader(inputPcapFileName.c_str()); + + // try to open the file device + if (!reader->open()) + EXIT_WITH_ERROR("Cannot open pcap/pcapng file"); + + // set output file name to input file name if not provided by the user + if (outputFileName.empty()) { + size_t fileNameOffset = inputPcapFileName.find_last_of("\\/") + 1; + size_t extensionOffset = inputPcapFileName.find_last_of("."); + std::string fileNameWithoutExtension = inputPcapFileName.substr( + fileNameOffset, extensionOffset - fileNameOffset); + outputFileName = fileNameWithoutExtension + ".txt"; + } + + // open output file + std::ofstream outputFile(outputFileName.c_str()); + if (!outputFile) { + EXIT_WITH_ERROR("Cannot open output file '" << outputFileName << "'"); + } + + // write the column headers to the output file + writeHeaderToOutputFile(outputFile, separator); + + // set BPF filter if provided by the user + if (!bpfFilter.empty()) { + if (!reader->setFilter(bpfFilter)) + EXIT_WITH_ERROR("Error in setting BPF filter to the pcap file"); + } + + std::cout << "Start reading '" << inputPcapFileName << "'..." << std::endl; + + TLSFingerprintingStats stats; + HandlePacketData data; + data.chFP = chFP; + data.shFP = shFP; + data.outputFile = &outputFile; + data.separator = separator; + data.stats = &stats; + + pcpp::RawPacket rawPacket; + + // iterate over all packets in the file + while (reader->getNextPacket(rawPacket)) { + handlePacket(&rawPacket, &data); + } + + // close the reader and free its memory + reader->close(); + delete reader; + + printStats(stats, chFP, shFP); + + std::cout << "Output file was written to: '" << outputFileName << "'" + << std::endl; } - /** - * packet capture callback - called whenever a packet arrives on the live interface (in live device capturing mode) + * packet capture callback - called whenever a packet arrives on the live + * interface (in live device capturing mode) */ -static void onPacketArrives(pcpp::RawPacket* rawPacket, pcpp::PcapLiveDevice* dev, void* cookie) -{ - HandlePacketData* data = static_cast(cookie); - handlePacket(rawPacket, data); +static void onPacketArrives(pcpp::RawPacket* rawPacket, + pcpp::PcapLiveDevice* dev, void* cookie) { + HandlePacketData* data = static_cast(cookie); + handlePacket(rawPacket, data); } - /** * Extract TLS fingerprints from a live interface */ -void doTlsFingerprintingOnLiveTraffic(const std::string& interfaceNameOrIP, std::string& outputFileName, const std::string& separator, bool chFP, bool shFP, const std::string& bpfFilter) -{ - // extract pcap live device by interface name or IP address - pcpp::PcapLiveDevice* dev = pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDeviceByIpOrName(interfaceNameOrIP); - if (dev == nullptr) - EXIT_WITH_ERROR("Couldn't find interface by given IP address or name"); - - if (!dev->open()) - EXIT_WITH_ERROR("Couldn't open interface"); - - // set output file name to interface name if not provided by the user - if (outputFileName.empty()) - { - // take the device name and remove all chars which are not alphanumeric - outputFileName = std::string(dev->getName()); - outputFileName.erase(remove_if( - outputFileName.begin(), - outputFileName.end(), - isNotAlphanumeric), outputFileName.end()); - - outputFileName += ".txt"; - } - - // open output file - std::ofstream outputFile(outputFileName.c_str()); - if (!outputFile) - { - EXIT_WITH_ERROR("Cannot open output file '" << outputFileName << "'"); - } - - // write the column headers to the output file - writeHeaderToOutputFile(outputFile, separator); - - // set BPF filter if provided by the user - if (!bpfFilter.empty()) - { - if (!dev->setFilter(bpfFilter)) - EXIT_WITH_ERROR("Error in setting BPF filter to interface"); - } - - std::cout << "Start capturing packets from '" << interfaceNameOrIP << "'..." << std::endl; - - TLSFingerprintingStats stats; - HandlePacketData data; - data.chFP = chFP; - data.shFP = shFP; - data.outputFile = &outputFile; - data.separator = separator; - data.stats = &stats; - - // start capturing packets. Each packet arrived will be handled by the onPacketArrives method - dev->startCapture(onPacketArrives, &data); - - // register the on app close event to print summary stats on app termination - bool shouldStop = false; - pcpp::ApplicationEventHandler::getInstance().onApplicationInterrupted(onApplicationInterrupted, &shouldStop); - - // run in an endless loop until the user press ctrl+c - while(!shouldStop) - pcpp::multiPlatformSleep(1); - - // stop capturing and close the live device - dev->stopCapture(); - dev->close(); - - printStats(stats, chFP, shFP); - - std::cout << "Output file was written to: '" << outputFileName << "'" << std::endl; +void doTlsFingerprintingOnLiveTraffic(const std::string& interfaceNameOrIP, + std::string& outputFileName, + const std::string& separator, bool chFP, + bool shFP, const std::string& bpfFilter) { + // extract pcap live device by interface name or IP address + pcpp::PcapLiveDevice* dev = + pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDeviceByIpOrName( + interfaceNameOrIP); + if (dev == nullptr) + EXIT_WITH_ERROR("Couldn't find interface by given IP address or name"); + + if (!dev->open()) + EXIT_WITH_ERROR("Couldn't open interface"); + + // set output file name to interface name if not provided by the user + if (outputFileName.empty()) { + // take the device name and remove all chars which are not alphanumeric + outputFileName = std::string(dev->getName()); + outputFileName.erase(remove_if(outputFileName.begin(), outputFileName.end(), + isNotAlphanumeric), + outputFileName.end()); + + outputFileName += ".txt"; + } + + // open output file + std::ofstream outputFile(outputFileName.c_str()); + if (!outputFile) { + EXIT_WITH_ERROR("Cannot open output file '" << outputFileName << "'"); + } + + // write the column headers to the output file + writeHeaderToOutputFile(outputFile, separator); + + // set BPF filter if provided by the user + if (!bpfFilter.empty()) { + if (!dev->setFilter(bpfFilter)) + EXIT_WITH_ERROR("Error in setting BPF filter to interface"); + } + + std::cout << "Start capturing packets from '" << interfaceNameOrIP << "'..." + << std::endl; + + TLSFingerprintingStats stats; + HandlePacketData data; + data.chFP = chFP; + data.shFP = shFP; + data.outputFile = &outputFile; + data.separator = separator; + data.stats = &stats; + + // start capturing packets. Each packet arrived will be handled by the + // onPacketArrives method + dev->startCapture(onPacketArrives, &data); + + // register the on app close event to print summary stats on app termination + bool shouldStop = false; + pcpp::ApplicationEventHandler::getInstance().onApplicationInterrupted( + onApplicationInterrupted, &shouldStop); + + // run in an endless loop until the user press ctrl+c + while (!shouldStop) + pcpp::multiPlatformSleep(1); + + // stop capturing and close the live device + dev->stopCapture(); + dev->close(); + + printStats(stats, chFP, shFP); + + std::cout << "Output file was written to: '" << outputFileName << "'" + << std::endl; } /** * main method of this utility */ -int main(int argc, char* argv[]) -{ - pcpp::AppName::init(argc, argv); - - std::string interfaceNameOrIP; - std::string inputPcapFileName; - std::string outputFileName; - std::string bpfFilter; - std::string separator = "\t"; - std::string tlsFingerprintType = TLS_FP_CH_ONLY; - - int optionIndex = 0; - int opt = 0; - - while((opt = getopt_long(argc, argv, "i:r:o:t:f:s:vhl", TLSFingerprintingOptions, &optionIndex)) != -1) - { - switch (opt) - { - case 0: - break; - case 'i': - interfaceNameOrIP = optarg; - break; - case 'r': - inputPcapFileName = optarg; - break; - case 'o': - outputFileName = optarg; - break; - case 'f': - bpfFilter = optarg; - break; - case 's': - separator = optarg; - break; - case 't': - tlsFingerprintType = optarg; - break; - case 'h': - printUsage(); - exit(0); - case 'v': - printAppVersion(); - break; - case 'l': - listInterfaces(); - break; - default: - printUsage(); - exit(-1); - } - } - - // if no interface or input pcap file provided or both are provided- exit with error - if (inputPcapFileName.empty() == interfaceNameOrIP.empty()) - { - EXIT_WITH_ERROR("Please provide an interface or an input pcap file"); - } - - // if the user chosen a separator which is not the default, check if this separator is allowed. - // allowed separators are a single character which is not alphanumeric and not one of the following: '.', ',', ':', '-' - static const std::string disallowedSeparatorsArr[] = { ".", ",", ":", "-" }; - std::vector disallowedSeparatorsVec(disallowedSeparatorsArr, disallowedSeparatorsArr + sizeof(disallowedSeparatorsArr) / sizeof(disallowedSeparatorsArr[0]) ); - if (separator.empty() || separator.size() > 1 || std::isalnum(separator[0]) || std::find(disallowedSeparatorsVec.begin(), disallowedSeparatorsVec.end(), separator) != disallowedSeparatorsVec.end()) - { - EXIT_WITH_ERROR("Allowed separators are single characters which are not alphanumeric and not ',', '.', ':', '-'"); - } - - // validate TLS fingerprint type the user has requested - if (tlsFingerprintType != TLS_FP_CH_ONLY && tlsFingerprintType != TLS_FP_SH_ONLY && tlsFingerprintType != TLS_FP_CH_AND_SH) - { - EXIT_WITH_ERROR("Possible options for TLS fingerprint types are 'ch' (Client Hello), 'sh' (Server Hello) or 'ch_sh' (Client Hello & Server Hello)\n"); - } - - bool chFP = true, shFP = true; - if (tlsFingerprintType == TLS_FP_CH_ONLY) - { - shFP = false; - } - else if (tlsFingerprintType == TLS_FP_SH_ONLY) - { - chFP = false; - } - - if (!inputPcapFileName.empty()) - { - doTlsFingerprintingOnPcapFile(inputPcapFileName, outputFileName, separator, chFP, shFP, bpfFilter); - } - else - { - doTlsFingerprintingOnLiveTraffic(interfaceNameOrIP, outputFileName, separator, chFP, shFP, bpfFilter); - } +int main(int argc, char* argv[]) { + pcpp::AppName::init(argc, argv); + + std::string interfaceNameOrIP; + std::string inputPcapFileName; + std::string outputFileName; + std::string bpfFilter; + std::string separator = "\t"; + std::string tlsFingerprintType = TLS_FP_CH_ONLY; + + int optionIndex = 0; + int opt = 0; + + while ((opt = getopt_long(argc, argv, "i:r:o:t:f:s:vhl", + TLSFingerprintingOptions, &optionIndex)) != -1) { + switch (opt) { + case 0: + break; + case 'i': + interfaceNameOrIP = optarg; + break; + case 'r': + inputPcapFileName = optarg; + break; + case 'o': + outputFileName = optarg; + break; + case 'f': + bpfFilter = optarg; + break; + case 's': + separator = optarg; + break; + case 't': + tlsFingerprintType = optarg; + break; + case 'h': + printUsage(); + exit(0); + case 'v': + printAppVersion(); + break; + case 'l': + listInterfaces(); + break; + default: + printUsage(); + exit(-1); + } + } + + // if no interface or input pcap file provided or both are provided- exit with + // error + if (inputPcapFileName.empty() == interfaceNameOrIP.empty()) { + EXIT_WITH_ERROR("Please provide an interface or an input pcap file"); + } + + // if the user chosen a separator which is not the default, check if this + // separator is allowed. allowed separators are a single character which is + // not alphanumeric and not one of the following: '.', ',', ':', '-' + static const std::string disallowedSeparatorsArr[] = {".", ",", ":", "-"}; + std::vector disallowedSeparatorsVec( + disallowedSeparatorsArr, + disallowedSeparatorsArr + + sizeof(disallowedSeparatorsArr) / sizeof(disallowedSeparatorsArr[0])); + if (separator.empty() || separator.size() > 1 || std::isalnum(separator[0]) || + std::find(disallowedSeparatorsVec.begin(), disallowedSeparatorsVec.end(), + separator) != disallowedSeparatorsVec.end()) { + EXIT_WITH_ERROR("Allowed separators are single characters which are not " + "alphanumeric and not ',', '.', ':', '-'"); + } + + // validate TLS fingerprint type the user has requested + if (tlsFingerprintType != TLS_FP_CH_ONLY && + tlsFingerprintType != TLS_FP_SH_ONLY && + tlsFingerprintType != TLS_FP_CH_AND_SH) { + EXIT_WITH_ERROR( + "Possible options for TLS fingerprint types are 'ch' (Client Hello), " + "'sh' (Server Hello) or 'ch_sh' (Client Hello & Server Hello)\n"); + } + + bool chFP = true, shFP = true; + if (tlsFingerprintType == TLS_FP_CH_ONLY) { + shFP = false; + } else if (tlsFingerprintType == TLS_FP_SH_ONLY) { + chFP = false; + } + + if (!inputPcapFileName.empty()) { + doTlsFingerprintingOnPcapFile(inputPcapFileName, outputFileName, separator, + chFP, shFP, bpfFilter); + } else { + doTlsFingerprintingOnLiveTraffic(interfaceNameOrIP, outputFileName, + separator, chFP, shFP, bpfFilter); + } } diff --git a/Examples/TcpReassembly/main.cpp b/Examples/TcpReassembly/main.cpp index d8c6e688b2..43867a9069 100644 --- a/Examples/TcpReassembly/main.cpp +++ b/Examples/TcpReassembly/main.cpp @@ -1,48 +1,54 @@ /** * TcpReassembly application * ========================= - * This is an application that captures data transmitted as part of TCP connections, organizes the data and stores it in a way that is convenient for protocol analysis and debugging. - * This application reconstructs the TCP data streams and stores each connection in a separate file(s). TcpReassembly understands TCP sequence numbers and will correctly reconstruct - * data streams regardless of retransmissions, out-of-order delivery or data loss. - * TcpReassembly works more or less the same like tcpflow (https://linux.die.net/man/1/tcpflow) but probably with less options. - * The main purpose of it is to demonstrate the TCP reassembly capabilities in PcapPlusPlus. - * Main features and capabilities: + * This is an application that captures data transmitted as part of TCP + * connections, organizes the data and stores it in a way that is convenient for + * protocol analysis and debugging. This application reconstructs the TCP data + * streams and stores each connection in a separate file(s). TcpReassembly + * understands TCP sequence numbers and will correctly reconstruct data streams + * regardless of retransmissions, out-of-order delivery or data loss. + * TcpReassembly works more or less the same like tcpflow + * (https://linux.die.net/man/1/tcpflow) but probably with less options. The + * main purpose of it is to demonstrate the TCP reassembly capabilities in + * PcapPlusPlus. Main features and capabilities: * - Captures packets from pcap/pcapng files or live traffic * - Handles TCP retransmission, out-of-order packets and packet loss * - Possibility to set a BPF filter to process only part of the traffic * - Write each connection to a separate file * - Write each side of each connection to a separate file - * - Limit the max number of open files in each point in time (to avoid running out of file descriptors for large files / heavy traffic) - * - Write a metadata file (txt file) for each connection with various stats on the connection: number of packets (in each side + total), number of TCP messages (in each side + total), - * number of bytes (in each side + total) + * - Limit the max number of open files in each point in time (to avoid + * running out of file descriptors for large files / heavy traffic) + * - Write a metadata file (txt file) for each connection with various stats + * on the connection: number of packets (in each side + total), number of TCP + * messages (in each side + total), number of bytes (in each side + total) * - Write to console only (instead of files) * - Set a directory to write files to (default is current directory) * * For more details about modes of operation and parameters run TcpReassembly -h */ - -#include -#include -#include -#include -#include -#include -#include "TcpReassembly.h" -#include "PcapLiveDeviceList.h" +#include "LRUList.h" #include "PcapFileDevice.h" -#include "SystemUtils.h" +#include "PcapLiveDeviceList.h" #include "PcapPlusPlusVersion.h" -#include "LRUList.h" +#include "SystemUtils.h" +#include "TcpReassembly.h" +#include +#include #include +#include +#include +#include +#include - -#define EXIT_WITH_ERROR(reason) do { \ - printUsage(); \ - std::cout << std::endl << "ERROR: " << reason << std::endl << std::endl; \ - exit(1); \ - } while(0) - +#define EXIT_WITH_ERROR(reason) \ + do { \ + printUsage(); \ + std::cout << std::endl \ + << "ERROR: " << reason << std::endl \ + << std::endl; \ + exit(1); \ + } while (0) #if defined(_WIN32) #define SEPARATOR '\\' @@ -50,633 +56,684 @@ #define SEPARATOR '/' #endif - -// unless the user chooses otherwise - default number of concurrent used file descriptors is 500 +// unless the user chooses otherwise - default number of concurrent used file +// descriptors is 500 constexpr int DEFAULT_MAX_NUMBER_OF_CONCURRENT_OPEN_FILES = 500; -static struct option TcpAssemblyOptions[] = -{ - {"interface", required_argument, nullptr, 'i'}, - {"input-file", required_argument, nullptr, 'r'}, - {"output-dir", required_argument, nullptr, 'o'}, - {"list-interfaces", no_argument, nullptr, 'l'}, - {"filter", required_argument, nullptr, 'e'}, - {"write-metadata", no_argument, nullptr, 'm'}, - {"write-to-console", no_argument, nullptr, 'c'}, - {"separate-sides", no_argument, nullptr, 's'}, - {"max-file-desc", required_argument, nullptr, 'f'}, - {"help", no_argument, nullptr, 'h'}, - {"version", no_argument, nullptr, 'v'}, - {nullptr, 0, nullptr, 0} -}; - +static struct option TcpAssemblyOptions[] = { + {"interface", required_argument, nullptr, 'i'}, + {"input-file", required_argument, nullptr, 'r'}, + {"output-dir", required_argument, nullptr, 'o'}, + {"list-interfaces", no_argument, nullptr, 'l'}, + {"filter", required_argument, nullptr, 'e'}, + {"write-metadata", no_argument, nullptr, 'm'}, + {"write-to-console", no_argument, nullptr, 'c'}, + {"separate-sides", no_argument, nullptr, 's'}, + {"max-file-desc", required_argument, nullptr, 'f'}, + {"help", no_argument, nullptr, 'h'}, + {"version", no_argument, nullptr, 'v'}, + {nullptr, 0, nullptr, 0}}; /** - * A singleton class containing the configuration as requested by the user. This singleton is used throughout the application + * A singleton class containing the configuration as requested by the user. This + * singleton is used throughout the application */ -class GlobalConfig -{ -private: - - /** - * A private c'tor (as this is a singleton) - */ - GlobalConfig() : m_RecentConnsWithActivity(nullptr), writeMetadata(false), writeToConsole(false), separateSides(false), maxOpenFiles(DEFAULT_MAX_NUMBER_OF_CONCURRENT_OPEN_FILES) { } - - // A least-recently-used (LRU) list of all connections seen so far. Each connection is represented by its flow key. This LRU list is used to decide which connection was seen least - // recently in case we reached max number of open file descriptors and we need to decide which files to close - pcpp::LRUList* m_RecentConnsWithActivity; - -public: - - // a flag indicating whether to write a metadata file for each connection (containing several stats) - bool writeMetadata; - - // the directory to write files to (default is current directory) - std::string outputDir; - - // a flag indicating whether to write TCP data to actual files or to console - bool writeToConsole; - - // a flag indicating whether to write both side of a connection to the same file (which is the default) or write each side to a separate file - bool separateSides; - - // max number of allowed open files in each point in time - size_t maxOpenFiles; - - - /** - * A method getting connection parameters as input and returns a filename and file path as output. - * The filename is constructed by the IPs (src and dst) and the TCP ports (src and dst) - */ - std::string getFileName(pcpp::ConnectionData connData, int side, bool useSeparateSides) const - { - std::stringstream stream; - - // if user chooses to write to a directory other than the current directory - add the dir path to the return value - if (!outputDir.empty()) - stream << outputDir << SEPARATOR; - - std::string sourceIP = connData.srcIP.toString(); - std::string destIP = connData.dstIP.toString(); - - // for IPv6 addresses, replace ':' with '_' - std::replace(sourceIP.begin(), sourceIP.end(), ':', '_'); - std::replace(destIP.begin(), destIP.end(), ':', '_'); - - // side == 0 means data is sent from client->server - if (side <= 0 || !useSeparateSides) - stream << sourceIP << '.' << connData.srcPort << '-' << destIP << '.' << connData.dstPort; - else // side == 1 means data is sent from server->client - stream << destIP << '.' << connData.dstPort << '-' << sourceIP << '.' << connData.srcPort; - - // return the file path - return stream.str(); - } - - - /** - * Open a file stream. Inputs are the filename to open and a flag indicating whether to append to an existing file or overwrite it. - * Return value is a pointer to the new file stream - */ - std::ostream* openFileStream(const std::string &fileName, bool reopen) const - { - // if the user chooses to write only to console, don't open anything and return std::cout - if (writeToConsole) - return &std::cout; - - // open the file on the disk (with append or overwrite mode) - if (reopen) - return new std::ofstream(fileName.c_str(), std::ios_base::binary | std::ios_base::app); - else - return new std::ofstream(fileName.c_str(), std::ios_base::binary); - } - - - /** - * Close a file stream - */ - void closeFileSteam(std::ostream* fileStream) const - { - // if the user chooses to write only to console - do nothing and return - if (!writeToConsole) - { - // close the file stream - auto fstream = (std::ofstream*)fileStream; - fstream->close(); - - // free the memory of the file stream - delete fstream; - } - } - - - /** - * Return a pointer to the least-recently-used (LRU) list of connections - */ - pcpp::LRUList* getRecentConnsWithActivity() - { - // This is a lazy implementation - the instance isn't created until the user requests it for the first time. - // the side of the LRU list is determined by the max number of allowed open files at any point in time. Default is DEFAULT_MAX_NUMBER_OF_CONCURRENT_OPEN_FILES - // but the user can choose another number - if (m_RecentConnsWithActivity == nullptr) - m_RecentConnsWithActivity = new pcpp::LRUList(maxOpenFiles); - - // return the pointer - return m_RecentConnsWithActivity; - } - - - /** - * The singleton implementation of this class - */ - static GlobalConfig& getInstance() - { - static GlobalConfig instance; - return instance; - } - - /** - * d'tor - */ - ~GlobalConfig() - { - delete m_RecentConnsWithActivity; - } +class GlobalConfig { + private: + /** + * A private c'tor (as this is a singleton) + */ + GlobalConfig() + : m_RecentConnsWithActivity(nullptr), writeMetadata(false), + writeToConsole(false), separateSides(false), + maxOpenFiles(DEFAULT_MAX_NUMBER_OF_CONCURRENT_OPEN_FILES) {} + + // A least-recently-used (LRU) list of all connections seen so far. Each + // connection is represented by its flow key. This LRU list is used to decide + // which connection was seen least recently in case we reached max number of + // open file descriptors and we need to decide which files to close + pcpp::LRUList* m_RecentConnsWithActivity; + + public: + // a flag indicating whether to write a metadata file for each connection + // (containing several stats) + bool writeMetadata; + + // the directory to write files to (default is current directory) + std::string outputDir; + + // a flag indicating whether to write TCP data to actual files or to console + bool writeToConsole; + + // a flag indicating whether to write both side of a connection to the same + // file (which is the default) or write each side to a separate file + bool separateSides; + + // max number of allowed open files in each point in time + size_t maxOpenFiles; + + /** + * A method getting connection parameters as input and returns a filename and + * file path as output. The filename is constructed by the IPs (src and dst) + * and the TCP ports (src and dst) + */ + std::string getFileName(pcpp::ConnectionData connData, int side, + bool useSeparateSides) const { + std::stringstream stream; + + // if user chooses to write to a directory other than the current directory + // - add the dir path to the return value + if (!outputDir.empty()) + stream << outputDir << SEPARATOR; + + std::string sourceIP = connData.srcIP.toString(); + std::string destIP = connData.dstIP.toString(); + + // for IPv6 addresses, replace ':' with '_' + std::replace(sourceIP.begin(), sourceIP.end(), ':', '_'); + std::replace(destIP.begin(), destIP.end(), ':', '_'); + + // side == 0 means data is sent from client->server + if (side <= 0 || !useSeparateSides) + stream << sourceIP << '.' << connData.srcPort << '-' << destIP << '.' + << connData.dstPort; + else // side == 1 means data is sent from server->client + stream << destIP << '.' << connData.dstPort << '-' << sourceIP << '.' + << connData.srcPort; + + // return the file path + return stream.str(); + } + + /** + * Open a file stream. Inputs are the filename to open and a flag indicating + * whether to append to an existing file or overwrite it. Return value is a + * pointer to the new file stream + */ + std::ostream* openFileStream(const std::string& fileName, bool reopen) const { + // if the user chooses to write only to console, don't open anything and + // return std::cout + if (writeToConsole) + return &std::cout; + + // open the file on the disk (with append or overwrite mode) + if (reopen) + return new std::ofstream(fileName.c_str(), + std::ios_base::binary | std::ios_base::app); + else + return new std::ofstream(fileName.c_str(), std::ios_base::binary); + } + + /** + * Close a file stream + */ + void closeFileSteam(std::ostream* fileStream) const { + // if the user chooses to write only to console - do nothing and return + if (!writeToConsole) { + // close the file stream + auto fstream = (std::ofstream*)fileStream; + fstream->close(); + + // free the memory of the file stream + delete fstream; + } + } + + /** + * Return a pointer to the least-recently-used (LRU) list of connections + */ + pcpp::LRUList* getRecentConnsWithActivity() { + // This is a lazy implementation - the instance isn't created until the user + // requests it for the first time. the side of the LRU list is determined by + // the max number of allowed open files at any point in time. Default is + // DEFAULT_MAX_NUMBER_OF_CONCURRENT_OPEN_FILES but the user can choose + // another number + if (m_RecentConnsWithActivity == nullptr) + m_RecentConnsWithActivity = new pcpp::LRUList(maxOpenFiles); + + // return the pointer + return m_RecentConnsWithActivity; + } + + /** + * The singleton implementation of this class + */ + static GlobalConfig& getInstance() { + static GlobalConfig instance; + return instance; + } + + /** + * d'tor + */ + ~GlobalConfig() { delete m_RecentConnsWithActivity; } }; - /** - * A struct to contain all data save on a specific connection. It contains the file streams to write to and also stats data on the connection + * A struct to contain all data save on a specific connection. It contains the + * file streams to write to and also stats data on the connection */ -struct TcpReassemblyData -{ - // pointer to 2 file stream - one for each side of the connection. If the user chooses to write both sides to the same file (which is the default), only one file stream is used (index 0) - std::ostream* fileStreams[2]; - - // flags indicating whether the file in each side was already opened before. If the answer is yes, next time it'll be opened in append mode (and not in overwrite mode) - bool reopenFileStreams[2]; - - // a flag indicating on which side was the latest message on this connection - int8_t curSide; - - // stats data: num of data packets on each side, bytes seen on each side and messages seen on each side - int numOfDataPackets[2]; - int numOfMessagesFromSide[2]; - int bytesFromSide[2]; - - /** - * the default c'tor - */ - TcpReassemblyData() { fileStreams[0] = nullptr; fileStreams[1] = nullptr; clear(); } - - /** - * The default d'tor - */ - ~TcpReassemblyData() - { - // close files on both sides if open - if (fileStreams[0] != nullptr) - GlobalConfig::getInstance().closeFileSteam(fileStreams[0]); - - if (fileStreams[1] != nullptr) - GlobalConfig::getInstance().closeFileSteam(fileStreams[1]); - } - - /** - * Clear all data (put 0, false or nullptr - whatever relevant for each field) - */ - void clear() - { - // for the file stream - close them if they're not null - if (fileStreams[0] != nullptr) - { - GlobalConfig::getInstance().closeFileSteam(fileStreams[0]); - fileStreams[0] = nullptr; - } - - if (fileStreams[1] != nullptr) - { - GlobalConfig::getInstance().closeFileSteam(fileStreams[1]); - fileStreams[1] = nullptr; - } - - reopenFileStreams[0] = false; - reopenFileStreams[1] = false; - numOfDataPackets[0] = 0; - numOfDataPackets[1] = 0; - numOfMessagesFromSide[0] = 0; - numOfMessagesFromSide[1] = 0; - bytesFromSide[0] = 0; - bytesFromSide[1] = 0; - curSide = -1; - } +struct TcpReassemblyData { + // pointer to 2 file stream - one for each side of the connection. If the user + // chooses to write both sides to the same file (which is the default), only + // one file stream is used (index 0) + std::ostream* fileStreams[2]; + + // flags indicating whether the file in each side was already opened before. + // If the answer is yes, next time it'll be opened in append mode (and not in + // overwrite mode) + bool reopenFileStreams[2]; + + // a flag indicating on which side was the latest message on this connection + int8_t curSide; + + // stats data: num of data packets on each side, bytes seen on each side and + // messages seen on each side + int numOfDataPackets[2]; + int numOfMessagesFromSide[2]; + int bytesFromSide[2]; + + /** + * the default c'tor + */ + TcpReassemblyData() { + fileStreams[0] = nullptr; + fileStreams[1] = nullptr; + clear(); + } + + /** + * The default d'tor + */ + ~TcpReassemblyData() { + // close files on both sides if open + if (fileStreams[0] != nullptr) + GlobalConfig::getInstance().closeFileSteam(fileStreams[0]); + + if (fileStreams[1] != nullptr) + GlobalConfig::getInstance().closeFileSteam(fileStreams[1]); + } + + /** + * Clear all data (put 0, false or nullptr - whatever relevant for each field) + */ + void clear() { + // for the file stream - close them if they're not null + if (fileStreams[0] != nullptr) { + GlobalConfig::getInstance().closeFileSteam(fileStreams[0]); + fileStreams[0] = nullptr; + } + + if (fileStreams[1] != nullptr) { + GlobalConfig::getInstance().closeFileSteam(fileStreams[1]); + fileStreams[1] = nullptr; + } + + reopenFileStreams[0] = false; + reopenFileStreams[1] = false; + numOfDataPackets[0] = 0; + numOfDataPackets[1] = 0; + numOfMessagesFromSide[0] = 0; + numOfMessagesFromSide[1] = 0; + bytesFromSide[0] = 0; + bytesFromSide[1] = 0; + curSide = -1; + } }; - // typedef representing the connection manager typedef std::unordered_map TcpReassemblyConnMgr; - /** * Print application usage */ -void printUsage() -{ - std::cout << std::endl - << "Usage:" << std::endl - << "------" << std::endl - << pcpp::AppName::get() << " [-hvlcms] [-r input_file] [-i interface] [-o output_dir] [-e bpf_filter] [-f max_files]" << std::endl - << std::endl - << "Options:" << std::endl - << std::endl - << " -r input_file : Input pcap/pcapng file to analyze. Required argument for reading from file" << std::endl - << " -i interface : Use the specified interface. Can be interface name (e.g eth0) or interface IPv4 address. Required argument for capturing from live interface" << std::endl - << " -o output_dir : Specify output directory (default is '.')" << std::endl - << " -e bpf_filter : Apply a BPF filter to capture file or live interface, meaning TCP reassembly will only work on filtered packets" << std::endl - << " -f max_files : Maximum number of file descriptors to use" << std::endl - << " -c : Write all output to console (nothing will be written to files)" << std::endl - << " -m : Write a metadata file for each connection" << std::endl - << " -s : Write each side of each connection to a separate file (default is writing both sides of each connection to the same file)" << std::endl - << " -l : Print the list of interfaces and exit" << std::endl - << " -v : Display the current version and exit" << std::endl - << " -h : Display this help message and exit" << std::endl - << std::endl; +void printUsage() { + std::cout + << std::endl + << "Usage:" << std::endl + << "------" << std::endl + << pcpp::AppName::get() + << " [-hvlcms] [-r input_file] [-i interface] [-o output_dir] [-e " + "bpf_filter] [-f max_files]" + << std::endl + << std::endl + << "Options:" << std::endl + << std::endl + << " -r input_file : Input pcap/pcapng file to analyze. Required " + "argument for reading from file" + << std::endl + << " -i interface : Use the specified interface. Can be interface " + "name (e.g eth0) or interface IPv4 address. Required argument for " + "capturing from live interface" + << std::endl + << " -o output_dir : Specify output directory (default is '.')" + << std::endl + << " -e bpf_filter : Apply a BPF filter to capture file or live " + "interface, meaning TCP reassembly will only work on filtered packets" + << std::endl + << " -f max_files : Maximum number of file descriptors to use" + << std::endl + << " -c : Write all output to console (nothing will be " + "written to files)" + << std::endl + << " -m : Write a metadata file for each connection" + << std::endl + << " -s : Write each side of each connection to a separate " + "file (default is writing both sides of each connection to the same " + "file)" + << std::endl + << " -l : Print the list of interfaces and exit" + << std::endl + << " -v : Display the current version and exit" << std::endl + << " -h : Display this help message and exit" << std::endl + << std::endl; } - /** * Print application version */ -void printAppVersion() -{ - std::cout - << pcpp::AppName::get() << " " << pcpp::getPcapPlusPlusVersionFull() << std::endl - << "Built: " << pcpp::getBuildDateTime() << std::endl - << "Built from: " << pcpp::getGitInfo() << std::endl; - exit(0); +void printAppVersion() { + std::cout << pcpp::AppName::get() << " " << pcpp::getPcapPlusPlusVersionFull() + << std::endl + << "Built: " << pcpp::getBuildDateTime() << std::endl + << "Built from: " << pcpp::getGitInfo() << std::endl; + exit(0); } - /** * Go over all interfaces and output their names */ -void listInterfaces() -{ - auto const& devList = pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDevicesList(); - - std::cout << std::endl << "Network interfaces:" << std::endl; - - for (auto dev : devList) - { - std::cout << " -> Name: '" << dev->getName() << "' IP address: " << dev->getIPv4Address().toString() << std::endl; - } - exit(0); +void listInterfaces() { + auto const& devList = + pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDevicesList(); + + std::cout << std::endl + << "Network interfaces:" << std::endl; + + for (auto dev : devList) { + std::cout << " -> Name: '" << dev->getName() + << "' IP address: " << dev->getIPv4Address().toString() + << std::endl; + } + exit(0); } - /** - * The callback being called by the TCP reassembly module whenever new data arrives on a certain connection + * The callback being called by the TCP reassembly module whenever new data + * arrives on a certain connection */ -static void tcpReassemblyMsgReadyCallback(const int8_t sideIndex, const pcpp::TcpStreamData& tcpData, void* userCookie) -{ - // extract the connection manager from the user cookie - auto connMgr = (TcpReassemblyConnMgr*)userCookie; - - // check if this flow already appears in the connection manager. If not add it - auto flow = connMgr->find(tcpData.getConnectionData().flowKey); - if (flow == connMgr->end()) - { - connMgr->insert(std::make_pair(tcpData.getConnectionData().flowKey, TcpReassemblyData())); - flow = connMgr->find(tcpData.getConnectionData().flowKey); - } - - int8_t side; - - // if the user wants to write each side in a different file - set side as the sideIndex, otherwise write everything to the same file ("side 0") - if (GlobalConfig::getInstance().separateSides) - side = sideIndex; - else - side = 0; - - // if the file stream on the relevant side isn't open yet (meaning it's the first data on this connection) - if (flow->second.fileStreams[side] == nullptr) - { - // add the flow key of this connection to the list of open connections. If the return value isn't nullptr it means that there are too many open files - // and we need to close the connection with least recently used file(s) in order to open a new one. - // The connection with the least recently used file is the return value - uint32_t flowKeyToCloseFiles; - int result = GlobalConfig::getInstance().getRecentConnsWithActivity()->put(tcpData.getConnectionData().flowKey, &flowKeyToCloseFiles); - - // if result equals to 1 it means we need to close the open files in this connection (the one with the least recently used files) - if (result == 1) - { - // find the connection from the flow key - auto flow2 = connMgr->find(flowKeyToCloseFiles); - if (flow2 != connMgr->end()) - { - // close files on both sides (if they're open) - for (int index = 0; index < 2; index++) - { - if (flow2->second.fileStreams[index] != nullptr) - { - // close the file - GlobalConfig::getInstance().closeFileSteam(flow2->second.fileStreams[index]); - flow2->second.fileStreams[index] = nullptr; - - // set the reopen flag to true to indicate that next time this file will be opened it will be opened in append mode (and not overwrite mode) - flow2->second.reopenFileStreams[index] = true; - } - } - } - } - - // get the file name according to the 5-tuple etc. - std::string fileName = GlobalConfig::getInstance().getFileName(tcpData.getConnectionData(), sideIndex, GlobalConfig::getInstance().separateSides) + ".txt"; - - // open the file in overwrite mode (if this is the first time the file is opened) or in append mode (if it was already opened before) - flow->second.fileStreams[side] = GlobalConfig::getInstance().openFileStream(fileName, flow->second.reopenFileStreams[side]); - } - - // if this messages comes on a different side than previous message seen on this connection - if (sideIndex != flow->second.curSide) - { - // count number of message in each side - flow->second.numOfMessagesFromSide[sideIndex]++; - - // set side index as the current active side - flow->second.curSide = sideIndex; - } - - // count number of packets and bytes in each side of the connection - flow->second.numOfDataPackets[sideIndex]++; - flow->second.bytesFromSide[sideIndex] += (int)tcpData.getDataLength(); - - // write the new data to the file - flow->second.fileStreams[side]->write((char*)tcpData.getData(), tcpData.getDataLength()); +static void tcpReassemblyMsgReadyCallback(const int8_t sideIndex, + const pcpp::TcpStreamData& tcpData, + void* userCookie) { + // extract the connection manager from the user cookie + auto connMgr = (TcpReassemblyConnMgr*)userCookie; + + // check if this flow already appears in the connection manager. If not add it + auto flow = connMgr->find(tcpData.getConnectionData().flowKey); + if (flow == connMgr->end()) { + connMgr->insert(std::make_pair(tcpData.getConnectionData().flowKey, + TcpReassemblyData())); + flow = connMgr->find(tcpData.getConnectionData().flowKey); + } + + int8_t side; + + // if the user wants to write each side in a different file - set side as the + // sideIndex, otherwise write everything to the same file ("side 0") + if (GlobalConfig::getInstance().separateSides) + side = sideIndex; + else + side = 0; + + // if the file stream on the relevant side isn't open yet (meaning it's the + // first data on this connection) + if (flow->second.fileStreams[side] == nullptr) { + // add the flow key of this connection to the list of open connections. If + // the return value isn't nullptr it means that there are too many open + // files and we need to close the connection with least recently used + // file(s) in order to open a new one. The connection with the least + // recently used file is the return value + uint32_t flowKeyToCloseFiles; + int result = GlobalConfig::getInstance().getRecentConnsWithActivity()->put( + tcpData.getConnectionData().flowKey, &flowKeyToCloseFiles); + + // if result equals to 1 it means we need to close the open files in this + // connection (the one with the least recently used files) + if (result == 1) { + // find the connection from the flow key + auto flow2 = connMgr->find(flowKeyToCloseFiles); + if (flow2 != connMgr->end()) { + // close files on both sides (if they're open) + for (int index = 0; index < 2; index++) { + if (flow2->second.fileStreams[index] != nullptr) { + // close the file + GlobalConfig::getInstance().closeFileSteam( + flow2->second.fileStreams[index]); + flow2->second.fileStreams[index] = nullptr; + + // set the reopen flag to true to indicate that next time this file + // will be opened it will be opened in append mode (and not + // overwrite mode) + flow2->second.reopenFileStreams[index] = true; + } + } + } + } + + // get the file name according to the 5-tuple etc. + std::string fileName = GlobalConfig::getInstance().getFileName( + tcpData.getConnectionData(), sideIndex, + GlobalConfig::getInstance().separateSides) + + ".txt"; + + // open the file in overwrite mode (if this is the first time the file is + // opened) or in append mode (if it was already opened before) + flow->second.fileStreams[side] = GlobalConfig::getInstance().openFileStream( + fileName, flow->second.reopenFileStreams[side]); + } + + // if this messages comes on a different side than previous message seen on + // this connection + if (sideIndex != flow->second.curSide) { + // count number of message in each side + flow->second.numOfMessagesFromSide[sideIndex]++; + + // set side index as the current active side + flow->second.curSide = sideIndex; + } + + // count number of packets and bytes in each side of the connection + flow->second.numOfDataPackets[sideIndex]++; + flow->second.bytesFromSide[sideIndex] += (int)tcpData.getDataLength(); + + // write the new data to the file + flow->second.fileStreams[side]->write((char*)tcpData.getData(), + tcpData.getDataLength()); } - /** - * The callback being called by the TCP reassembly module whenever a new connection is found. This method adds the connection to the connection manager + * The callback being called by the TCP reassembly module whenever a new + * connection is found. This method adds the connection to the connection + * manager */ -static void tcpReassemblyConnectionStartCallback(const pcpp::ConnectionData& connectionData, void* userCookie) -{ - // get a pointer to the connection manager - auto connMgr = (TcpReassemblyConnMgr*)userCookie; - - // look for the connection in the connection manager - auto connectionMngr = connMgr->find(connectionData.flowKey); - - // assuming it's a new connection - if (connectionMngr == connMgr->end()) - { - // add it to the connection manager - connMgr->insert(std::make_pair(connectionData.flowKey, TcpReassemblyData())); - } +static void +tcpReassemblyConnectionStartCallback(const pcpp::ConnectionData& connectionData, + void* userCookie) { + // get a pointer to the connection manager + auto connMgr = (TcpReassemblyConnMgr*)userCookie; + + // look for the connection in the connection manager + auto connectionMngr = connMgr->find(connectionData.flowKey); + + // assuming it's a new connection + if (connectionMngr == connMgr->end()) { + // add it to the connection manager + connMgr->insert( + std::make_pair(connectionData.flowKey, TcpReassemblyData())); + } } - /** - * The callback being called by the TCP reassembly module whenever a connection is ending. This method removes the connection from the connection manager and writes the metadata file if requested - * by the user + * The callback being called by the TCP reassembly module whenever a connection + * is ending. This method removes the connection from the connection manager and + * writes the metadata file if requested by the user */ -static void tcpReassemblyConnectionEndCallback(const pcpp::ConnectionData& connectionData, pcpp::TcpReassembly::ConnectionEndReason reason, void* userCookie) -{ - // get a pointer to the connection manager - auto connMgr = (TcpReassemblyConnMgr*)userCookie; - - // find the connection in the connection manager by the flow key - auto connection = connMgr->find(connectionData.flowKey); - - // connection wasn't found - shouldn't get here - if (connection == connMgr->end()) - return; - - // write a metadata file if required by the user - if (GlobalConfig::getInstance().writeMetadata) - { - std::string fileName = GlobalConfig::getInstance().getFileName(connectionData, 0, false) + "-metadata.txt"; - std::ofstream metadataFile(fileName.c_str()); - metadataFile << "Number of data packets in side 0: " << connection->second.numOfDataPackets[0] << std::endl; - metadataFile << "Number of data packets in side 1: " << connection->second.numOfDataPackets[1] << std::endl; - metadataFile << "Total number of data packets: " << (connection->second.numOfDataPackets[0] + connection->second.numOfDataPackets[1]) << std::endl; - metadataFile << std::endl; - metadataFile << "Number of bytes in side 0: " << connection->second.bytesFromSide[0] << std::endl; - metadataFile << "Number of bytes in side 1: " << connection->second.bytesFromSide[1] << std::endl; - metadataFile << "Total number of bytes: " << (connection->second.bytesFromSide[0] + connection->second.bytesFromSide[1]) << std::endl; - metadataFile << std::endl; - metadataFile << "Number of messages in side 0: " << connection->second.numOfMessagesFromSide[0] << std::endl; - metadataFile << "Number of messages in side 1: " << connection->second.numOfMessagesFromSide[1] << std::endl; - metadataFile.close(); - } - - // remove the connection from the connection manager - connMgr->erase(connection); +static void tcpReassemblyConnectionEndCallback( + const pcpp::ConnectionData& connectionData, + pcpp::TcpReassembly::ConnectionEndReason reason, void* userCookie) { + // get a pointer to the connection manager + auto connMgr = (TcpReassemblyConnMgr*)userCookie; + + // find the connection in the connection manager by the flow key + auto connection = connMgr->find(connectionData.flowKey); + + // connection wasn't found - shouldn't get here + if (connection == connMgr->end()) + return; + + // write a metadata file if required by the user + if (GlobalConfig::getInstance().writeMetadata) { + std::string fileName = + GlobalConfig::getInstance().getFileName(connectionData, 0, false) + + "-metadata.txt"; + std::ofstream metadataFile(fileName.c_str()); + metadataFile << "Number of data packets in side 0: " + << connection->second.numOfDataPackets[0] << std::endl; + metadataFile << "Number of data packets in side 1: " + << connection->second.numOfDataPackets[1] << std::endl; + metadataFile << "Total number of data packets: " + << (connection->second.numOfDataPackets[0] + + connection->second.numOfDataPackets[1]) + << std::endl; + metadataFile << std::endl; + metadataFile << "Number of bytes in side 0: " + << connection->second.bytesFromSide[0] << std::endl; + metadataFile << "Number of bytes in side 1: " + << connection->second.bytesFromSide[1] << std::endl; + metadataFile << "Total number of bytes: " + << (connection->second.bytesFromSide[0] + + connection->second.bytesFromSide[1]) + << std::endl; + metadataFile << std::endl; + metadataFile << "Number of messages in side 0: " + << connection->second.numOfMessagesFromSide[0] << std::endl; + metadataFile << "Number of messages in side 1: " + << connection->second.numOfMessagesFromSide[1] << std::endl; + metadataFile.close(); + } + + // remove the connection from the connection manager + connMgr->erase(connection); } - /** - * The callback to be called when application is terminated by ctrl-c. Stops the endless while loop + * The callback to be called when application is terminated by ctrl-c. Stops the + * endless while loop */ -static void onApplicationInterrupted(void* cookie) -{ - bool* shouldStop = (bool*)cookie; - *shouldStop = true; +static void onApplicationInterrupted(void* cookie) { + bool* shouldStop = (bool*)cookie; + *shouldStop = true; } - /** - * packet capture callback - called whenever a packet arrives on the live device (in live device capturing mode) + * packet capture callback - called whenever a packet arrives on the live device + * (in live device capturing mode) */ -static void onPacketArrives(pcpp::RawPacket* packet, pcpp::PcapLiveDevice* dev, void* tcpReassemblyCookie) -{ - // get a pointer to the TCP reassembly instance and feed the packet arrived to it - auto tcpReassembly = (pcpp::TcpReassembly*)tcpReassemblyCookie; - tcpReassembly->reassemblePacket(packet); +static void onPacketArrives(pcpp::RawPacket* packet, pcpp::PcapLiveDevice* dev, + void* tcpReassemblyCookie) { + // get a pointer to the TCP reassembly instance and feed the packet arrived to + // it + auto tcpReassembly = (pcpp::TcpReassembly*)tcpReassemblyCookie; + tcpReassembly->reassemblePacket(packet); } - /** * The method responsible for TCP reassembly on pcap/pcapng files */ -void doTcpReassemblyOnPcapFile(const std::string& fileName, pcpp::TcpReassembly& tcpReassembly, const std::string& bpfFilter = "") -{ - // open input file (pcap or pcapng file) - pcpp::IFileReaderDevice* reader = pcpp::IFileReaderDevice::getReader(fileName); - - // try to open the file device - if (!reader->open()) - EXIT_WITH_ERROR("Cannot open pcap/pcapng file"); - - // set BPF filter if set by the user - if (!bpfFilter.empty()) - { - if (!reader->setFilter(bpfFilter)) - EXIT_WITH_ERROR("Cannot set BPF filter to pcap file"); - } - - std::cout << "Starting reading '" << fileName << "'..." << std::endl; - - // run in a loop that reads one packet from the file in each iteration and feeds it to the TCP reassembly instance - pcpp::RawPacket rawPacket; - while (reader->getNextPacket(rawPacket)) - { - tcpReassembly.reassemblePacket(&rawPacket); - } - - // extract number of connections before closing all of them - size_t numOfConnectionsProcessed = tcpReassembly.getConnectionInformation().size(); - - // after all packets have been read - close the connections which are still opened - tcpReassembly.closeAllConnections(); - - // close the reader and free its memory - reader->close(); - delete reader; - - std::cout << "Done! processed " << numOfConnectionsProcessed << " connections" << std::endl; +void doTcpReassemblyOnPcapFile(const std::string& fileName, + pcpp::TcpReassembly& tcpReassembly, + const std::string& bpfFilter = "") { + // open input file (pcap or pcapng file) + pcpp::IFileReaderDevice* reader = + pcpp::IFileReaderDevice::getReader(fileName); + + // try to open the file device + if (!reader->open()) + EXIT_WITH_ERROR("Cannot open pcap/pcapng file"); + + // set BPF filter if set by the user + if (!bpfFilter.empty()) { + if (!reader->setFilter(bpfFilter)) + EXIT_WITH_ERROR("Cannot set BPF filter to pcap file"); + } + + std::cout << "Starting reading '" << fileName << "'..." << std::endl; + + // run in a loop that reads one packet from the file in each iteration and + // feeds it to the TCP reassembly instance + pcpp::RawPacket rawPacket; + while (reader->getNextPacket(rawPacket)) { + tcpReassembly.reassemblePacket(&rawPacket); + } + + // extract number of connections before closing all of them + size_t numOfConnectionsProcessed = + tcpReassembly.getConnectionInformation().size(); + + // after all packets have been read - close the connections which are still + // opened + tcpReassembly.closeAllConnections(); + + // close the reader and free its memory + reader->close(); + delete reader; + + std::cout << "Done! processed " << numOfConnectionsProcessed << " connections" + << std::endl; } - /** * The method responsible for TCP reassembly on live traffic */ -void doTcpReassemblyOnLiveTraffic(pcpp::PcapLiveDevice* dev, pcpp::TcpReassembly& tcpReassembly, const std::string& bpfFilter = "") -{ - // try to open device - if (!dev->open()) - EXIT_WITH_ERROR("Cannot open interface"); - - // set BPF filter if set by the user - if (!bpfFilter.empty()) - { - if (!dev->setFilter(bpfFilter)) - EXIT_WITH_ERROR("Cannot set BPF filter to interface"); - } - - std::cout << "Starting packet capture on '" << dev->getIPv4Address() << "'..." << std::endl; - - // start capturing packets. Each packet arrived will be handled by onPacketArrives method - dev->startCapture(onPacketArrives, &tcpReassembly); - - // register the on app close event to print summary stats on app termination - bool shouldStop = false; - pcpp::ApplicationEventHandler::getInstance().onApplicationInterrupted(onApplicationInterrupted, &shouldStop); - - // run in an endless loop until the user presses ctrl+c - while(!shouldStop) - pcpp::multiPlatformSleep(1); - - // stop capturing and close the live device - dev->stopCapture(); - dev->close(); - - // close all connections which are still opened - tcpReassembly.closeAllConnections(); - - std::cout << "Done! processed " << tcpReassembly.getConnectionInformation().size() << " connections" << std::endl; +void doTcpReassemblyOnLiveTraffic(pcpp::PcapLiveDevice* dev, + pcpp::TcpReassembly& tcpReassembly, + const std::string& bpfFilter = "") { + // try to open device + if (!dev->open()) + EXIT_WITH_ERROR("Cannot open interface"); + + // set BPF filter if set by the user + if (!bpfFilter.empty()) { + if (!dev->setFilter(bpfFilter)) + EXIT_WITH_ERROR("Cannot set BPF filter to interface"); + } + + std::cout << "Starting packet capture on '" << dev->getIPv4Address() << "'..." + << std::endl; + + // start capturing packets. Each packet arrived will be handled by + // onPacketArrives method + dev->startCapture(onPacketArrives, &tcpReassembly); + + // register the on app close event to print summary stats on app termination + bool shouldStop = false; + pcpp::ApplicationEventHandler::getInstance().onApplicationInterrupted( + onApplicationInterrupted, &shouldStop); + + // run in an endless loop until the user presses ctrl+c + while (!shouldStop) + pcpp::multiPlatformSleep(1); + + // stop capturing and close the live device + dev->stopCapture(); + dev->close(); + + // close all connections which are still opened + tcpReassembly.closeAllConnections(); + + std::cout << "Done! processed " + << tcpReassembly.getConnectionInformation().size() << " connections" + << std::endl; } - /** * main method of this utility */ -int main(int argc, char* argv[]) -{ - pcpp::AppName::init(argc, argv); - - std::string interfaceNameOrIP; - std::string inputPcapFileName; - std::string bpfFilter; - std::string outputDir; - bool writeMetadata = false; - bool writeToConsole = false; - bool separateSides = false; - size_t maxOpenFiles = DEFAULT_MAX_NUMBER_OF_CONCURRENT_OPEN_FILES; - - int optionIndex = 0; - int opt; - - while((opt = getopt_long(argc, argv, "i:r:o:e:f:mcsvhl", TcpAssemblyOptions, &optionIndex)) != -1) - { - switch (opt) - { - case 0: - break; - case 'i': - interfaceNameOrIP = optarg; - break; - case 'r': - inputPcapFileName = optarg; - break; - case 'o': - outputDir = optarg; - break; - case 'e': - bpfFilter = optarg; - break; - case 's': - separateSides = true; - break; - case 'm': - writeMetadata = true; - break; - case 'c': - writeToConsole = true; - break; - case 'f': - maxOpenFiles = (size_t)atoi(optarg); - break; - case 'h': - printUsage(); - exit(0); - case 'v': - printAppVersion(); - break; - case 'l': - listInterfaces(); - break; - default: - printUsage(); - exit(-1); - } - } - - // if no interface nor input pcap file were provided - exit with error - if (inputPcapFileName.empty() && interfaceNameOrIP.empty()) - EXIT_WITH_ERROR("Neither interface nor input pcap file were provided"); - - // verify output dir exists - if (!outputDir.empty() && !pcpp::directoryExists(outputDir)) - EXIT_WITH_ERROR("Output directory doesn't exist"); - - // set global config singleton with input configuration - GlobalConfig::getInstance().outputDir = outputDir; - GlobalConfig::getInstance().writeMetadata = writeMetadata; - GlobalConfig::getInstance().writeToConsole = writeToConsole; - GlobalConfig::getInstance().separateSides = separateSides; - GlobalConfig::getInstance().maxOpenFiles = maxOpenFiles; - - // create the object which manages info on all connections - TcpReassemblyConnMgr connMgr; - - // create the TCP reassembly instance - pcpp::TcpReassembly tcpReassembly(tcpReassemblyMsgReadyCallback, &connMgr, tcpReassemblyConnectionStartCallback, tcpReassemblyConnectionEndCallback); - - // analyze in pcap file mode - if (!inputPcapFileName.empty()) - { - doTcpReassemblyOnPcapFile(inputPcapFileName, tcpReassembly, bpfFilter); - } - else // analyze in live traffic mode - { - // extract pcap live device by interface name or IP address - pcpp::PcapLiveDevice* dev = pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDeviceByIpOrName(interfaceNameOrIP); - if (dev == nullptr) - EXIT_WITH_ERROR("Couldn't find interface by provided IP address or name"); - - // start capturing packets and do TCP reassembly - doTcpReassemblyOnLiveTraffic(dev, tcpReassembly, bpfFilter); - } +int main(int argc, char* argv[]) { + pcpp::AppName::init(argc, argv); + + std::string interfaceNameOrIP; + std::string inputPcapFileName; + std::string bpfFilter; + std::string outputDir; + bool writeMetadata = false; + bool writeToConsole = false; + bool separateSides = false; + size_t maxOpenFiles = DEFAULT_MAX_NUMBER_OF_CONCURRENT_OPEN_FILES; + + int optionIndex = 0; + int opt; + + while ((opt = getopt_long(argc, argv, "i:r:o:e:f:mcsvhl", TcpAssemblyOptions, + &optionIndex)) != -1) { + switch (opt) { + case 0: + break; + case 'i': + interfaceNameOrIP = optarg; + break; + case 'r': + inputPcapFileName = optarg; + break; + case 'o': + outputDir = optarg; + break; + case 'e': + bpfFilter = optarg; + break; + case 's': + separateSides = true; + break; + case 'm': + writeMetadata = true; + break; + case 'c': + writeToConsole = true; + break; + case 'f': + maxOpenFiles = (size_t)atoi(optarg); + break; + case 'h': + printUsage(); + exit(0); + case 'v': + printAppVersion(); + break; + case 'l': + listInterfaces(); + break; + default: + printUsage(); + exit(-1); + } + } + + // if no interface nor input pcap file were provided - exit with error + if (inputPcapFileName.empty() && interfaceNameOrIP.empty()) + EXIT_WITH_ERROR("Neither interface nor input pcap file were provided"); + + // verify output dir exists + if (!outputDir.empty() && !pcpp::directoryExists(outputDir)) + EXIT_WITH_ERROR("Output directory doesn't exist"); + + // set global config singleton with input configuration + GlobalConfig::getInstance().outputDir = outputDir; + GlobalConfig::getInstance().writeMetadata = writeMetadata; + GlobalConfig::getInstance().writeToConsole = writeToConsole; + GlobalConfig::getInstance().separateSides = separateSides; + GlobalConfig::getInstance().maxOpenFiles = maxOpenFiles; + + // create the object which manages info on all connections + TcpReassemblyConnMgr connMgr; + + // create the TCP reassembly instance + pcpp::TcpReassembly tcpReassembly(tcpReassemblyMsgReadyCallback, &connMgr, + tcpReassemblyConnectionStartCallback, + tcpReassemblyConnectionEndCallback); + + // analyze in pcap file mode + if (!inputPcapFileName.empty()) { + doTcpReassemblyOnPcapFile(inputPcapFileName, tcpReassembly, bpfFilter); + } else // analyze in live traffic mode + { + // extract pcap live device by interface name or IP address + pcpp::PcapLiveDevice* dev = + pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDeviceByIpOrName( + interfaceNameOrIP); + if (dev == nullptr) + EXIT_WITH_ERROR("Couldn't find interface by provided IP address or name"); + + // start capturing packets and do TCP reassembly + doTcpReassemblyOnLiveTraffic(dev, tcpReassembly, bpfFilter); + } } diff --git a/Examples/Tutorials/Tutorial-DpdkL2Fwd/WorkerThread.cpp b/Examples/Tutorials/Tutorial-DpdkL2Fwd/WorkerThread.cpp index 50417731f3..0dd6a9f422 100644 --- a/Examples/Tutorials/Tutorial-DpdkL2Fwd/WorkerThread.cpp +++ b/Examples/Tutorials/Tutorial-DpdkL2Fwd/WorkerThread.cpp @@ -1,43 +1,33 @@ #include "WorkerThread.h" - -L2FwdWorkerThread::L2FwdWorkerThread(pcpp::DpdkDevice* rxDevice, pcpp::DpdkDevice* txDevice) : - m_RxDevice(rxDevice), m_TxDevice(txDevice), m_Stop(true), m_CoreId(MAX_NUM_OF_CORES+1) -{ +L2FwdWorkerThread::L2FwdWorkerThread(pcpp::DpdkDevice* rxDevice, + pcpp::DpdkDevice* txDevice) + : m_RxDevice(rxDevice), m_TxDevice(txDevice), m_Stop(true), + m_CoreId(MAX_NUM_OF_CORES + 1) {} + +bool L2FwdWorkerThread::run(uint32_t coreId) { + // Register coreId for this worker + m_CoreId = coreId; + m_Stop = false; + + // initialize a mbuf packet array of size 64 + pcpp::MBufRawPacket* mbufArr[64] = {}; + + // endless loop, until asking the thread to stop + // cppcheck-suppress knownConditionTrueFalse + while (!m_Stop) { + // receive packets from RX device + uint16_t numOfPackets = m_RxDevice->receivePackets(mbufArr, 64, 0); + + if (numOfPackets > 0) { + // send received packet on the TX device + m_TxDevice->sendPackets(mbufArr, numOfPackets, 0); + } + } + + return true; } -bool L2FwdWorkerThread::run(uint32_t coreId) -{ - // Register coreId for this worker - m_CoreId = coreId; - m_Stop = false; - - // initialize a mbuf packet array of size 64 - pcpp::MBufRawPacket* mbufArr[64] = {}; - - // endless loop, until asking the thread to stop - // cppcheck-suppress knownConditionTrueFalse - while (!m_Stop) - { - // receive packets from RX device - uint16_t numOfPackets = m_RxDevice->receivePackets(mbufArr, 64, 0); - - if (numOfPackets > 0) - { - // send received packet on the TX device - m_TxDevice->sendPackets(mbufArr, numOfPackets, 0); - } - } - - return true; -} +void L2FwdWorkerThread::stop() { m_Stop = true; } -void L2FwdWorkerThread::stop() -{ - m_Stop = true; -} - -uint32_t L2FwdWorkerThread::getCoreId() const -{ - return m_CoreId; -} +uint32_t L2FwdWorkerThread::getCoreId() const { return m_CoreId; } diff --git a/Examples/Tutorials/Tutorial-DpdkL2Fwd/WorkerThread.h b/Examples/Tutorials/Tutorial-DpdkL2Fwd/WorkerThread.h index 3e2a7ebb40..0c71194321 100644 --- a/Examples/Tutorials/Tutorial-DpdkL2Fwd/WorkerThread.h +++ b/Examples/Tutorials/Tutorial-DpdkL2Fwd/WorkerThread.h @@ -3,29 +3,28 @@ #include "DpdkDevice.h" #include "DpdkDeviceList.h" -class L2FwdWorkerThread : public pcpp::DpdkWorkerThread -{ - private: - pcpp::DpdkDevice* m_RxDevice; - pcpp::DpdkDevice* m_TxDevice; - bool m_Stop; - uint32_t m_CoreId; +class L2FwdWorkerThread : public pcpp::DpdkWorkerThread { + private: + pcpp::DpdkDevice* m_RxDevice; + pcpp::DpdkDevice* m_TxDevice; + bool m_Stop; + uint32_t m_CoreId; -public: - // c'tor - L2FwdWorkerThread(pcpp::DpdkDevice* rxDevice, pcpp::DpdkDevice* txDevice); + public: + // c'tor + L2FwdWorkerThread(pcpp::DpdkDevice* rxDevice, pcpp::DpdkDevice* txDevice); - // d'tor (does nothing) - ~L2FwdWorkerThread() { } + // d'tor (does nothing) + ~L2FwdWorkerThread() {} - // implement abstract method + // implement abstract method - // start running the worker thread - bool run(uint32_t coreId); + // start running the worker thread + bool run(uint32_t coreId); - // ask the worker thread to stop - void stop(); + // ask the worker thread to stop + void stop(); - // get worker thread core ID - uint32_t getCoreId() const; + // get worker thread core ID + uint32_t getCoreId() const; }; diff --git a/Examples/Tutorials/Tutorial-DpdkL2Fwd/main.cpp b/Examples/Tutorials/Tutorial-DpdkL2Fwd/main.cpp index 4f9ceecc2c..f8b8f7e704 100644 --- a/Examples/Tutorials/Tutorial-DpdkL2Fwd/main.cpp +++ b/Examples/Tutorials/Tutorial-DpdkL2Fwd/main.cpp @@ -1,157 +1,158 @@ -#include -#include -#include -#include "SystemUtils.h" #include "DpdkDeviceList.h" +#include "SystemUtils.h" #include "TablePrinter.h" +#include +#include +#include #include "WorkerThread.h" - -#define MBUF_POOL_SIZE 16*1024-1 +#define MBUF_POOL_SIZE 16 * 1024 - 1 #define DEVICE_ID_1 0 #define DEVICE_ID_2 1 #define COLLECT_STATS_EVERY_SEC 2 - // Keep running flag bool keepRunning = true; -void onApplicationInterrupted(void* cookie) -{ - keepRunning = false; - std::cout << std::endl << "Shutting down..." << std::endl; +void onApplicationInterrupted(void* cookie) { + keepRunning = false; + std::cout << std::endl + << "Shutting down..." << std::endl; } - -void printStats(pcpp::DpdkDevice* rxDevice, pcpp::DpdkDevice* txDevice) -{ - pcpp::DpdkDevice::DpdkDeviceStats rxStats; - pcpp::DpdkDevice::DpdkDeviceStats txStats; - rxDevice->getStatistics(rxStats); - txDevice->getStatistics(txStats); - - std::vector columnNames; - columnNames.push_back(" "); - columnNames.push_back("Total Packets"); - columnNames.push_back("Packets/sec"); - columnNames.push_back("Bytes"); - columnNames.push_back("Bits/sec"); - - std::vector columnLengths; - columnLengths.push_back(10); - columnLengths.push_back(15); - columnLengths.push_back(15); - columnLengths.push_back(15); - columnLengths.push_back(15); - - pcpp::TablePrinter printer(columnNames, columnLengths); - - std::stringstream totalRx; - totalRx << "rx" << "|" << rxStats.aggregatedRxStats.packets << "|" << rxStats.aggregatedRxStats.packetsPerSec << "|" << rxStats.aggregatedRxStats.bytes << "|" << rxStats.aggregatedRxStats.bytesPerSec*8; - printer.printRow(totalRx.str(), '|'); - - std::stringstream totalTx; - totalTx << "tx" << "|" << txStats.aggregatedTxStats.packets << "|" << txStats.aggregatedTxStats.packetsPerSec << "|" << txStats.aggregatedTxStats.bytes << "|" << txStats.aggregatedTxStats.bytesPerSec*8; - printer.printRow(totalTx.str(), '|'); +void printStats(pcpp::DpdkDevice* rxDevice, pcpp::DpdkDevice* txDevice) { + pcpp::DpdkDevice::DpdkDeviceStats rxStats; + pcpp::DpdkDevice::DpdkDeviceStats txStats; + rxDevice->getStatistics(rxStats); + txDevice->getStatistics(txStats); + + std::vector columnNames; + columnNames.push_back(" "); + columnNames.push_back("Total Packets"); + columnNames.push_back("Packets/sec"); + columnNames.push_back("Bytes"); + columnNames.push_back("Bits/sec"); + + std::vector columnLengths; + columnLengths.push_back(10); + columnLengths.push_back(15); + columnLengths.push_back(15); + columnLengths.push_back(15); + columnLengths.push_back(15); + + pcpp::TablePrinter printer(columnNames, columnLengths); + + std::stringstream totalRx; + totalRx << "rx" + << "|" << rxStats.aggregatedRxStats.packets << "|" + << rxStats.aggregatedRxStats.packetsPerSec << "|" + << rxStats.aggregatedRxStats.bytes << "|" + << rxStats.aggregatedRxStats.bytesPerSec * 8; + printer.printRow(totalRx.str(), '|'); + + std::stringstream totalTx; + totalTx << "tx" + << "|" << txStats.aggregatedTxStats.packets << "|" + << txStats.aggregatedTxStats.packetsPerSec << "|" + << txStats.aggregatedTxStats.bytes << "|" + << txStats.aggregatedTxStats.bytesPerSec * 8; + printer.printRow(totalTx.str(), '|'); } - -int main(int argc, char* argv[]) -{ - // Register the on app close event handler - pcpp::ApplicationEventHandler::getInstance().onApplicationInterrupted(onApplicationInterrupted, NULL); - - // Initialize DPDK - pcpp::CoreMask coreMaskToUse = pcpp::getCoreMaskForAllMachineCores(); - pcpp::DpdkDeviceList::initDpdk(coreMaskToUse, MBUF_POOL_SIZE); - - // Find DPDK devices - pcpp::DpdkDevice* device1 = pcpp::DpdkDeviceList::getInstance().getDeviceByPort(DEVICE_ID_1); - if (device1 == NULL) - { - std::cerr << "Cannot find device1 with port '" << DEVICE_ID_1 << "'" << std::endl; - return 1; - } - - pcpp::DpdkDevice* device2 = pcpp::DpdkDeviceList::getInstance().getDeviceByPort(DEVICE_ID_2); - if (device2 == NULL) - { - std::cerr << "Cannot find device2 with port '" << DEVICE_ID_2 << "'" << std::endl; - return 1; - } - - // Open DPDK devices - if (!device1->openMultiQueues(1, 1)) - { - std::cerr << "Couldn't open device1 #" << device1->getDeviceId() << ", PMD '" << device1->getPMDName() << "'" << std::endl; - return 1; - } - - if (!device2->openMultiQueues(1, 1)) - { - std::cerr << "Couldn't open device2 #" << device2->getDeviceId() << ", PMD '" << device2->getPMDName() << "'" << std::endl; - return 1; - } - - // Create worker threads - std::vector workers; - workers.push_back(new L2FwdWorkerThread(device1, device2)); - workers.push_back(new L2FwdWorkerThread(device2, device1)); - - // Create core mask - use core 1 and 2 for the two threads - int workersCoreMask = 0; - for (int i = 1; i <= 2; i++) - { - workersCoreMask = workersCoreMask | (1 << i); - } - - // Start capture in async mode - if (!pcpp::DpdkDeviceList::getInstance().startDpdkWorkerThreads(workersCoreMask, workers)) - { - std::cerr << "Couldn't start worker threads" << std::endl; - return 1; - } - - uint64_t counter = 0; - int statsCounter = 1; - - // Keep running while flag is on - while (keepRunning) - { - // Sleep for 1 second - sleep(1); - - // Print stats every COLLECT_STATS_EVERY_SEC seconds - if (counter % COLLECT_STATS_EVERY_SEC == 0) - { - // Clear screen and move to top left - std::cout << "\033[2J\033[1;1H"; - - std::cout - << "Stats #" << statsCounter++ << std::endl - << "==========" << std::endl - << std::endl; - - // Print stats of traffic going from Device1 to Device2 - std::cout << std::endl - << "Device1->Device2 stats:" << std::endl - << std::endl; - printStats(device1, device2); - - // Print stats of traffic going from Device2 to Device1 - std::cout << std::endl - << "Device2->Device1 stats:" << std::endl - << std::endl; - printStats(device2, device1); - } - counter++; - } - - // Stop worker threads - pcpp::DpdkDeviceList::getInstance().stopDpdkWorkerThreads(); - - // Exit app with normal exit code - return 0; +int main(int argc, char* argv[]) { + // Register the on app close event handler + pcpp::ApplicationEventHandler::getInstance().onApplicationInterrupted( + onApplicationInterrupted, NULL); + + // Initialize DPDK + pcpp::CoreMask coreMaskToUse = pcpp::getCoreMaskForAllMachineCores(); + pcpp::DpdkDeviceList::initDpdk(coreMaskToUse, MBUF_POOL_SIZE); + + // Find DPDK devices + pcpp::DpdkDevice* device1 = + pcpp::DpdkDeviceList::getInstance().getDeviceByPort(DEVICE_ID_1); + if (device1 == NULL) { + std::cerr << "Cannot find device1 with port '" << DEVICE_ID_1 << "'" + << std::endl; + return 1; + } + + pcpp::DpdkDevice* device2 = + pcpp::DpdkDeviceList::getInstance().getDeviceByPort(DEVICE_ID_2); + if (device2 == NULL) { + std::cerr << "Cannot find device2 with port '" << DEVICE_ID_2 << "'" + << std::endl; + return 1; + } + + // Open DPDK devices + if (!device1->openMultiQueues(1, 1)) { + std::cerr << "Couldn't open device1 #" << device1->getDeviceId() + << ", PMD '" << device1->getPMDName() << "'" << std::endl; + return 1; + } + + if (!device2->openMultiQueues(1, 1)) { + std::cerr << "Couldn't open device2 #" << device2->getDeviceId() + << ", PMD '" << device2->getPMDName() << "'" << std::endl; + return 1; + } + + // Create worker threads + std::vector workers; + workers.push_back(new L2FwdWorkerThread(device1, device2)); + workers.push_back(new L2FwdWorkerThread(device2, device1)); + + // Create core mask - use core 1 and 2 for the two threads + int workersCoreMask = 0; + for (int i = 1; i <= 2; i++) { + workersCoreMask = workersCoreMask | (1 << i); + } + + // Start capture in async mode + if (!pcpp::DpdkDeviceList::getInstance().startDpdkWorkerThreads( + workersCoreMask, workers)) { + std::cerr << "Couldn't start worker threads" << std::endl; + return 1; + } + + uint64_t counter = 0; + int statsCounter = 1; + + // Keep running while flag is on + while (keepRunning) { + // Sleep for 1 second + sleep(1); + + // Print stats every COLLECT_STATS_EVERY_SEC seconds + if (counter % COLLECT_STATS_EVERY_SEC == 0) { + // Clear screen and move to top left + std::cout << "\033[2J\033[1;1H"; + + std::cout << "Stats #" << statsCounter++ << std::endl + << "==========" << std::endl + << std::endl; + + // Print stats of traffic going from Device1 to Device2 + std::cout << std::endl + << "Device1->Device2 stats:" << std::endl + << std::endl; + printStats(device1, device2); + + // Print stats of traffic going from Device2 to Device1 + std::cout << std::endl + << "Device2->Device1 stats:" << std::endl + << std::endl; + printStats(device2, device1); + } + counter++; + } + + // Stop worker threads + pcpp::DpdkDeviceList::getInstance().stopDpdkWorkerThreads(); + + // Exit app with normal exit code + return 0; } diff --git a/Examples/Tutorials/Tutorial-HelloWorld/main.cpp b/Examples/Tutorials/Tutorial-HelloWorld/main.cpp index fcad0e09fb..116d67ea90 100644 --- a/Examples/Tutorials/Tutorial-HelloWorld/main.cpp +++ b/Examples/Tutorials/Tutorial-HelloWorld/main.cpp @@ -1,45 +1,41 @@ -#include #include #include #include +#include -int main(int argc, char* argv[]) -{ - // open a pcap file for reading - pcpp::PcapFileReaderDevice reader("1_packet.pcap"); - if (!reader.open()) - { - std::cerr << "Error opening the pcap file" << std::endl; - return 1; - } +int main(int argc, char* argv[]) { + // open a pcap file for reading + pcpp::PcapFileReaderDevice reader("1_packet.pcap"); + if (!reader.open()) { + std::cerr << "Error opening the pcap file" << std::endl; + return 1; + } - // read the first (and only) packet from the file - pcpp::RawPacket rawPacket; - if (!reader.getNextPacket(rawPacket)) - { - std::cerr << "Couldn't read the first packet in the file" << std::endl; - return 1; - } + // read the first (and only) packet from the file + pcpp::RawPacket rawPacket; + if (!reader.getNextPacket(rawPacket)) { + std::cerr << "Couldn't read the first packet in the file" << std::endl; + return 1; + } - // parse the raw packet into a parsed packet - pcpp::Packet parsedPacket(&rawPacket); + // parse the raw packet into a parsed packet + pcpp::Packet parsedPacket(&rawPacket); - // verify the packet is IPv4 - if (parsedPacket.isPacketOfType(pcpp::IPv4)) - { - // extract source and dest IPs - pcpp::IPv4Address srcIP = parsedPacket.getLayerOfType()->getSrcIPv4Address(); - pcpp::IPv4Address destIP = parsedPacket.getLayerOfType()->getDstIPv4Address(); + // verify the packet is IPv4 + if (parsedPacket.isPacketOfType(pcpp::IPv4)) { + // extract source and dest IPs + pcpp::IPv4Address srcIP = + parsedPacket.getLayerOfType()->getSrcIPv4Address(); + pcpp::IPv4Address destIP = + parsedPacket.getLayerOfType()->getDstIPv4Address(); - // print source and dest IPs - std::cout - << "Source IP is '" << srcIP << "'; " - << "Dest IP is '" << destIP << "'" - << std::endl; - } + // print source and dest IPs + std::cout << "Source IP is '" << srcIP << "'; " + << "Dest IP is '" << destIP << "'" << std::endl; + } - // close the file - reader.close(); + // close the file + reader.close(); - return 0; + return 0; } diff --git a/Examples/Tutorials/Tutorial-LiveTraffic/main.cpp b/Examples/Tutorials/Tutorial-LiveTraffic/main.cpp index 834a397872..7fcba002d5 100644 --- a/Examples/Tutorials/Tutorial-LiveTraffic/main.cpp +++ b/Examples/Tutorials/Tutorial-LiveTraffic/main.cpp @@ -1,286 +1,305 @@ -#include -#include "stdlib.h" #include "PcapLiveDeviceList.h" #include "SystemUtils.h" +#include "stdlib.h" +#include /** * A struct for collecting packet statistics */ -struct PacketStats -{ - int ethPacketCount; - int ipv4PacketCount; - int ipv6PacketCount; - int tcpPacketCount; - int udpPacketCount; - int dnsPacketCount; - int httpPacketCount; - int sslPacketCount; - - - /** - * Clear all stats - */ - void clear() { ethPacketCount = 0; ipv4PacketCount = 0; ipv6PacketCount = 0; tcpPacketCount = 0; udpPacketCount = 0; tcpPacketCount = 0; dnsPacketCount = 0; httpPacketCount = 0; sslPacketCount = 0; } - - /** - * C'tor - */ - PacketStats() { clear(); } - - /** - * Collect stats from a packet - */ - void consumePacket(pcpp::Packet& packet) - { - if (packet.isPacketOfType(pcpp::Ethernet)) - ethPacketCount++; - if (packet.isPacketOfType(pcpp::IPv4)) - ipv4PacketCount++; - if (packet.isPacketOfType(pcpp::IPv6)) - ipv6PacketCount++; - if (packet.isPacketOfType(pcpp::TCP)) - tcpPacketCount++; - if (packet.isPacketOfType(pcpp::UDP)) - udpPacketCount++; - if (packet.isPacketOfType(pcpp::DNS)) - dnsPacketCount++; - if (packet.isPacketOfType(pcpp::HTTP)) - httpPacketCount++; - if (packet.isPacketOfType(pcpp::SSL)) - sslPacketCount++; - } - - /** - * Print stats to console - */ - void printToConsole() - { - std::cout - << "Ethernet packet count: " << ethPacketCount << std::endl - << "IPv4 packet count: " << ipv4PacketCount << std::endl - << "IPv6 packet count: " << ipv6PacketCount << std::endl - << "TCP packet count: " << tcpPacketCount << std::endl - << "UDP packet count: " << udpPacketCount << std::endl - << "DNS packet count: " << dnsPacketCount << std::endl - << "HTTP packet count: " << httpPacketCount << std::endl - << "SSL packet count: " << sslPacketCount << std::endl; - } +struct PacketStats { + int ethPacketCount; + int ipv4PacketCount; + int ipv6PacketCount; + int tcpPacketCount; + int udpPacketCount; + int dnsPacketCount; + int httpPacketCount; + int sslPacketCount; + + /** + * Clear all stats + */ + void clear() { + ethPacketCount = 0; + ipv4PacketCount = 0; + ipv6PacketCount = 0; + tcpPacketCount = 0; + udpPacketCount = 0; + tcpPacketCount = 0; + dnsPacketCount = 0; + httpPacketCount = 0; + sslPacketCount = 0; + } + + /** + * C'tor + */ + PacketStats() { clear(); } + + /** + * Collect stats from a packet + */ + void consumePacket(pcpp::Packet& packet) { + if (packet.isPacketOfType(pcpp::Ethernet)) + ethPacketCount++; + if (packet.isPacketOfType(pcpp::IPv4)) + ipv4PacketCount++; + if (packet.isPacketOfType(pcpp::IPv6)) + ipv6PacketCount++; + if (packet.isPacketOfType(pcpp::TCP)) + tcpPacketCount++; + if (packet.isPacketOfType(pcpp::UDP)) + udpPacketCount++; + if (packet.isPacketOfType(pcpp::DNS)) + dnsPacketCount++; + if (packet.isPacketOfType(pcpp::HTTP)) + httpPacketCount++; + if (packet.isPacketOfType(pcpp::SSL)) + sslPacketCount++; + } + + /** + * Print stats to console + */ + void printToConsole() { + std::cout << "Ethernet packet count: " << ethPacketCount << std::endl + << "IPv4 packet count: " << ipv4PacketCount << std::endl + << "IPv6 packet count: " << ipv6PacketCount << std::endl + << "TCP packet count: " << tcpPacketCount << std::endl + << "UDP packet count: " << udpPacketCount << std::endl + << "DNS packet count: " << dnsPacketCount << std::endl + << "HTTP packet count: " << httpPacketCount << std::endl + << "SSL packet count: " << sslPacketCount << std::endl; + } }; - /** - * A callback function for the async capture which is called each time a packet is captured + * A callback function for the async capture which is called each time a packet + * is captured */ -static void onPacketArrives(pcpp::RawPacket* packet, pcpp::PcapLiveDevice* dev, void* cookie) -{ - // extract the stats object form the cookie - PacketStats* stats = (PacketStats*)cookie; +static void onPacketArrives(pcpp::RawPacket* packet, pcpp::PcapLiveDevice* dev, + void* cookie) { + // extract the stats object form the cookie + PacketStats* stats = (PacketStats*)cookie; - // parsed the raw packet - pcpp::Packet parsedPacket(packet); + // parsed the raw packet + pcpp::Packet parsedPacket(packet); - // collect stats from packet - stats->consumePacket(parsedPacket); + // collect stats from packet + stats->consumePacket(parsedPacket); } - /** - * a callback function for the blocking mode capture which is called each time a packet is captured + * a callback function for the blocking mode capture which is called each time a + * packet is captured */ -static bool onPacketArrivesBlockingMode(pcpp::RawPacket* packet, pcpp::PcapLiveDevice* dev, void* cookie) -{ - // extract the stats object form the cookie - PacketStats* stats = (PacketStats*)cookie; +static bool onPacketArrivesBlockingMode(pcpp::RawPacket* packet, + pcpp::PcapLiveDevice* dev, + void* cookie) { + // extract the stats object form the cookie + PacketStats* stats = (PacketStats*)cookie; - // parsed the raw packet - pcpp::Packet parsedPacket(packet); + // parsed the raw packet + pcpp::Packet parsedPacket(packet); - // collect stats from packet - stats->consumePacket(parsedPacket); + // collect stats from packet + stats->consumePacket(parsedPacket); - // return false means we don't want to stop capturing after this callback - return false; + // return false means we don't want to stop capturing after this callback + return false; } - /** * main method of the application */ -int main(int argc, char* argv[]) -{ - // IPv4 address of the interface we want to sniff - std::string interfaceIPAddr = "10.0.0.1"; - - // find the interface by IP address - pcpp::PcapLiveDevice* dev = pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDeviceByIp(interfaceIPAddr); - if (dev == nullptr) - { - std::cerr << "Cannot find interface with IPv4 address of '" << interfaceIPAddr << "'" << std::endl; - return 1; - } - - // Get device info - // ~~~~~~~~~~~~~~~ - - // before capturing packets let's print some info about this interface - std::cout - << "Interface info:" << std::endl - << " Interface name: " << dev->getName() << std::endl // get interface name - << " Interface description: " << dev->getDesc() << std::endl // get interface description - << " MAC address: " << dev->getMacAddress() << std::endl // get interface MAC address - << " Default gateway: " << dev->getDefaultGateway() << std::endl // get default gateway - << " Interface MTU: " << dev->getMtu() << std::endl; // get interface MTU - - if (dev->getDnsServers().size() > 0) - std::cout << " DNS server: " << dev->getDnsServers().at(0) << std::endl; - - // open the device before start capturing/sending packets - if (!dev->open()) - { - std::cerr << "Cannot open device" << std::endl; - return 1; - } - - // create the stats object - PacketStats stats; - - - // Async packet capture with a callback function - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - std::cout << std::endl << "Starting async capture..." << std::endl; - - // start capture in async mode. Give a callback function to call to whenever a packet is captured and the stats object as the cookie - dev->startCapture(onPacketArrives, &stats); - - // sleep for 10 seconds in main thread, in the meantime packets are captured in the async thread - pcpp::multiPlatformSleep(10); - - // stop capturing packets - dev->stopCapture(); - - // print results - std::cout << "Results:" << std::endl; - stats.printToConsole(); - - // clear stats - stats.clear(); - - - // Capturing packets in a packet vector - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - std::cout << std::endl << "Starting capture with packet vector..." << std::endl; - - // create an empty packet vector object - pcpp::RawPacketVector packetVec; - - // start capturing packets. All packets will be added to the packet vector - dev->startCapture(packetVec); - - // sleep for 10 seconds in main thread, in the meantime packets are captured in the async thread - pcpp::multiPlatformSleep(10); +int main(int argc, char* argv[]) { + // IPv4 address of the interface we want to sniff + std::string interfaceIPAddr = "10.0.0.1"; + + // find the interface by IP address + pcpp::PcapLiveDevice* dev = + pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDeviceByIp( + interfaceIPAddr); + if (dev == nullptr) { + std::cerr << "Cannot find interface with IPv4 address of '" + << interfaceIPAddr << "'" << std::endl; + return 1; + } + + // Get device info + // ~~~~~~~~~~~~~~~ + + // before capturing packets let's print some info about this interface + std::cout << "Interface info:" << std::endl + << " Interface name: " << dev->getName() + << std::endl // get interface name + << " Interface description: " << dev->getDesc() + << std::endl // get interface description + << " MAC address: " << dev->getMacAddress() + << std::endl // get interface MAC address + << " Default gateway: " << dev->getDefaultGateway() + << std::endl // get default gateway + << " Interface MTU: " << dev->getMtu() + << std::endl; // get interface MTU - // stop capturing packets - dev->stopCapture(); + if (dev->getDnsServers().size() > 0) + std::cout << " DNS server: " << dev->getDnsServers().at(0) + << std::endl; + + // open the device before start capturing/sending packets + if (!dev->open()) { + std::cerr << "Cannot open device" << std::endl; + return 1; + } + + // create the stats object + PacketStats stats; + + // Async packet capture with a callback function + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + std::cout << std::endl + << "Starting async capture..." << std::endl; + + // start capture in async mode. Give a callback function to call to whenever a + // packet is captured and the stats object as the cookie + dev->startCapture(onPacketArrives, &stats); + + // sleep for 10 seconds in main thread, in the meantime packets are captured + // in the async thread + pcpp::multiPlatformSleep(10); + + // stop capturing packets + dev->stopCapture(); + + // print results + std::cout << "Results:" << std::endl; + stats.printToConsole(); + + // clear stats + stats.clear(); - // go over the packet vector and feed all packets to the stats object - for (pcpp::RawPacketVector::ConstVectorIterator iter = packetVec.begin(); iter != packetVec.end(); iter++) - { - // parse raw packet - pcpp::Packet parsedPacket(*iter); + // Capturing packets in a packet vector + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // feed packet to the stats object - stats.consumePacket(parsedPacket); - } + std::cout << std::endl + << "Starting capture with packet vector..." << std::endl; - // print results - std::cout << "Results:" << std::endl; - stats.printToConsole(); + // create an empty packet vector object + pcpp::RawPacketVector packetVec; + + // start capturing packets. All packets will be added to the packet vector + dev->startCapture(packetVec); - // clear stats - stats.clear(); + // sleep for 10 seconds in main thread, in the meantime packets are captured + // in the async thread + pcpp::multiPlatformSleep(10); + // stop capturing packets + dev->stopCapture(); - // Capturing packets in blocking mode - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // go over the packet vector and feed all packets to the stats object + for (pcpp::RawPacketVector::ConstVectorIterator iter = packetVec.begin(); + iter != packetVec.end(); iter++) { + // parse raw packet + pcpp::Packet parsedPacket(*iter); - std::cout << std::endl << "Starting capture in blocking mode..." << std::endl; + // feed packet to the stats object + stats.consumePacket(parsedPacket); + } - // start capturing in blocking mode. Give a callback function to call to whenever a packet is captured, the stats object as the cookie and a 10 seconds timeout - dev->startCaptureBlockingMode(onPacketArrivesBlockingMode, &stats, 10); + // print results + std::cout << "Results:" << std::endl; + stats.printToConsole(); - // thread is blocked until capture is finished + // clear stats + stats.clear(); - // capture is finished, print results - std::cout << "Results:" << std::endl; - stats.printToConsole(); + // Capturing packets in blocking mode + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - stats.clear(); + std::cout << std::endl + << "Starting capture in blocking mode..." << std::endl; + // start capturing in blocking mode. Give a callback function to call to + // whenever a packet is captured, the stats object as the cookie and a 10 + // seconds timeout + dev->startCaptureBlockingMode(onPacketArrivesBlockingMode, &stats, 10); - // Sending single packets - // ~~~~~~~~~~~~~~~~~~~~~~ + // thread is blocked until capture is finished - std::cout << std::endl << "Sending " << packetVec.size() << " packets one by one..." << std::endl; + // capture is finished, print results + std::cout << "Results:" << std::endl; + stats.printToConsole(); - // go over the vector of packets and send them one by one - for (pcpp::RawPacketVector::ConstVectorIterator iter = packetVec.begin(); iter != packetVec.end(); iter++) - { - // send the packet. If fails exit the application - if (!dev->sendPacket(**iter)) - { - std::cerr << "Couldn't send packet" << std::endl; - return 1; - } - } - std::cout << packetVec.size() << " packets sent" << std::endl; + stats.clear(); + // Sending single packets + // ~~~~~~~~~~~~~~~~~~~~~~ - // Sending batch of packets - // ~~~~~~~~~~~~~~~~~~~~~~~~ + std::cout << std::endl + << "Sending " << packetVec.size() << " packets one by one..." + << std::endl; - std::cout << std::endl << "Sending " << packetVec.size() << " packets..." << std::endl; + // go over the vector of packets and send them one by one + for (pcpp::RawPacketVector::ConstVectorIterator iter = packetVec.begin(); + iter != packetVec.end(); iter++) { + // send the packet. If fails exit the application + if (!dev->sendPacket(**iter)) { + std::cerr << "Couldn't send packet" << std::endl; + return 1; + } + } + std::cout << packetVec.size() << " packets sent" << std::endl; - // send all packets in the vector. The returned number shows how many packets were actually sent (expected to be equal to vector size) - int packetsSent = dev->sendPackets(packetVec); + // Sending batch of packets + // ~~~~~~~~~~~~~~~~~~~~~~~~ - std::cout << packetsSent << " packets sent" << std::endl; + std::cout << std::endl + << "Sending " << packetVec.size() << " packets..." << std::endl; + // send all packets in the vector. The returned number shows how many packets + // were actually sent (expected to be equal to vector size) + int packetsSent = dev->sendPackets(packetVec); - // Using filters - // ~~~~~~~~~~~~~ + std::cout << packetsSent << " packets sent" << std::endl; - // create a filter instance to capture only traffic on port 80 - pcpp::PortFilter portFilter(80, pcpp::SRC_OR_DST); + // Using filters + // ~~~~~~~~~~~~~ - // create a filter instance to capture only TCP traffic - pcpp::ProtoFilter protocolFilter(pcpp::TCP); + // create a filter instance to capture only traffic on port 80 + pcpp::PortFilter portFilter(80, pcpp::SRC_OR_DST); - // create an AND filter to combine both filters - capture only TCP traffic on port 80 - pcpp::AndFilter andFilter; - andFilter.addFilter(&portFilter); - andFilter.addFilter(&protocolFilter); + // create a filter instance to capture only TCP traffic + pcpp::ProtoFilter protocolFilter(pcpp::TCP); - // set the filter on the device - dev->setFilter(andFilter); + // create an AND filter to combine both filters - capture only TCP traffic on + // port 80 + pcpp::AndFilter andFilter; + andFilter.addFilter(&portFilter); + andFilter.addFilter(&protocolFilter); - std::cout << std::endl << "Starting packet capture with a filter in place..." << std::endl; + // set the filter on the device + dev->setFilter(andFilter); - // start capture in async mode. Give a callback function to call to whenever a packet is captured and the stats object as the cookie - dev->startCapture(onPacketArrives, &stats); + std::cout << std::endl + << "Starting packet capture with a filter in place..." << std::endl; - // sleep for 10 seconds in main thread, in the meantime packets are captured in the async thread - pcpp::multiPlatformSleep(10); + // start capture in async mode. Give a callback function to call to whenever a + // packet is captured and the stats object as the cookie + dev->startCapture(onPacketArrives, &stats); - // stop capturing packets - dev->stopCapture(); + // sleep for 10 seconds in main thread, in the meantime packets are captured + // in the async thread + pcpp::multiPlatformSleep(10); - // print results - should capture only packets which match the filter (which is TCP port 80) - std::cout << "Results:" << std::endl; - stats.printToConsole(); + // stop capturing packets + dev->stopCapture(); + // print results - should capture only packets which match the filter (which + // is TCP port 80) + std::cout << "Results:" << std::endl; + stats.printToConsole(); - // close the device before application ends - dev->close(); + // close the device before application ends + dev->close(); } diff --git a/Examples/Tutorials/Tutorial-PacketCraftAndEdit/main.cpp b/Examples/Tutorials/Tutorial-PacketCraftAndEdit/main.cpp index 54d71ffb34..8acfbd1297 100644 --- a/Examples/Tutorials/Tutorial-PacketCraftAndEdit/main.cpp +++ b/Examples/Tutorials/Tutorial-PacketCraftAndEdit/main.cpp @@ -1,140 +1,147 @@ -#include -#include "stdlib.h" -#include "SystemUtils.h" -#include "Packet.h" +#include "DnsLayer.h" #include "EthLayer.h" -#include "VlanLayer.h" +#include "HttpLayer.h" #include "IPv4Layer.h" +#include "Packet.h" +#include "PcapFileDevice.h" +#include "SystemUtils.h" #include "TcpLayer.h" -#include "HttpLayer.h" #include "UdpLayer.h" -#include "DnsLayer.h" -#include "PcapFileDevice.h" +#include "VlanLayer.h" +#include "stdlib.h" +#include -int main(int argc, char* argv[]) -{ - // Packet Editing - // ~~~~~~~~~~~~~~ - - // use the IFileReaderDevice interface to automatically identify file type (pcap/pcap-ng) - // and create an interface instance that both readers implement - pcpp::IFileReaderDevice* reader = pcpp::IFileReaderDevice::getReader("1_http_packet.pcap"); - - // verify that a reader interface was indeed created - if (reader == nullptr) - { - std::cerr << "Cannot determine reader for file type" << std::endl; - return 1; - } - - // open the reader for reading - if (!reader->open()) - { - std::cerr << "Cannot open input.pcap for reading" << std::endl; - return 1; - } - - // read the first (and only) packet from the file - pcpp::RawPacket rawPacket; - if (!reader->getNextPacket(rawPacket)) - { - std::cerr << "Couldn't read the first packet in the file" << std::endl; - return 1; - } - - // close the file reader, we don't need it anymore - reader->close(); - - // parse the raw packet into a parsed packet - pcpp::Packet parsedPacket(&rawPacket); - - // now let's get the Ethernet layer - pcpp::EthLayer* ethernetLayer = parsedPacket.getLayerOfType(); - // change the source dest MAC address - ethernetLayer->setDestMac(pcpp::MacAddress("aa:bb:cc:dd:ee:ff")); - - // let's get the IPv4 layer - pcpp::IPv4Layer* ipLayer = parsedPacket.getLayerOfType(); - // change source IP address - ipLayer->setSrcIPv4Address(pcpp::IPv4Address("1.1.1.1")); - // change IP ID - ipLayer->getIPv4Header()->ipId = pcpp::hostToNet16(4000); - // change TTL value - ipLayer->getIPv4Header()->timeToLive = 12; - - // let's get the TCP layer - pcpp::TcpLayer* tcpLayer = parsedPacket.getLayerOfType(); - // change source port - tcpLayer->getTcpHeader()->portSrc = pcpp::hostToNet16(12345); - // add URG flag - tcpLayer->getTcpHeader()->urgFlag = 1; - // add MSS TCP option - tcpLayer->addTcpOptionAfter(pcpp::TcpOptionBuilder(pcpp::TCPOPT_MSS, (uint16_t)1460)); - - // let's get the HTTP layer - pcpp::HttpRequestLayer* httpRequestLayer = parsedPacket.getLayerOfType(); - // change the request method from GET to TRACE - httpRequestLayer->getFirstLine()->setMethod(pcpp::HttpRequestLayer::HttpTRACE); - // change host to www.google.com - httpRequestLayer->getFieldByName(PCPP_HTTP_HOST_FIELD)->setFieldValue("www.google.com"); - // change referer value to www.aol.com - httpRequestLayer->getFieldByName(PCPP_HTTP_REFERER_FIELD)->setFieldValue("www.aol.com"); - // remove cookie field - httpRequestLayer->removeField(PCPP_HTTP_COOKIE_FIELD); - // add x-forwarded-for field - pcpp::HeaderField* xForwardedForField = httpRequestLayer->insertField(httpRequestLayer->getFieldByName(PCPP_HTTP_HOST_FIELD), "X-Forwarded-For", "1.1.1.1"); - // add cache-control field - httpRequestLayer->insertField(xForwardedForField, "Cache-Control", "max-age=0"); - - // create a new vlan layer - pcpp::VlanLayer newVlanLayer(123, false, 1, PCPP_ETHERTYPE_IP); - - // add the vlan layer to the packet after the existing Ethernet layer - parsedPacket.insertLayer(ethernetLayer, &newVlanLayer); - - // compute all calculated fields - parsedPacket.computeCalculateFields(); - - // write the modified packet to a pcap file - pcpp::PcapFileWriterDevice writer("1_modified_packet.pcap"); - writer.open(); - writer.writePacket(*(parsedPacket.getRawPacket())); - writer.close(); - - - // Packet Creation - // ~~~~~~~~~~~~~~~ - - // create a new Ethernet layer - pcpp::EthLayer newEthernetLayer(pcpp::MacAddress("00:50:43:11:22:33"), pcpp::MacAddress("aa:bb:cc:dd:ee:ff")); - - // create a new IPv4 layer - pcpp::IPv4Layer newIPLayer(pcpp::IPv4Address("192.168.1.1"), pcpp::IPv4Address("10.0.0.1")); - newIPLayer.getIPv4Header()->ipId = pcpp::hostToNet16(2000); - newIPLayer.getIPv4Header()->timeToLive = 64; - - // create a new UDP layer - pcpp::UdpLayer newUdpLayer(12345, 53); - - // create a new DNS layer - pcpp::DnsLayer newDnsLayer; - newDnsLayer.addQuery("www.ebay.com", pcpp::DNS_TYPE_A, pcpp::DNS_CLASS_IN); - - // create a packet with initial capacity of 100 bytes (will grow automatically if needed) - pcpp::Packet newPacket(100); - - // add all the layers we created - newPacket.addLayer(&newEthernetLayer); - newPacket.addLayer(&newIPLayer); - newPacket.addLayer(&newUdpLayer); - newPacket.addLayer(&newDnsLayer); - - // compute all calculated fields - newPacket.computeCalculateFields(); - - // write the new packet to a pcap file - pcpp::PcapFileWriterDevice writer2("1_new_packet.pcap"); - writer2.open(); - writer2.writePacket(*(newPacket.getRawPacket())); - writer2.close(); +int main(int argc, char* argv[]) { + // Packet Editing + // ~~~~~~~~~~~~~~ + + // use the IFileReaderDevice interface to automatically identify file type + // (pcap/pcap-ng) and create an interface instance that both readers implement + pcpp::IFileReaderDevice* reader = + pcpp::IFileReaderDevice::getReader("1_http_packet.pcap"); + + // verify that a reader interface was indeed created + if (reader == nullptr) { + std::cerr << "Cannot determine reader for file type" << std::endl; + return 1; + } + + // open the reader for reading + if (!reader->open()) { + std::cerr << "Cannot open input.pcap for reading" << std::endl; + return 1; + } + + // read the first (and only) packet from the file + pcpp::RawPacket rawPacket; + if (!reader->getNextPacket(rawPacket)) { + std::cerr << "Couldn't read the first packet in the file" << std::endl; + return 1; + } + + // close the file reader, we don't need it anymore + reader->close(); + + // parse the raw packet into a parsed packet + pcpp::Packet parsedPacket(&rawPacket); + + // now let's get the Ethernet layer + pcpp::EthLayer* ethernetLayer = parsedPacket.getLayerOfType(); + // change the source dest MAC address + ethernetLayer->setDestMac(pcpp::MacAddress("aa:bb:cc:dd:ee:ff")); + + // let's get the IPv4 layer + pcpp::IPv4Layer* ipLayer = parsedPacket.getLayerOfType(); + // change source IP address + ipLayer->setSrcIPv4Address(pcpp::IPv4Address("1.1.1.1")); + // change IP ID + ipLayer->getIPv4Header()->ipId = pcpp::hostToNet16(4000); + // change TTL value + ipLayer->getIPv4Header()->timeToLive = 12; + + // let's get the TCP layer + pcpp::TcpLayer* tcpLayer = parsedPacket.getLayerOfType(); + // change source port + tcpLayer->getTcpHeader()->portSrc = pcpp::hostToNet16(12345); + // add URG flag + tcpLayer->getTcpHeader()->urgFlag = 1; + // add MSS TCP option + tcpLayer->addTcpOptionAfter( + pcpp::TcpOptionBuilder(pcpp::TCPOPT_MSS, (uint16_t)1460)); + + // let's get the HTTP layer + pcpp::HttpRequestLayer* httpRequestLayer = + parsedPacket.getLayerOfType(); + // change the request method from GET to TRACE + httpRequestLayer->getFirstLine()->setMethod( + pcpp::HttpRequestLayer::HttpTRACE); + // change host to www.google.com + httpRequestLayer->getFieldByName(PCPP_HTTP_HOST_FIELD) + ->setFieldValue("www.google.com"); + // change referer value to www.aol.com + httpRequestLayer->getFieldByName(PCPP_HTTP_REFERER_FIELD) + ->setFieldValue("www.aol.com"); + // remove cookie field + httpRequestLayer->removeField(PCPP_HTTP_COOKIE_FIELD); + // add x-forwarded-for field + pcpp::HeaderField* xForwardedForField = httpRequestLayer->insertField( + httpRequestLayer->getFieldByName(PCPP_HTTP_HOST_FIELD), "X-Forwarded-For", + "1.1.1.1"); + // add cache-control field + httpRequestLayer->insertField(xForwardedForField, "Cache-Control", + "max-age=0"); + + // create a new vlan layer + pcpp::VlanLayer newVlanLayer(123, false, 1, PCPP_ETHERTYPE_IP); + + // add the vlan layer to the packet after the existing Ethernet layer + parsedPacket.insertLayer(ethernetLayer, &newVlanLayer); + + // compute all calculated fields + parsedPacket.computeCalculateFields(); + + // write the modified packet to a pcap file + pcpp::PcapFileWriterDevice writer("1_modified_packet.pcap"); + writer.open(); + writer.writePacket(*(parsedPacket.getRawPacket())); + writer.close(); + + // Packet Creation + // ~~~~~~~~~~~~~~~ + + // create a new Ethernet layer + pcpp::EthLayer newEthernetLayer(pcpp::MacAddress("00:50:43:11:22:33"), + pcpp::MacAddress("aa:bb:cc:dd:ee:ff")); + + // create a new IPv4 layer + pcpp::IPv4Layer newIPLayer(pcpp::IPv4Address("192.168.1.1"), + pcpp::IPv4Address("10.0.0.1")); + newIPLayer.getIPv4Header()->ipId = pcpp::hostToNet16(2000); + newIPLayer.getIPv4Header()->timeToLive = 64; + + // create a new UDP layer + pcpp::UdpLayer newUdpLayer(12345, 53); + + // create a new DNS layer + pcpp::DnsLayer newDnsLayer; + newDnsLayer.addQuery("www.ebay.com", pcpp::DNS_TYPE_A, pcpp::DNS_CLASS_IN); + + // create a packet with initial capacity of 100 bytes (will grow automatically + // if needed) + pcpp::Packet newPacket(100); + + // add all the layers we created + newPacket.addLayer(&newEthernetLayer); + newPacket.addLayer(&newIPLayer); + newPacket.addLayer(&newUdpLayer); + newPacket.addLayer(&newDnsLayer); + + // compute all calculated fields + newPacket.computeCalculateFields(); + + // write the new packet to a pcap file + pcpp::PcapFileWriterDevice writer2("1_new_packet.pcap"); + writer2.open(); + writer2.writePacket(*(newPacket.getRawPacket())); + writer2.close(); } diff --git a/Examples/Tutorials/Tutorial-PacketParsing/main.cpp b/Examples/Tutorials/Tutorial-PacketParsing/main.cpp index 15280ffef0..4d3f5c46e4 100644 --- a/Examples/Tutorials/Tutorial-PacketParsing/main.cpp +++ b/Examples/Tutorials/Tutorial-PacketParsing/main.cpp @@ -1,195 +1,213 @@ -#include -#include "stdlib.h" -#include "SystemUtils.h" -#include "Packet.h" #include "EthLayer.h" -#include "IPv4Layer.h" -#include "TcpLayer.h" #include "HttpLayer.h" +#include "IPv4Layer.h" +#include "Packet.h" #include "PcapFileDevice.h" +#include "SystemUtils.h" +#include "TcpLayer.h" +#include "stdlib.h" +#include -std::string getProtocolTypeAsString(pcpp::ProtocolType protocolType) -{ - switch (protocolType) - { - case pcpp::Ethernet: - return "Ethernet"; - case pcpp::IPv4: - return "IPv4"; - case pcpp::TCP: - return "TCP"; - case pcpp::HTTPRequest: - case pcpp::HTTPResponse: - return "HTTP"; - default: - return "Unknown"; - } +std::string getProtocolTypeAsString(pcpp::ProtocolType protocolType) { + switch (protocolType) { + case pcpp::Ethernet: + return "Ethernet"; + case pcpp::IPv4: + return "IPv4"; + case pcpp::TCP: + return "TCP"; + case pcpp::HTTPRequest: + case pcpp::HTTPResponse: + return "HTTP"; + default: + return "Unknown"; + } } -std::string printTcpFlags(pcpp::TcpLayer* tcpLayer) -{ - std::string result = ""; - if (tcpLayer->getTcpHeader()->synFlag == 1) - result += "SYN "; - if (tcpLayer->getTcpHeader()->ackFlag == 1) - result += "ACK "; - if (tcpLayer->getTcpHeader()->pshFlag == 1) - result += "PSH "; - if (tcpLayer->getTcpHeader()->cwrFlag == 1) - result += "CWR "; - if (tcpLayer->getTcpHeader()->urgFlag == 1) - result += "URG "; - if (tcpLayer->getTcpHeader()->eceFlag == 1) - result += "ECE "; - if (tcpLayer->getTcpHeader()->rstFlag == 1) - result += "RST "; - if (tcpLayer->getTcpHeader()->finFlag == 1) - result += "FIN "; - - return result; +std::string printTcpFlags(pcpp::TcpLayer* tcpLayer) { + std::string result = ""; + if (tcpLayer->getTcpHeader()->synFlag == 1) + result += "SYN "; + if (tcpLayer->getTcpHeader()->ackFlag == 1) + result += "ACK "; + if (tcpLayer->getTcpHeader()->pshFlag == 1) + result += "PSH "; + if (tcpLayer->getTcpHeader()->cwrFlag == 1) + result += "CWR "; + if (tcpLayer->getTcpHeader()->urgFlag == 1) + result += "URG "; + if (tcpLayer->getTcpHeader()->eceFlag == 1) + result += "ECE "; + if (tcpLayer->getTcpHeader()->rstFlag == 1) + result += "RST "; + if (tcpLayer->getTcpHeader()->finFlag == 1) + result += "FIN "; + + return result; } -std::string printTcpOptionType(pcpp::TcpOptionType optionType) -{ - switch (optionType) - { - case pcpp::PCPP_TCPOPT_NOP: - return "NOP"; - case pcpp::PCPP_TCPOPT_TIMESTAMP: - return "Timestamp"; - default: - return "Other"; - } +std::string printTcpOptionType(pcpp::TcpOptionType optionType) { + switch (optionType) { + case pcpp::PCPP_TCPOPT_NOP: + return "NOP"; + case pcpp::PCPP_TCPOPT_TIMESTAMP: + return "Timestamp"; + default: + return "Other"; + } } -std::string printHttpMethod(pcpp::HttpRequestLayer::HttpMethod httpMethod) -{ - switch (httpMethod) - { - case pcpp::HttpRequestLayer::HttpGET: - return "GET"; - case pcpp::HttpRequestLayer::HttpPOST: - return "POST"; - default: - return "Other"; - } +std::string printHttpMethod(pcpp::HttpRequestLayer::HttpMethod httpMethod) { + switch (httpMethod) { + case pcpp::HttpRequestLayer::HttpGET: + return "GET"; + case pcpp::HttpRequestLayer::HttpPOST: + return "POST"; + default: + return "Other"; + } } -int main(int argc, char* argv[]) -{ - // use the IFileReaderDevice interface to automatically identify file type (pcap/pcap-ng) - // and create an interface instance that both readers implement - pcpp::IFileReaderDevice* reader = pcpp::IFileReaderDevice::getReader("1_http_packet.pcap"); - - // verify that a reader interface was indeed created - if (reader == nullptr) - { - std::cerr << "Cannot determine reader for file type" << std::endl; - return 1; - } - - // open the reader for reading - if (!reader->open()) - { - std::cerr << "Cannot open input.pcap for reading" << std::endl; - return 1; - } - - // read the first (and only) packet from the file - pcpp::RawPacket rawPacket; - if (!reader->getNextPacket(rawPacket)) - { - std::cerr << "Couldn't read the first packet in the file" << std::endl; - return 1; - } - - // close the file reader, we don't need it anymore - reader->close(); - - // parse the raw packet into a parsed packet - pcpp::Packet parsedPacket(&rawPacket); - - // first let's go over the layers one by one and find out its type, its total length, its header length and its payload length - for (pcpp::Layer* curLayer = parsedPacket.getFirstLayer(); curLayer != nullptr; curLayer = curLayer->getNextLayer()) - { - std::cout - << "Layer type: " << getProtocolTypeAsString(curLayer->getProtocol()) << "; " // get layer type - << "Total data: " << curLayer->getDataLen() << " [bytes]; " // get total length of the layer - << "Layer data: " << curLayer->getHeaderLen() << " [bytes]; " // get the header length of the layer - << "Layer payload: " << curLayer->getLayerPayloadSize() << " [bytes]" // get the payload length of the layer (equals total length minus header length) - << std::endl; - } - - // now let's get the Ethernet layer - pcpp::EthLayer* ethernetLayer = parsedPacket.getLayerOfType(); - if (ethernetLayer == nullptr) - { - std::cerr << "Something went wrong, couldn't find Ethernet layer" << std::endl; - return 1; - } - - // print the source and dest MAC addresses and the Ether type - std::cout << std::endl - << "Source MAC address: " << ethernetLayer->getSourceMac() << std::endl - << "Destination MAC address: " << ethernetLayer->getDestMac() << std::endl - << "Ether type = 0x" << std::hex << pcpp::netToHost16(ethernetLayer->getEthHeader()->etherType) << std::endl; - - // let's get the IPv4 layer - pcpp::IPv4Layer* ipLayer = parsedPacket.getLayerOfType(); - if (ipLayer == nullptr) - { - std::cerr << "Something went wrong, couldn't find IPv4 layer" << std::endl; - return 1; - } - - // print source and dest IP addresses, IP ID and TTL - std::cout << std::endl - << "Source IP address: " << ipLayer->getSrcIPAddress() << std::endl - << "Destination IP address: " << ipLayer->getDstIPAddress() << std::endl - << "IP ID: 0x" << std::hex << pcpp::netToHost16(ipLayer->getIPv4Header()->ipId) << std::endl - << "TTL: " << std::dec << (int)ipLayer->getIPv4Header()->timeToLive << std::endl; - - // let's get the TCP layer - pcpp::TcpLayer* tcpLayer = parsedPacket.getLayerOfType(); - if (tcpLayer == nullptr) - { - std::cerr << "Something went wrong, couldn't find TCP layer" << std::endl; - return 1; - } - - // print TCP source and dest ports, window size, and the TCP flags that are set in this layer - std::cout << std::endl - << "Source TCP port: " << tcpLayer->getSrcPort() << std::endl - << "Destination TCP port: " << tcpLayer->getDstPort() << std::endl - << "Window size: " << pcpp::netToHost16(tcpLayer->getTcpHeader()->windowSize) << std::endl - << "TCP flags: " << printTcpFlags(tcpLayer) << std::endl; - - std::cout << "TCP options: "; - for (pcpp::TcpOption tcpOption = tcpLayer->getFirstTcpOption(); tcpOption.isNotNull(); tcpOption = tcpLayer->getNextTcpOption(tcpOption)) - { - std::cout << printTcpOptionType(tcpOption.getTcpOptionType()) << " "; - } - std::cout << std::endl; - - // let's get the HTTP request layer - pcpp::HttpRequestLayer* httpRequestLayer = parsedPacket.getLayerOfType(); - if (httpRequestLayer == nullptr) - { - std::cerr << "Something went wrong, couldn't find HTTP request layer" << std::endl; - return 1; - } - - // print HTTP method and URI. Both appear in the first line of the HTTP request - std::cout << std::endl - << "HTTP method: " << printHttpMethod(httpRequestLayer->getFirstLine()->getMethod()) << std::endl - << "HTTP URI: " << httpRequestLayer->getFirstLine()->getUri() << std::endl; - - // print values of the following HTTP field: Host, User-Agent and Cookie - std::cout - << "HTTP host: " << httpRequestLayer->getFieldByName(PCPP_HTTP_HOST_FIELD)->getFieldValue() << std::endl - << "HTTP user-agent: " << httpRequestLayer->getFieldByName(PCPP_HTTP_USER_AGENT_FIELD)->getFieldValue() << std::endl - << "HTTP cookie: " << httpRequestLayer->getFieldByName(PCPP_HTTP_COOKIE_FIELD)->getFieldValue() << std::endl; - - // print the full URL of this request - std::cout << "HTTP full URL: " << httpRequestLayer->getUrl() << std::endl; +int main(int argc, char* argv[]) { + // use the IFileReaderDevice interface to automatically identify file type + // (pcap/pcap-ng) and create an interface instance that both readers implement + pcpp::IFileReaderDevice* reader = + pcpp::IFileReaderDevice::getReader("1_http_packet.pcap"); + + // verify that a reader interface was indeed created + if (reader == nullptr) { + std::cerr << "Cannot determine reader for file type" << std::endl; + return 1; + } + + // open the reader for reading + if (!reader->open()) { + std::cerr << "Cannot open input.pcap for reading" << std::endl; + return 1; + } + + // read the first (and only) packet from the file + pcpp::RawPacket rawPacket; + if (!reader->getNextPacket(rawPacket)) { + std::cerr << "Couldn't read the first packet in the file" << std::endl; + return 1; + } + + // close the file reader, we don't need it anymore + reader->close(); + + // parse the raw packet into a parsed packet + pcpp::Packet parsedPacket(&rawPacket); + + // first let's go over the layers one by one and find out its type, its total + // length, its header length and its payload length + for (pcpp::Layer* curLayer = parsedPacket.getFirstLayer(); + curLayer != nullptr; curLayer = curLayer->getNextLayer()) { + std::cout << "Layer type: " + << getProtocolTypeAsString(curLayer->getProtocol()) + << "; " // get layer type + << "Total data: " << curLayer->getDataLen() + << " [bytes]; " // get total length of the layer + << "Layer data: " << curLayer->getHeaderLen() + << " [bytes]; " // get the header length of the layer + << "Layer payload: " << curLayer->getLayerPayloadSize() + << " [bytes]" // get the payload length of the layer (equals total + // length minus header length) + << std::endl; + } + + // now let's get the Ethernet layer + pcpp::EthLayer* ethernetLayer = parsedPacket.getLayerOfType(); + if (ethernetLayer == nullptr) { + std::cerr << "Something went wrong, couldn't find Ethernet layer" + << std::endl; + return 1; + } + + // print the source and dest MAC addresses and the Ether type + std::cout << std::endl + << "Source MAC address: " << ethernetLayer->getSourceMac() + << std::endl + << "Destination MAC address: " << ethernetLayer->getDestMac() + << std::endl + << "Ether type = 0x" << std::hex + << pcpp::netToHost16(ethernetLayer->getEthHeader()->etherType) + << std::endl; + + // let's get the IPv4 layer + pcpp::IPv4Layer* ipLayer = parsedPacket.getLayerOfType(); + if (ipLayer == nullptr) { + std::cerr << "Something went wrong, couldn't find IPv4 layer" << std::endl; + return 1; + } + + // print source and dest IP addresses, IP ID and TTL + std::cout << std::endl + << "Source IP address: " << ipLayer->getSrcIPAddress() << std::endl + << "Destination IP address: " << ipLayer->getDstIPAddress() + << std::endl + << "IP ID: 0x" << std::hex + << pcpp::netToHost16(ipLayer->getIPv4Header()->ipId) << std::endl + << "TTL: " << std::dec << (int)ipLayer->getIPv4Header()->timeToLive + << std::endl; + + // let's get the TCP layer + pcpp::TcpLayer* tcpLayer = parsedPacket.getLayerOfType(); + if (tcpLayer == nullptr) { + std::cerr << "Something went wrong, couldn't find TCP layer" << std::endl; + return 1; + } + + // print TCP source and dest ports, window size, and the TCP flags that are + // set in this layer + std::cout << std::endl + << "Source TCP port: " << tcpLayer->getSrcPort() << std::endl + << "Destination TCP port: " << tcpLayer->getDstPort() << std::endl + << "Window size: " + << pcpp::netToHost16(tcpLayer->getTcpHeader()->windowSize) + << std::endl + << "TCP flags: " << printTcpFlags(tcpLayer) << std::endl; + + std::cout << "TCP options: "; + for (pcpp::TcpOption tcpOption = tcpLayer->getFirstTcpOption(); + tcpOption.isNotNull(); + tcpOption = tcpLayer->getNextTcpOption(tcpOption)) { + std::cout << printTcpOptionType(tcpOption.getTcpOptionType()) << " "; + } + std::cout << std::endl; + + // let's get the HTTP request layer + pcpp::HttpRequestLayer* httpRequestLayer = + parsedPacket.getLayerOfType(); + if (httpRequestLayer == nullptr) { + std::cerr << "Something went wrong, couldn't find HTTP request layer" + << std::endl; + return 1; + } + + // print HTTP method and URI. Both appear in the first line of the HTTP + // request + std::cout << std::endl + << "HTTP method: " + << printHttpMethod(httpRequestLayer->getFirstLine()->getMethod()) + << std::endl + << "HTTP URI: " << httpRequestLayer->getFirstLine()->getUri() + << std::endl; + + // print values of the following HTTP field: Host, User-Agent and Cookie + std::cout + << "HTTP host: " + << httpRequestLayer->getFieldByName(PCPP_HTTP_HOST_FIELD)->getFieldValue() + << std::endl + << "HTTP user-agent: " + << httpRequestLayer->getFieldByName(PCPP_HTTP_USER_AGENT_FIELD) + ->getFieldValue() + << std::endl + << "HTTP cookie: " + << httpRequestLayer->getFieldByName(PCPP_HTTP_COOKIE_FIELD) + ->getFieldValue() + << std::endl; + + // print the full URL of this request + std::cout << "HTTP full URL: " << httpRequestLayer->getUrl() << std::endl; } diff --git a/Examples/Tutorials/Tutorial-PcapFiles/main.cpp b/Examples/Tutorials/Tutorial-PcapFiles/main.cpp index 89bff2f3d7..4432ad39d3 100644 --- a/Examples/Tutorials/Tutorial-PcapFiles/main.cpp +++ b/Examples/Tutorials/Tutorial-PcapFiles/main.cpp @@ -1,93 +1,95 @@ -#include -#include "stdlib.h" #include "PcapFileDevice.h" +#include "stdlib.h" +#include /** * main method of the application */ -int main(int argc, char* argv[]) -{ - // use the IFileReaderDevice interface to automatically identify file type (pcap/pcap-ng) - // and create an interface instance that both readers implement - pcpp::IFileReaderDevice* reader = pcpp::IFileReaderDevice::getReader("input.pcap"); - - // verify that a reader interface was indeed created - if (reader == nullptr) - { - std::cerr << "Cannot determine reader for file type" << std::endl; - return 1; - } - - // open the reader for reading - if (!reader->open()) - { - std::cerr << "Cannot open input.pcap for reading" << std::endl; - return 1; - } - - // create a pcap file writer. Specify file name and link type of all packets that - // will be written to it - pcpp::PcapFileWriterDevice pcapWriter("output.pcap", pcpp::LINKTYPE_ETHERNET); - - // try to open the file for writing - if (!pcapWriter.open()) - { - std::cerr << "Cannot open output.pcap for writing" << std::endl; - return 1; - } - - // create a pcap-ng file writer. Specify file name. Link type is not necessary because - // pcap-ng files can store multiple link types in the same file - pcpp::PcapNgFileWriterDevice pcapNgWriter("output.pcapng"); - - // try to open the file for writing - if (!pcapNgWriter.open()) - { - std::cerr << "Cannot open output.pcapng for writing" << std::endl; - return 1; - } - - // set a BPF filter for the reader - only packets that match the filter will be read - if (!reader->setFilter("net 98.138.19.88")) - { - std::cerr << "Cannot set filter for file reader" << std::endl; - return 1; - } - - // the packet container - pcpp::RawPacket rawPacket; - - // a while loop that will continue as long as there are packets in the input file - // matching the BPF filter - while (reader->getNextPacket(rawPacket)) - { - // write each packet to both writers - pcapWriter.writePacket(rawPacket); - pcapNgWriter.writePacket(rawPacket); - } - - // create the stats object - pcpp::IPcapDevice::PcapStats stats; - - // read stats from reader and print them - reader->getStatistics(stats); - std::cout << "Read " << stats.packetsRecv << " packets successfully and " << stats.packetsDrop << " packets could not be read" << std::endl; - - // read stats from pcap writer and print them - pcapWriter.getStatistics(stats); - std::cout << "Written " << stats.packetsRecv << " packets successfully to pcap writer and " << stats.packetsDrop << " packets could not be written" << std::endl; - - // read stats from pcap-ng writer and print them - pcapNgWriter.getStatistics(stats); - std::cout << "Written " << stats.packetsRecv << " packets successfully to pcap-ng writer and " << stats.packetsDrop << " packets could not be written" << std::endl; - - // close reader - reader->close(); - - // close writers - pcapWriter.close(); - pcapNgWriter.close(); - - // free reader memory because it was created by pcpp::IFileReaderDevice::getReader() - delete reader; +int main(int argc, char* argv[]) { + // use the IFileReaderDevice interface to automatically identify file type + // (pcap/pcap-ng) and create an interface instance that both readers implement + pcpp::IFileReaderDevice* reader = + pcpp::IFileReaderDevice::getReader("input.pcap"); + + // verify that a reader interface was indeed created + if (reader == nullptr) { + std::cerr << "Cannot determine reader for file type" << std::endl; + return 1; + } + + // open the reader for reading + if (!reader->open()) { + std::cerr << "Cannot open input.pcap for reading" << std::endl; + return 1; + } + + // create a pcap file writer. Specify file name and link type of all packets + // that will be written to it + pcpp::PcapFileWriterDevice pcapWriter("output.pcap", pcpp::LINKTYPE_ETHERNET); + + // try to open the file for writing + if (!pcapWriter.open()) { + std::cerr << "Cannot open output.pcap for writing" << std::endl; + return 1; + } + + // create a pcap-ng file writer. Specify file name. Link type is not necessary + // because pcap-ng files can store multiple link types in the same file + pcpp::PcapNgFileWriterDevice pcapNgWriter("output.pcapng"); + + // try to open the file for writing + if (!pcapNgWriter.open()) { + std::cerr << "Cannot open output.pcapng for writing" << std::endl; + return 1; + } + + // set a BPF filter for the reader - only packets that match the filter will + // be read + if (!reader->setFilter("net 98.138.19.88")) { + std::cerr << "Cannot set filter for file reader" << std::endl; + return 1; + } + + // the packet container + pcpp::RawPacket rawPacket; + + // a while loop that will continue as long as there are packets in the input + // file matching the BPF filter + while (reader->getNextPacket(rawPacket)) { + // write each packet to both writers + pcapWriter.writePacket(rawPacket); + pcapNgWriter.writePacket(rawPacket); + } + + // create the stats object + pcpp::IPcapDevice::PcapStats stats; + + // read stats from reader and print them + reader->getStatistics(stats); + std::cout << "Read " << stats.packetsRecv << " packets successfully and " + << stats.packetsDrop << " packets could not be read" << std::endl; + + // read stats from pcap writer and print them + pcapWriter.getStatistics(stats); + std::cout << "Written " << stats.packetsRecv + << " packets successfully to pcap writer and " << stats.packetsDrop + << " packets could not be written" << std::endl; + + // read stats from pcap-ng writer and print them + pcapNgWriter.getStatistics(stats); + std::cout << "Written " << stats.packetsRecv + << " packets successfully to pcap-ng writer and " + << stats.packetsDrop << " packets could not be written" + << std::endl; + + // close reader + reader->close(); + + // close writers + pcapWriter.close(); + pcapNgWriter.close(); + + // free reader memory because it was created by + // pcpp::IFileReaderDevice::getReader() + delete reader; } diff --git a/Packet++/header/ArpLayer.h b/Packet++/header/ArpLayer.h index 21d66412b1..48335cf477 100644 --- a/Packet++/header/ArpLayer.h +++ b/Packet++/header/ArpLayer.h @@ -1,8 +1,8 @@ #ifndef PACKETPP_ARP_LAYER #define PACKETPP_ARP_LAYER -#include "Layer.h" #include "IpAddress.h" +#include "Layer.h" #include "MacAddress.h" /// @file @@ -11,140 +11,162 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ +namespace pcpp { - /** - * @struct arphdr - * Represents an ARP protocol header - */ +/** + * @struct arphdr + * Represents an ARP protocol header + */ #pragma pack(push, 1) - struct arphdr - { - /** Hardware type (HTYPE) */ - uint16_t hardwareType; - /** Protocol type (PTYPE). The permitted PTYPE values share a numbering space with those for EtherType */ - uint16_t protocolType; - /** Hardware address length (HLEN). For IPv4, this has the value 0x0800 */ - uint8_t hardwareSize; - /** Protocol length (PLEN). Length (in octets) of addresses used in the upper layer protocol. (The upper layer protocol specified in PTYPE.) IPv4 address size is 4 */ - uint8_t protocolSize; - /** Specifies the operation that the sender is performing: 1 (::ARP_REQUEST) for request, 2 (::ARP_REPLY) for reply */ - uint16_t opcode; - /** Sender hardware address (SHA) */ - uint8_t senderMacAddr[6]; - /** Sender protocol address (SPA) */ - uint32_t senderIpAddr; - /** Target hardware address (THA) */ - uint8_t targetMacAddr[6]; - /** Target protocol address (TPA) */ - uint32_t targetIpAddr; - }; +struct arphdr { + /** Hardware type (HTYPE) */ + uint16_t hardwareType; + /** Protocol type (PTYPE). The permitted PTYPE values share a numbering space + * with those for EtherType */ + uint16_t protocolType; + /** Hardware address length (HLEN). For IPv4, this has the value 0x0800 */ + uint8_t hardwareSize; + /** Protocol length (PLEN). Length (in octets) of addresses used in the upper + * layer protocol. (The upper layer protocol specified in PTYPE.) IPv4 address + * size is 4 */ + uint8_t protocolSize; + /** Specifies the operation that the sender is performing: 1 (::ARP_REQUEST) + * for request, 2 (::ARP_REPLY) for reply */ + uint16_t opcode; + /** Sender hardware address (SHA) */ + uint8_t senderMacAddr[6]; + /** Sender protocol address (SPA) */ + uint32_t senderIpAddr; + /** Target hardware address (THA) */ + uint8_t targetMacAddr[6]; + /** Target protocol address (TPA) */ + uint32_t targetIpAddr; +}; #pragma pack(pop) - /** - * An enum for ARP message type - */ - enum ArpOpcode - { - ARP_REQUEST = 0x0001, ///< ARP request - ARP_REPLY = 0x0002 ///< ARP reply (response) - }; - - /** - * @class ArpLayer - * Represents an ARP protocol layer. Currently only IPv4 ARP messages are supported - */ - class ArpLayer : public Layer - { - public: - /** - * A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data (will be casted to @ref arphdr) - * @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 - */ - ArpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) { m_Protocol = ARP; m_DataLen = sizeof(arphdr); } - - /** - * A constructor that allocates a new ARP header - * @param[in] opCode ARP message type (ARP request or ARP reply) - * @param[in] senderMacAddr The sender MAC address (will be put in arphdr#senderMacAddr) - * @param[in] targetMacAddr The target MAC address (will be put in arphdr#targetMacAddr) - * @param[in] senderIpAddr The sender IP address (will be put in arphdr#senderIpAddr) - * @param[in] targetIpAddr The target IP address (will be put in arphdr#targetIpAddr) - */ - ArpLayer(ArpOpcode opCode, const MacAddress& senderMacAddr, const MacAddress& targetMacAddr, const IPv4Address& senderIpAddr, const IPv4Address& targetIpAddr); - - ~ArpLayer() {} - - /** - * Get a pointer to the ARP header. Notice this points directly to the data, so every change will change the actual packet data - * @return A pointer to the @ref arphdr - */ - inline arphdr* getArpHeader() const { return (arphdr*)m_Data; } - - /** - * Get the sender hardware address (SHA) in the form of MacAddress - * @return A MacAddress containing the sender hardware address (SHA) - */ - inline MacAddress getSenderMacAddress() const { return MacAddress(getArpHeader()->senderMacAddr); } - - /** - * Get the target hardware address (THA) in the form of MacAddress - * @return A MacAddress containing the target hardware address (THA) - */ - inline MacAddress getTargetMacAddress() const { return MacAddress(getArpHeader()->targetMacAddr); } - - /** - * Get the sender protocol address (SPA) in the form of IPv4Address - * @return An IPv4Address containing the sender protocol address (SPA) - */ - inline IPv4Address getSenderIpAddr() const { return getArpHeader()->senderIpAddr; } - - /** - * Get the target protocol address (TPA) in the form of IPv4Address - * @return An IPv4Address containing the target protocol address (TPA) - */ - inline IPv4Address getTargetIpAddr() const { return getArpHeader()->targetIpAddr; } - - // implement abstract methods - - /** - * Does nothing for this layer (ArpLayer is always last) - */ - void parseNextLayer() {} - - /** - * @return The size of @ref arphdr - */ - size_t getHeaderLen() const { return sizeof(arphdr); } - - /** - * Calculate the following fields: - * - @ref arphdr#hardwareType = Ethernet (1) - * - @ref arphdr#hardwareSize = 6 - * - @ref arphdr#protocolType = ETHERTYPE_IP (assume IPv4 over ARP) - * - @ref arphdr#protocolSize = 4 (assume IPv4 over ARP) - * - if it's an ARP request: @ref arphdr#targetMacAddr = MacAddress("00:00:00:00:00:00") - */ - void computeCalculateFields(); - - /** - * Is this packet an ARP request? - */ - bool isRequest() const; - - /** - * Is this packet an ARP reply? - */ - bool isReply() const; - - std::string toString() const; - - OsiModelLayer getOsiModelLayer() const { return OsiModelNetworkLayer; } - }; +/** + * An enum for ARP message type + */ +enum ArpOpcode { + ARP_REQUEST = 0x0001, ///< ARP request + ARP_REPLY = 0x0002 ///< ARP reply (response) +}; + +/** + * @class ArpLayer + * Represents an ARP protocol layer. Currently only IPv4 ARP messages are + * supported + */ +class ArpLayer : public Layer { + public: + /** + * A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data (will be casted to @ref arphdr) + * @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 + */ + ArpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : Layer(data, dataLen, prevLayer, packet) { + m_Protocol = ARP; + m_DataLen = sizeof(arphdr); + } + + /** + * A constructor that allocates a new ARP header + * @param[in] opCode ARP message type (ARP request or ARP reply) + * @param[in] senderMacAddr The sender MAC address (will be put in + * arphdr#senderMacAddr) + * @param[in] targetMacAddr The target MAC address (will be put in + * arphdr#targetMacAddr) + * @param[in] senderIpAddr The sender IP address (will be put in + * arphdr#senderIpAddr) + * @param[in] targetIpAddr The target IP address (will be put in + * arphdr#targetIpAddr) + */ + ArpLayer(ArpOpcode opCode, const MacAddress& senderMacAddr, + const MacAddress& targetMacAddr, const IPv4Address& senderIpAddr, + const IPv4Address& targetIpAddr); + + ~ArpLayer() {} + + /** + * Get a pointer to the ARP header. Notice this points directly to the data, + * so every change will change the actual packet data + * @return A pointer to the @ref arphdr + */ + inline arphdr* getArpHeader() const { return (arphdr*)m_Data; } + + /** + * Get the sender hardware address (SHA) in the form of MacAddress + * @return A MacAddress containing the sender hardware address (SHA) + */ + inline MacAddress getSenderMacAddress() const { + return MacAddress(getArpHeader()->senderMacAddr); + } + + /** + * Get the target hardware address (THA) in the form of MacAddress + * @return A MacAddress containing the target hardware address (THA) + */ + inline MacAddress getTargetMacAddress() const { + return MacAddress(getArpHeader()->targetMacAddr); + } + + /** + * Get the sender protocol address (SPA) in the form of IPv4Address + * @return An IPv4Address containing the sender protocol address (SPA) + */ + inline IPv4Address getSenderIpAddr() const { + return getArpHeader()->senderIpAddr; + } + + /** + * Get the target protocol address (TPA) in the form of IPv4Address + * @return An IPv4Address containing the target protocol address (TPA) + */ + inline IPv4Address getTargetIpAddr() const { + return getArpHeader()->targetIpAddr; + } + + // implement abstract methods + + /** + * Does nothing for this layer (ArpLayer is always last) + */ + void parseNextLayer() {} + + /** + * @return The size of @ref arphdr + */ + size_t getHeaderLen() const { return sizeof(arphdr); } + + /** + * Calculate the following fields: + * - @ref arphdr#hardwareType = Ethernet (1) + * - @ref arphdr#hardwareSize = 6 + * - @ref arphdr#protocolType = ETHERTYPE_IP (assume IPv4 over ARP) + * - @ref arphdr#protocolSize = 4 (assume IPv4 over ARP) + * - if it's an ARP request: @ref arphdr#targetMacAddr = + * MacAddress("00:00:00:00:00:00") + */ + void computeCalculateFields(); + + /** + * Is this packet an ARP request? + */ + bool isRequest() const; + + /** + * Is this packet an ARP reply? + */ + bool isReply() const; + + std::string toString() const; + + OsiModelLayer getOsiModelLayer() const { return OsiModelNetworkLayer; } +}; } // namespace pcpp #endif /* PACKETPP_ARP_LAYER */ diff --git a/Packet++/header/BgpLayer.h b/Packet++/header/BgpLayer.h index 31cb756376..7d6a96ed1d 100644 --- a/Packet++/header/BgpLayer.h +++ b/Packet++/header/BgpLayer.h @@ -1,678 +1,758 @@ #ifndef PACKETPP_BGP_LAYER #define PACKETPP_BGP_LAYER -#include -#include "Layer.h" #include "IpAddress.h" +#include "Layer.h" +#include /** * @file - * This file contains classes for parsing, creating and editing Border Gateway Protocol (BGP) version 4 packets. - * It contains an abstract class named BgpLayer which has common functionality and 5 inherited classes that - * represent the different BGP message types: OPEN, UPDATE, NOTIFICATION, KEEPALIVE and ROUTE-REFRESH. - * Each of these classes contains unique functionality for parsing. creating and editing of these message. + * This file contains classes for parsing, creating and editing Border Gateway + * Protocol (BGP) version 4 packets. It contains an abstract class named + * BgpLayer which has common functionality and 5 inherited classes that + * represent the different BGP message types: OPEN, UPDATE, NOTIFICATION, + * KEEPALIVE and ROUTE-REFRESH. Each of these classes contains unique + * functionality for parsing. creating and editing of these message. */ /** * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ +namespace pcpp { /** * @class BgpLayer - * Represents Border Gateway Protocol (BGP) v4 protocol layer. This is an abstract class that cannot be instantiated, - * and contains functionality which is common to all BGP message types. + * Represents Border Gateway Protocol (BGP) v4 protocol layer. This is an + * abstract class that cannot be instantiated, and contains functionality which + * is common to all BGP message types. */ -class BgpLayer : public Layer -{ -public: - - /** - * An enum representing BGP message types - */ - enum BgpMessageType - { - /** BGP OPEN message */ - Open = 1, - /** BGP UPDATE message */ - Update = 2, - /** BGP NOTIFICATION message */ - Notification = 3, - /** BGP KEEPALIVE message */ - Keepalive = 4, - /** BGP ROUTE-REFRESH message */ - RouteRefresh = 5, - }; - - /** - * @struct bgp_common_header - * Represents the common fields of a BGP 4 message - */ - #pragma pack(push, 1) - struct bgp_common_header - { - /** 16-octet marker */ - uint8_t marker[16]; - /** Total length of the message, including the header */ - uint16_t length; - /** BGP message type */ - uint8_t messageType; - }; - #pragma pack(pop) - - /** - * @return BGP message type - */ - virtual BgpMessageType getBgpMessageType() const = 0; - - /** - * @return BGP message type as string. Return value can be one of the following: - * "OPEN", "UPDATE", "NOTIFICATION", "KEEPALIVE", "ROUTE-REFRESH", "Unknown" - */ - std::string getMessageTypeAsString() const; - - /** - * A static method that checks whether a source or dest port match those associated with the BGP protocol - * @param[in] portSrc Source port number to check - * @param[in] portDst Dest port number to check - * @return True if the source or dest port match those associated with the BGP protocol - */ - static bool isBgpPort(uint16_t portSrc, uint16_t portDst) { return portSrc == 179 || portDst == 179; } - - /** - * A method that creates a BGP layer from 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 - * @return A newly allocated BGP layer of one of the following types (according to the message type): - * BgpOpenMessageLayer, BgpUpdateMessageLayer, BgpNotificationMessageLayer, BgpKeepaliveMessageLayer, - * BgpRouteRefreshMessageLayer - */ - static BgpLayer* parseBgpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); - - // implement abstract methods - - /** - * @return The size of the BGP message - */ - size_t getHeaderLen() const; - - /** - * Multiple BGP messages can reside in a single packet, and the only layer that can come after a BGP message - * is another BGP message. This method checks for remaining data and parses it as another BGP layer - */ - void parseNextLayer(); - - std::string toString() const; - - OsiModelLayer getOsiModelLayer() const { return OsiModelApplicationLayer; } - - /** - * Calculates the basic BGP fields: - * - Set marker to all ones - * - Set message type value - * - Set message length - */ - void computeCalculateFields(); - -protected: - - // protected c'tors, this class cannot be instantiated by users - BgpLayer() {} - BgpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) { m_Protocol = BGP; } - - bgp_common_header* getBasicHeader() const { return (bgp_common_header*)m_Data; } - - void setBgpFields(size_t messageLen = 0); +class BgpLayer : public Layer { + public: + /** + * An enum representing BGP message types + */ + enum BgpMessageType { + /** BGP OPEN message */ + Open = 1, + /** BGP UPDATE message */ + Update = 2, + /** BGP NOTIFICATION message */ + Notification = 3, + /** BGP KEEPALIVE message */ + Keepalive = 4, + /** BGP ROUTE-REFRESH message */ + RouteRefresh = 5, + }; +/** + * @struct bgp_common_header + * Represents the common fields of a BGP 4 message + */ +#pragma pack(push, 1) + struct bgp_common_header { + /** 16-octet marker */ + uint8_t marker[16]; + /** Total length of the message, including the header */ + uint16_t length; + /** BGP message type */ + uint8_t messageType; + }; +#pragma pack(pop) + + /** + * @return BGP message type + */ + virtual BgpMessageType getBgpMessageType() const = 0; + + /** + * @return BGP message type as string. Return value can be one of the + * following: "OPEN", "UPDATE", "NOTIFICATION", "KEEPALIVE", "ROUTE-REFRESH", + * "Unknown" + */ + std::string getMessageTypeAsString() const; + + /** + * A static method that checks whether a source or dest port match those + * associated with the BGP protocol + * @param[in] portSrc Source port number to check + * @param[in] portDst Dest port number to check + * @return True if the source or dest port match those associated with the BGP + * protocol + */ + static bool isBgpPort(uint16_t portSrc, uint16_t portDst) { + return portSrc == 179 || portDst == 179; + } + + /** + * A method that creates a BGP layer from 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 + * @return A newly allocated BGP layer of one of the following types + * (according to the message type): BgpOpenMessageLayer, + * BgpUpdateMessageLayer, BgpNotificationMessageLayer, + * BgpKeepaliveMessageLayer, BgpRouteRefreshMessageLayer + */ + static BgpLayer* parseBgpLayer(uint8_t* data, size_t dataLen, + Layer* prevLayer, Packet* packet); + + // implement abstract methods + + /** + * @return The size of the BGP message + */ + size_t getHeaderLen() const; + + /** + * Multiple BGP messages can reside in a single packet, and the only layer + * that can come after a BGP message is another BGP message. This method + * checks for remaining data and parses it as another BGP layer + */ + void parseNextLayer(); + + std::string toString() const; + + OsiModelLayer getOsiModelLayer() const { return OsiModelApplicationLayer; } + + /** + * Calculates the basic BGP fields: + * - Set marker to all ones + * - Set message type value + * - Set message length + */ + void computeCalculateFields(); + + protected: + // protected c'tors, this class cannot be instantiated by users + BgpLayer() {} + BgpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : Layer(data, dataLen, prevLayer, packet) { + m_Protocol = BGP; + } + + bgp_common_header* getBasicHeader() const { + return (bgp_common_header*)m_Data; + } + + void setBgpFields(size_t messageLen = 0); }; - - /** * @class BgpOpenMessageLayer * Represents a BGP v4 OPEN message */ -class BgpOpenMessageLayer : public BgpLayer -{ -public: - - /** - * @struct bgp_open_message - * BGP OPEN message structure - */ - #pragma pack(push, 1) - typedef struct bgp_open_message : bgp_common_header - { - /** BGP version number */ - uint8_t version; - /** Autonomous System number of the sender */ - uint16_t myAutonomousSystem; - /** The number of seconds the sender proposes for the value of the Hold Timer */ - uint16_t holdTime; - /** BGP Identifier of the sender */ - uint32_t bgpId; - /** The total length of the Optional Parameters field */ - uint8_t optionalParameterLength; - } bgp_open_message; - #pragma pack(pop) - - /** - * @struct optional_parameter - * A structure that represents BGP OPEN message optional parameters - */ - struct optional_parameter - { - /** Parameter type */ - uint8_t type; - /** Parameter length */ - uint8_t length; - /** Parameter data */ - uint8_t value[32]; - - /** - * A default c'tor that zeroes all data - */ - optional_parameter() {} - - /** - * A c'tor that initializes the values of the struct - * @param[in] typeVal Parameter type value - * @param[in] valueAsHexString Parameter data as hex string. The length field will be set accordingly. - * If this parameter is not a valid hex string the data will remain zeroed and length will be also zero - */ - optional_parameter(uint8_t typeVal, const std::string& valueAsHexString); - }; - - /** - * 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 - */ - BgpOpenMessageLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : BgpLayer(data, dataLen, prevLayer, packet) {} - - /** - * A c'tor that creates a new BGP OPEN message - * @param[in] myAutonomousSystem The Autonomous System number of the sender - * @param[in] holdTime The number of seconds the sender proposes for the value of the Hold Timer - * @param[in] bgpId The BGP Identifier of the sender - * @param[in] optionalParams A vector of optional parameters. This parameter is optional and if not provided no parameters will be - * set on the message - */ - BgpOpenMessageLayer(uint16_t myAutonomousSystem, uint16_t holdTime, const IPv4Address& bgpId, - const std::vector& optionalParams = std::vector()); - - /** - * Get a pointer to the open message data. Notice this points directly to the data, so any change will modify the actual packet data - * @return A pointer to a bgp_open_message structure containing the data - */ - bgp_open_message* getOpenMsgHeader() const { return (bgp_open_message*)m_Data; } - - /** - * @return The BGP identifier as IPv4Address object - */ - IPv4Address getBgpId() const { return IPv4Address(getOpenMsgHeader()->bgpId); } - - /** - * Set the BGP identifier - * @param[in] newBgpId BGP identifier to set. If value is not a valid IPv4 address it won't be set - */ - void setBgpId(const IPv4Address& newBgpId); - - /** - * Get a vector of the optional parameters in the message - * @param[out] optionalParameters The vector where the optional parameters will be written to. This method doesn't remove any - * existing data on this vector before pushing data to it - */ - void getOptionalParameters(std::vector& optionalParameters); - - /** - * @return The length in [bytes] of the optional parameters data in the message - */ - size_t getOptionalParametersLength(); - - /** - * Set optional parameters in the message. This method will override all existing optional parameters currently in the message. - * If the input is an empty vector all optional parameters will be cleared. This method automatically sets the - * bgp_common_header#length and the bgp_open_message#optionalParameterLength fields on the message - * @param[in] optionalParameters A vector of new optional parameters to set in the message - * @return True if all optional parameters were set successfully or false otherwise. In case of an error an appropriate message - * will be printed to log - */ - bool setOptionalParameters(const std::vector& optionalParameters); - - /** - * Clear all optional parameters currently in the message. This is equivalent to calling setOptionalParameters() with an empty - * vector as a parameter - * @return True if all optional parameters were successfully cleared or false otherwise. In case of an error an appropriate message - * will be printed to log - */ - bool clearOptionalParameters(); - - // implement abstract methods - - BgpMessageType getBgpMessageType() const { return BgpLayer::Open; } - -private: - - size_t optionalParamsToByteArray(const std::vector& optionalParams, uint8_t* resultByteArr, size_t maxByteArrSize); - +class BgpOpenMessageLayer : public BgpLayer { + public: +/** + * @struct bgp_open_message + * BGP OPEN message structure + */ +#pragma pack(push, 1) + typedef struct bgp_open_message : bgp_common_header { + /** BGP version number */ + uint8_t version; + /** Autonomous System number of the sender */ + uint16_t myAutonomousSystem; + /** The number of seconds the sender proposes for the value of the Hold + * Timer */ + uint16_t holdTime; + /** BGP Identifier of the sender */ + uint32_t bgpId; + /** The total length of the Optional Parameters field */ + uint8_t optionalParameterLength; + } bgp_open_message; +#pragma pack(pop) + + /** + * @struct optional_parameter + * A structure that represents BGP OPEN message optional parameters + */ + struct optional_parameter { + /** Parameter type */ + uint8_t type; + /** Parameter length */ + uint8_t length; + /** Parameter data */ + uint8_t value[32]; + + /** + * A default c'tor that zeroes all data + */ + optional_parameter() {} + + /** + * A c'tor that initializes the values of the struct + * @param[in] typeVal Parameter type value + * @param[in] valueAsHexString Parameter data as hex string. The length + * field will be set accordingly. If this parameter is not a valid hex + * string the data will remain zeroed and length will be also zero + */ + optional_parameter(uint8_t typeVal, const std::string& valueAsHexString); + }; + + /** + * 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 + */ + BgpOpenMessageLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet) + : BgpLayer(data, dataLen, prevLayer, packet) {} + + /** + * A c'tor that creates a new BGP OPEN message + * @param[in] myAutonomousSystem The Autonomous System number of the sender + * @param[in] holdTime The number of seconds the sender proposes for the value + * of the Hold Timer + * @param[in] bgpId The BGP Identifier of the sender + * @param[in] optionalParams A vector of optional parameters. This parameter + * is optional and if not provided no parameters will be set on the message + */ + BgpOpenMessageLayer(uint16_t myAutonomousSystem, uint16_t holdTime, + const IPv4Address& bgpId, + const std::vector& optionalParams = + std::vector()); + + /** + * Get a pointer to the open message data. Notice this points directly to the + * data, so any change will modify the actual packet data + * @return A pointer to a bgp_open_message structure containing the data + */ + bgp_open_message* getOpenMsgHeader() const { + return (bgp_open_message*)m_Data; + } + + /** + * @return The BGP identifier as IPv4Address object + */ + IPv4Address getBgpId() const { + return IPv4Address(getOpenMsgHeader()->bgpId); + } + + /** + * Set the BGP identifier + * @param[in] newBgpId BGP identifier to set. If value is not a valid IPv4 + * address it won't be set + */ + void setBgpId(const IPv4Address& newBgpId); + + /** + * Get a vector of the optional parameters in the message + * @param[out] optionalParameters The vector where the optional parameters + * will be written to. This method doesn't remove any existing data on this + * vector before pushing data to it + */ + void + getOptionalParameters(std::vector& optionalParameters); + + /** + * @return The length in [bytes] of the optional parameters data in the + * message + */ + size_t getOptionalParametersLength(); + + /** + * Set optional parameters in the message. This method will override all + * existing optional parameters currently in the message. If the input is an + * empty vector all optional parameters will be cleared. This method + * automatically sets the bgp_common_header#length and the + * bgp_open_message#optionalParameterLength fields on the message + * @param[in] optionalParameters A vector of new optional parameters to set in + * the message + * @return True if all optional parameters were set successfully or false + * otherwise. In case of an error an appropriate message will be printed to + * log + */ + bool setOptionalParameters( + const std::vector& optionalParameters); + + /** + * Clear all optional parameters currently in the message. This is equivalent + * to calling setOptionalParameters() with an empty vector as a parameter + * @return True if all optional parameters were successfully cleared or false + * otherwise. In case of an error an appropriate message will be printed to + * log + */ + bool clearOptionalParameters(); + + // implement abstract methods + + BgpMessageType getBgpMessageType() const { return BgpLayer::Open; } + + private: + size_t optionalParamsToByteArray( + const std::vector& optionalParams, + uint8_t* resultByteArr, size_t maxByteArrSize); }; - - /** * @class BgpUpdateMessageLayer * Represents a BGP v4 UPDATE message */ -class BgpUpdateMessageLayer : public BgpLayer -{ -public: - - /** - * @struct prefix_and_ip - * A structure that contains IPv4 address and IP address mask (prefix) information. - * It's used to represent BGP Withdrawn Routes and Network Layer Reachability Information (NLRI) - */ - struct prefix_and_ip - { - /** IPv4 address mask, must contain one of the values: 8, 16, 24, 32 */ - uint8_t prefix; - /** IPv4 address */ - IPv4Address ipAddr; - - /** - * A default c'tor that zeroes all data - */ - prefix_and_ip(): prefix(0), ipAddr(IPv4Address::Zero) {} - - /** - * A c'tor that initializes the values of the struct - * @param[in] prefixVal IPv4 address mask value - * @param[in] ipAddrVal IPv4 address - */ - prefix_and_ip(uint8_t prefixVal, const std::string& ipAddrVal): prefix(prefixVal), ipAddr(ipAddrVal) {} - }; - - - /** - * @struct path_attribute - * A structure that represents BGP OPEN message Path Attributes information - */ - struct path_attribute - { - /** Path attribute flags */ - uint8_t flags; - /** Path attribute type */ - uint8_t type; - /** Path attribute length */ - uint8_t length; - /** Path attribute data. Max supported data length is 32 bytes */ - uint8_t data[32]; - - /** - * A default c'tor that zeroes all data - */ - path_attribute() {} - - /** - * A c'tor that initializes the values of the struct - * @param[in] flagsVal Path attribute flags value - * @param[in] typeVal Path attribute type value - * @param[in] dataAsHexString Path attribute data as hex string. The path_attribute#length field will be set accordingly. - * If this parameter is not a valid hex string the data will remain zeroed and length will be also set to zero - */ - path_attribute(uint8_t flagsVal, uint8_t typeVal, const std::string& dataAsHexString); - }; - - /** - * 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 - */ - BgpUpdateMessageLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : BgpLayer(data, dataLen, prevLayer, packet) {} - - /** - * A static method that takes a byte array and detects whether it is a BgpUpdateMessage - * @param[in] data A byte array - * @param[in] dataSize The byte array size (in bytes) - * @return True if the data looks like a valid BgpUpdateMessage layer - */ - static bool isDataValid(const uint8_t *data, size_t dataSize); - - /** - * A c'tor that creates a new BGP UPDATE message - * @param[in] withdrawnRoutes A vector of withdrawn routes data. If left empty (which is the default value) no withdrawn route information will be written to the message - * @param[in] pathAttributes A vector of path attributes data. If left empty (which is the default value) no path attribute information will be written to the message - * @param[in] nlri A vector of network layer reachability data. If left empty (which is the default value) no reachability information will be written to the message - */ - explicit BgpUpdateMessageLayer( - const std::vector& withdrawnRoutes = std::vector(), - const std::vector& pathAttributes = std::vector(), - const std::vector& nlri = std::vector()); - - /** - * Get a pointer to the basic BGP message data. Notice this points directly to the data, so any change will modify the actual packet data - * @return A pointer to a bgp_common_header structure containing the data - */ - bgp_common_header* getBasicMsgHeader() const { return (bgp_common_header*)m_Data; } - - /** - * @return The size in [bytes] of the Withdrawn Routes data - */ - size_t getWithdrawnRoutesLength() const; - - /** - * Get a vector of the Withdrawn Routes currently in the message - * @param[out] withdrawnRoutes A reference to a vector the Withdrawn Routes data will be written to - */ - void getWithdrawnRoutes(std::vector& withdrawnRoutes); - - /** - * Set Withdrawn Routes in this message. This method will override any existing Withdrawn Routes in the message. - * If the input is an empty vector all Withdrawn Routes will be removed. This method automatically sets the - * bgp_common_header#length and the Withdrawn Routes length fields in the message - * @param[in] withdrawnRoutes New Withdrawn Routes to set in the message - * @return True if all Withdrawn Routes were set successfully or false otherwise. In case of an error an appropriate message - * will be printed to log - */ - bool setWithdrawnRoutes(const std::vector& withdrawnRoutes); - - /** - * Clear all Withdrawn Routes data currently in the message. This is equivalent to calling setWithdrawnRoutes() with an empty - * vector as a parameter - * @return True if all Withdrawn Routes were successfully cleared or false otherwise. In case of an error an appropriate message - * will be printed to log - */ - bool clearWithdrawnRoutes(); - - /** - * @return The size in [bytes] of the Path Attributes data - */ - size_t getPathAttributesLength() const; - - /** - * Get a vector of the Path Attributes currently in the message - * @param[out] pathAttributes A reference to a vector the Path Attributes data will be written to - */ - void getPathAttributes(std::vector& pathAttributes); - - /** - * Set Path Attributes in this message. This method will override any existing Path Attributes in the message. - * If the input is an empty vector all Path Attributes will be removed. This method automatically sets the - * bgp_common_header#length and the Path Attributes length fields in the message - * @param[in] pathAttributes New Path Attributes to set in the message - * @return True if all Path Attributes were set successfully or false otherwise. In case of an error an appropriate message - * will be printed to log - */ - bool setPathAttributes(const std::vector& pathAttributes); - - /** - * Clear all Path Attributes data currently in the message. This is equivalent to calling setPathAttributes() with an empty - * vector as a parameter - * @return True if all Path Attributes were successfully cleared or false otherwise. In case of an error an appropriate message - * will be printed to log - */ - bool clearPathAttributes(); - - /** - * @return The size in [bytes] of the Network Layer Reachability Info - */ - size_t getNetworkLayerReachabilityInfoLength() const; - - /** - * Get a vector of the Network Layer Reachability Info currently in the message - * @param[out] nlri A reference to a vector the NLRI data will be written to - */ - void getNetworkLayerReachabilityInfo(std::vector& nlri); - - /** - * Set NLRI data in this message. This method will override any existing NLRI data in the message. - * If the input is an empty vector all NLRI data will be removed. This method automatically sets the - * bgp_common_header#length field in the message - * @param[in] nlri New NLRI data to set in the message - * @return True if all NLRI data was set successfully or false otherwise. In case of an error an appropriate message - * will be printed to log - */ - bool setNetworkLayerReachabilityInfo(const std::vector& nlri); - - /** - * Clear all NLRI data currently in the message. This is equivalent to calling setNetworkLayerReachabilityInfo() with an empty - * vector as a parameter - * @return True if all NLRI were successfully cleared or false otherwise. In case of an error an appropriate message - * will be printed to log - */ - bool clearNetworkLayerReachabilityInfo(); - - // implement abstract methods - - BgpMessageType getBgpMessageType() const { return BgpLayer::Update; } - -private: - - void parsePrefixAndIPData(uint8_t* dataPtr, size_t dataLen, std::vector& result); - - size_t prefixAndIPDataToByteArray(const std::vector& prefixAndIpData, uint8_t* resultByteArr, size_t maxByteArrSize); - - size_t pathAttributesToByteArray(const std::vector& pathAttributes, uint8_t* resultByteArr, size_t maxByteArrSize); - +class BgpUpdateMessageLayer : public BgpLayer { + public: + /** + * @struct prefix_and_ip + * A structure that contains IPv4 address and IP address mask (prefix) + * information. It's used to represent BGP Withdrawn Routes and Network Layer + * Reachability Information (NLRI) + */ + struct prefix_and_ip { + /** IPv4 address mask, must contain one of the values: 8, 16, 24, 32 */ + uint8_t prefix; + /** IPv4 address */ + IPv4Address ipAddr; + + /** + * A default c'tor that zeroes all data + */ + prefix_and_ip() : prefix(0), ipAddr(IPv4Address::Zero) {} + + /** + * A c'tor that initializes the values of the struct + * @param[in] prefixVal IPv4 address mask value + * @param[in] ipAddrVal IPv4 address + */ + prefix_and_ip(uint8_t prefixVal, const std::string& ipAddrVal) + : prefix(prefixVal), ipAddr(ipAddrVal) {} + }; + + /** + * @struct path_attribute + * A structure that represents BGP OPEN message Path Attributes information + */ + struct path_attribute { + /** Path attribute flags */ + uint8_t flags; + /** Path attribute type */ + uint8_t type; + /** Path attribute length */ + uint8_t length; + /** Path attribute data. Max supported data length is 32 bytes */ + uint8_t data[32]; + + /** + * A default c'tor that zeroes all data + */ + path_attribute() {} + + /** + * A c'tor that initializes the values of the struct + * @param[in] flagsVal Path attribute flags value + * @param[in] typeVal Path attribute type value + * @param[in] dataAsHexString Path attribute data as hex string. The + * path_attribute#length field will be set accordingly. If this parameter is + * not a valid hex string the data will remain zeroed and length will be + * also set to zero + */ + path_attribute(uint8_t flagsVal, uint8_t typeVal, + const std::string& dataAsHexString); + }; + + /** + * 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 + */ + BgpUpdateMessageLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet) + : BgpLayer(data, dataLen, prevLayer, packet) {} + + /** + * A static method that takes a byte array and detects whether it is a + * BgpUpdateMessage + * @param[in] data A byte array + * @param[in] dataSize The byte array size (in bytes) + * @return True if the data looks like a valid BgpUpdateMessage layer + */ + static bool isDataValid(const uint8_t* data, size_t dataSize); + + /** + * A c'tor that creates a new BGP UPDATE message + * @param[in] withdrawnRoutes A vector of withdrawn routes data. If left empty + * (which is the default value) no withdrawn route information will be written + * to the message + * @param[in] pathAttributes A vector of path attributes data. If left empty + * (which is the default value) no path attribute information will be written + * to the message + * @param[in] nlri A vector of network layer reachability data. If left empty + * (which is the default value) no reachability information will be written to + * the message + */ + explicit BgpUpdateMessageLayer( + const std::vector& withdrawnRoutes = + std::vector(), + const std::vector& pathAttributes = + std::vector(), + const std::vector& nlri = std::vector()); + + /** + * Get a pointer to the basic BGP message data. Notice this points directly to + * the data, so any change will modify the actual packet data + * @return A pointer to a bgp_common_header structure containing the data + */ + bgp_common_header* getBasicMsgHeader() const { + return (bgp_common_header*)m_Data; + } + + /** + * @return The size in [bytes] of the Withdrawn Routes data + */ + size_t getWithdrawnRoutesLength() const; + + /** + * Get a vector of the Withdrawn Routes currently in the message + * @param[out] withdrawnRoutes A reference to a vector the Withdrawn Routes + * data will be written to + */ + void getWithdrawnRoutes(std::vector& withdrawnRoutes); + + /** + * Set Withdrawn Routes in this message. This method will override any + * existing Withdrawn Routes in the message. If the input is an empty vector + * all Withdrawn Routes will be removed. This method automatically sets the + * bgp_common_header#length and the Withdrawn Routes length fields in the + * message + * @param[in] withdrawnRoutes New Withdrawn Routes to set in the message + * @return True if all Withdrawn Routes were set successfully or false + * otherwise. In case of an error an appropriate message will be printed to + * log + */ + bool setWithdrawnRoutes(const std::vector& withdrawnRoutes); + + /** + * Clear all Withdrawn Routes data currently in the message. This is + * equivalent to calling setWithdrawnRoutes() with an empty vector as a + * parameter + * @return True if all Withdrawn Routes were successfully cleared or false + * otherwise. In case of an error an appropriate message will be printed to + * log + */ + bool clearWithdrawnRoutes(); + + /** + * @return The size in [bytes] of the Path Attributes data + */ + size_t getPathAttributesLength() const; + + /** + * Get a vector of the Path Attributes currently in the message + * @param[out] pathAttributes A reference to a vector the Path Attributes data + * will be written to + */ + void getPathAttributes(std::vector& pathAttributes); + + /** + * Set Path Attributes in this message. This method will override any existing + * Path Attributes in the message. If the input is an empty vector all Path + * Attributes will be removed. This method automatically sets the + * bgp_common_header#length and the Path Attributes length fields in the + * message + * @param[in] pathAttributes New Path Attributes to set in the message + * @return True if all Path Attributes were set successfully or false + * otherwise. In case of an error an appropriate message will be printed to + * log + */ + bool setPathAttributes(const std::vector& pathAttributes); + + /** + * Clear all Path Attributes data currently in the message. This is equivalent + * to calling setPathAttributes() with an empty vector as a parameter + * @return True if all Path Attributes were successfully cleared or false + * otherwise. In case of an error an appropriate message will be printed to + * log + */ + bool clearPathAttributes(); + + /** + * @return The size in [bytes] of the Network Layer Reachability Info + */ + size_t getNetworkLayerReachabilityInfoLength() const; + + /** + * Get a vector of the Network Layer Reachability Info currently in the + * message + * @param[out] nlri A reference to a vector the NLRI data will be written to + */ + void getNetworkLayerReachabilityInfo(std::vector& nlri); + + /** + * Set NLRI data in this message. This method will override any existing NLRI + * data in the message. If the input is an empty vector all NLRI data will be + * removed. This method automatically sets the bgp_common_header#length field + * in the message + * @param[in] nlri New NLRI data to set in the message + * @return True if all NLRI data was set successfully or false otherwise. In + * case of an error an appropriate message will be printed to log + */ + bool setNetworkLayerReachabilityInfo(const std::vector& nlri); + + /** + * Clear all NLRI data currently in the message. This is equivalent to calling + * setNetworkLayerReachabilityInfo() with an empty vector as a parameter + * @return True if all NLRI were successfully cleared or false otherwise. In + * case of an error an appropriate message will be printed to log + */ + bool clearNetworkLayerReachabilityInfo(); + + // implement abstract methods + + BgpMessageType getBgpMessageType() const { return BgpLayer::Update; } + + private: + void parsePrefixAndIPData(uint8_t* dataPtr, size_t dataLen, + std::vector& result); + + size_t + prefixAndIPDataToByteArray(const std::vector& prefixAndIpData, + uint8_t* resultByteArr, size_t maxByteArrSize); + + size_t + pathAttributesToByteArray(const std::vector& pathAttributes, + uint8_t* resultByteArr, size_t maxByteArrSize); }; - - /** * @class BgpNotificationMessageLayer * Represents a BGP v4 NOTIFICATION message */ -class BgpNotificationMessageLayer : public BgpLayer -{ -public: - - /** - * @struct bgp_notification_message - * BGP NOTIFICATION message structure - */ - #pragma pack(push, 1) - typedef struct bgp_notification_message : bgp_common_header - { - /** BGP notification error code */ - uint8_t errorCode; - /** BGP notification error sub-code */ - uint8_t errorSubCode; - } bgp_notification_message; - #pragma pack(pop) - - /** - * 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 - */ - BgpNotificationMessageLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : BgpLayer(data, dataLen, prevLayer, packet) {} - - /** - * A c'tor that creates a new BGP NOTIFICATION message - * @param[in] errorCode BGP notification error code - * @param[in] errorSubCode BGP notification error sub code - */ - BgpNotificationMessageLayer(uint8_t errorCode, uint8_t errorSubCode); - - /** - * A c'tor that creates a new BGP Notification message - * @param[in] errorCode BGP notification error code - * @param[in] errorSubCode BGP notification error sub code - * @param[in] notificationData A byte array that contains the notification data - * @param[in] notificationDataLen The size of the byte array that contains the notification data - */ - BgpNotificationMessageLayer(uint8_t errorCode, uint8_t errorSubCode, const uint8_t* notificationData, size_t notificationDataLen); - - /** - * A c'tor that creates a new BGP Notification message - * @param[in] errorCode BGP notification error code - * @param[in] errorSubCode BGP notification error sub code - * @param[in] notificationData A hex string that contains the notification data. This string will be converted to a byte array that will be - * added to the message. If the input isn't a valid hex string notification data will remain empty and an error will be printed to log - */ - BgpNotificationMessageLayer(uint8_t errorCode, uint8_t errorSubCode, const std::string& notificationData); - - /** - * Get a pointer to the notification message data. Notice this points directly to the data, so any change will modify the actual packet data - * @return A pointer to a bgp_notification_message structure containing the data - */ - bgp_notification_message* getNotificationMsgHeader() const { return (bgp_notification_message*)m_Data; } - - /** - * @return The size in [bytes] of the notification data. Notification data is a variable-length field used to diagnose the reason for - * the BGP NOTIFICATION - */ - size_t getNotificationDataLen() const; - - /** - * @return A pointer to the notification data. Notification data is a variable-length field used to diagnose the reason for - * the BGP NOTIFICATION - */ - uint8_t* getNotificationData() const; - - /** - * @return A hex string which represents the notification data. Notification data is a variable-length field used to diagnose the reason for - * the BGP NOTIFICATION - */ - std::string getNotificationDataAsHexString() const; - - /** - * Set the notification data. This method will extend or shorten the existing layer to include the new notification data. - * If newNotificationData is NULL or newNotificationDataLen is zero then notification data will be set to none. - * @param[in] newNotificationData A byte array containing the new notification data - * @param[in] newNotificationDataLen The size of the byte array - * @return True if notification data was set successfully or false if any error occurred. In case of an error an appropriate - * error message will be printed to log - */ - bool setNotificationData(const uint8_t* newNotificationData, size_t newNotificationDataLen); - - /** - * Set the notification data. This method will extend or shorten the existing layer to include the new notification data. - * If newNotificationDataAsHexString is an empty string then notification data will be set to none. - * @param[in] newNotificationDataAsHexString A hex string representing the new notification data. If the string is not a valid hex string - * no data will be changed and an error will be returned - * @return True if notification data was set successfully or false if any error occurred or if the string is not a valid hex string. - * In case of an error an appropriate error message will be printed to log - */ - bool setNotificationData(const std::string& newNotificationDataAsHexString); - - // implement abstract methods - - BgpMessageType getBgpMessageType() const { return BgpLayer::Notification; } - -private: - - void initMessageData(uint8_t errorCode, uint8_t errorSubCode, const uint8_t* notificationData, size_t notificationDataLen); - +class BgpNotificationMessageLayer : public BgpLayer { + public: +/** + * @struct bgp_notification_message + * BGP NOTIFICATION message structure + */ +#pragma pack(push, 1) + typedef struct bgp_notification_message : bgp_common_header { + /** BGP notification error code */ + uint8_t errorCode; + /** BGP notification error sub-code */ + uint8_t errorSubCode; + } bgp_notification_message; +#pragma pack(pop) + + /** + * 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 + */ + BgpNotificationMessageLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet) + : BgpLayer(data, dataLen, prevLayer, packet) {} + + /** + * A c'tor that creates a new BGP NOTIFICATION message + * @param[in] errorCode BGP notification error code + * @param[in] errorSubCode BGP notification error sub code + */ + BgpNotificationMessageLayer(uint8_t errorCode, uint8_t errorSubCode); + + /** + * A c'tor that creates a new BGP Notification message + * @param[in] errorCode BGP notification error code + * @param[in] errorSubCode BGP notification error sub code + * @param[in] notificationData A byte array that contains the notification + * data + * @param[in] notificationDataLen The size of the byte array that contains the + * notification data + */ + BgpNotificationMessageLayer(uint8_t errorCode, uint8_t errorSubCode, + const uint8_t* notificationData, + size_t notificationDataLen); + + /** + * A c'tor that creates a new BGP Notification message + * @param[in] errorCode BGP notification error code + * @param[in] errorSubCode BGP notification error sub code + * @param[in] notificationData A hex string that contains the notification + * data. This string will be converted to a byte array that will be added to + * the message. If the input isn't a valid hex string notification data will + * remain empty and an error will be printed to log + */ + BgpNotificationMessageLayer(uint8_t errorCode, uint8_t errorSubCode, + const std::string& notificationData); + + /** + * Get a pointer to the notification message data. Notice this points directly + * to the data, so any change will modify the actual packet data + * @return A pointer to a bgp_notification_message structure containing the + * data + */ + bgp_notification_message* getNotificationMsgHeader() const { + return (bgp_notification_message*)m_Data; + } + + /** + * @return The size in [bytes] of the notification data. Notification data is + * a variable-length field used to diagnose the reason for the BGP + * NOTIFICATION + */ + size_t getNotificationDataLen() const; + + /** + * @return A pointer to the notification data. Notification data is a + * variable-length field used to diagnose the reason for the BGP NOTIFICATION + */ + uint8_t* getNotificationData() const; + + /** + * @return A hex string which represents the notification data. Notification + * data is a variable-length field used to diagnose the reason for the BGP + * NOTIFICATION + */ + std::string getNotificationDataAsHexString() const; + + /** + * Set the notification data. This method will extend or shorten the existing + * layer to include the new notification data. If newNotificationData is NULL + * or newNotificationDataLen is zero then notification data will be set to + * none. + * @param[in] newNotificationData A byte array containing the new notification + * data + * @param[in] newNotificationDataLen The size of the byte array + * @return True if notification data was set successfully or false if any + * error occurred. In case of an error an appropriate error message will be + * printed to log + */ + bool setNotificationData(const uint8_t* newNotificationData, + size_t newNotificationDataLen); + + /** + * Set the notification data. This method will extend or shorten the existing + * layer to include the new notification data. If + * newNotificationDataAsHexString is an empty string then notification data + * will be set to none. + * @param[in] newNotificationDataAsHexString A hex string representing the new + * notification data. If the string is not a valid hex string no data will be + * changed and an error will be returned + * @return True if notification data was set successfully or false if any + * error occurred or if the string is not a valid hex string. In case of an + * error an appropriate error message will be printed to log + */ + bool setNotificationData(const std::string& newNotificationDataAsHexString); + + // implement abstract methods + + BgpMessageType getBgpMessageType() const { return BgpLayer::Notification; } + + private: + void initMessageData(uint8_t errorCode, uint8_t errorSubCode, + const uint8_t* notificationData, + size_t notificationDataLen); }; - - /** * @class BgpKeepaliveMessageLayer * Represents a BGP v4 KEEPALIVE message */ -class BgpKeepaliveMessageLayer : public BgpLayer -{ -public: - - /** - * @typedef bgp_keepalive_message - * BGP KEEPALIVE message structure - */ - typedef bgp_common_header bgp_keepalive_message; - - /** - * 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 - */ - BgpKeepaliveMessageLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : BgpLayer(data, dataLen, prevLayer, packet) {} - - /** - * A c'tor that creates a new BGP KEEPALIVE message - */ - BgpKeepaliveMessageLayer(); - - /** - * Get a pointer to the KeepAlive message data. Notice this points directly to the data, so any change will modify the actual packet data - * @return A pointer to a bgp_keepalive_message structure containing the data - */ - bgp_keepalive_message* getKeepaliveHeader() const { return (bgp_keepalive_message*)getBasicHeader(); } - - // implement abstract methods - - BgpMessageType getBgpMessageType() const { return BgpLayer::Keepalive; } - +class BgpKeepaliveMessageLayer : public BgpLayer { + public: + /** + * @typedef bgp_keepalive_message + * BGP KEEPALIVE message structure + */ + typedef bgp_common_header bgp_keepalive_message; + + /** + * 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 + */ + BgpKeepaliveMessageLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet) + : BgpLayer(data, dataLen, prevLayer, packet) {} + + /** + * A c'tor that creates a new BGP KEEPALIVE message + */ + BgpKeepaliveMessageLayer(); + + /** + * Get a pointer to the KeepAlive message data. Notice this points directly to + * the data, so any change will modify the actual packet data + * @return A pointer to a bgp_keepalive_message structure containing the data + */ + bgp_keepalive_message* getKeepaliveHeader() const { + return (bgp_keepalive_message*)getBasicHeader(); + } + + // implement abstract methods + + BgpMessageType getBgpMessageType() const { return BgpLayer::Keepalive; } }; - - /** * @class BgpRouteRefreshMessageLayer * Represents a BGP v4 ROUTE-REFRESH message */ -class BgpRouteRefreshMessageLayer : public BgpLayer -{ -public: - - /** - * @struct bgp_route_refresh_message - * BGP ROUTE-REFRESH message structure - */ - #pragma pack(push, 1) - typedef struct bgp_route_refresh_message : bgp_common_header - { - /** Address Family Identifier */ - uint16_t afi; - /** Reserved field */ - uint8_t reserved; - /** Subsequent Address Family Identifier */ - uint8_t safi; - } bgp_route_refresh_message; - #pragma pack(pop) - - /** - * 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 - */ - BgpRouteRefreshMessageLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : BgpLayer(data, dataLen, prevLayer, packet) {} - - /** - * A c'tor that creates a new BGP ROUTE-REFRESH message - * @param[in] afi The Address Family Identifier (AFI) value to set in the message - * @param[in] safi The Subsequent Address Family Identifier (SAFI) value to set in the message - */ - BgpRouteRefreshMessageLayer(uint16_t afi, uint8_t safi); - - /** - * Get a pointer to the ROUTE-REFRESH message data. Notice this points directly to the data, so any change will modify the actual packet data - * @return A pointer to a bgp_route_refresh_message structure containing the data - */ - bgp_route_refresh_message* getRouteRefreshHeader() const { return (bgp_route_refresh_message*)getBasicHeader(); } - - // implement abstract methods - - BgpMessageType getBgpMessageType() const { return BgpLayer::RouteRefresh; } - +class BgpRouteRefreshMessageLayer : public BgpLayer { + public: +/** + * @struct bgp_route_refresh_message + * BGP ROUTE-REFRESH message structure + */ +#pragma pack(push, 1) + typedef struct bgp_route_refresh_message : bgp_common_header { + /** Address Family Identifier */ + uint16_t afi; + /** Reserved field */ + uint8_t reserved; + /** Subsequent Address Family Identifier */ + uint8_t safi; + } bgp_route_refresh_message; +#pragma pack(pop) + + /** + * 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 + */ + BgpRouteRefreshMessageLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet) + : BgpLayer(data, dataLen, prevLayer, packet) {} + + /** + * A c'tor that creates a new BGP ROUTE-REFRESH message + * @param[in] afi The Address Family Identifier (AFI) value to set in the + * message + * @param[in] safi The Subsequent Address Family Identifier (SAFI) value to + * set in the message + */ + BgpRouteRefreshMessageLayer(uint16_t afi, uint8_t safi); + + /** + * Get a pointer to the ROUTE-REFRESH message data. Notice this points + * directly to the data, so any change will modify the actual packet data + * @return A pointer to a bgp_route_refresh_message structure containing the + * data + */ + bgp_route_refresh_message* getRouteRefreshHeader() const { + return (bgp_route_refresh_message*)getBasicHeader(); + } + + // implement abstract methods + + BgpMessageType getBgpMessageType() const { return BgpLayer::RouteRefresh; } }; -} +} // namespace pcpp -#endif //PACKETPP_BGP_LAYER +#endif // PACKETPP_BGP_LAYER diff --git a/Packet++/header/CotpLayer.h b/Packet++/header/CotpLayer.h index 119737a28e..be26b415f1 100644 --- a/Packet++/header/CotpLayer.h +++ b/Packet++/header/CotpLayer.h @@ -4,116 +4,116 @@ #include "EthLayer.h" #include "Layer.h" -namespace pcpp -{ +namespace pcpp { /** * @struct cotphdr * Represents a COTP protocol header */ #pragma pack(push, 1) - typedef struct - { - /** length */ - uint8_t length; - /** PDU type identifier */ - uint8_t pduType ; - /** TPDU number sequence*/ - uint8_t tpduNumber; - } cotphdr; +typedef struct { + /** length */ + uint8_t length; + /** PDU type identifier */ + uint8_t pduType; + /** TPDU number sequence*/ + uint8_t tpduNumber; +} cotphdr; #pragma pack(pop) - /** - * @class CotpLayer - * Represents a COTP (Connection Oriented Transport Protocol) - */ - class CotpLayer : public Layer - { - public: - /** - * A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data (will be casted to @ref cotphdr) - * @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 - */ - CotpLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) - : Layer(data, dataLen, prevLayer, packet) - { - m_Protocol = COTP; - } - - /** - * A constructor that allocates a new COTP header - * @param[in] tpduNumber Protocol TPDU number - */ - explicit CotpLayer(uint8_t tpduNumber); - - virtual ~CotpLayer() {} - - /** - * @return COTP length - */ - uint8_t getLength() const; - - /** - * @return COTP PDU type - */ - uint8_t getPduType() const; - - /** - * @return COTP TPDU number - */ - uint8_t getTpduNumber() const; - - /** - * @return Size of @ref cotphdr - */ - size_t getHeaderLen() const override { return sizeof(cotphdr); } - - /** - * Set the value of the length - * @param[in] length The value of the length - */ - void setLength(uint8_t length) const; - - /** - * Set the value of the version - * @param[in] pduType The number of the PDU type - */ - void setPduType(uint8_t pduType) const; - - /** - * Set the value of the version - * @param[in] tpduNumber The value of the TPDU number - */ - void setTpduNumber(uint8_t tpduNumber) const; - - /** - * Does nothing for this layer - */ - void computeCalculateFields() override {} - - /** - * Currently parses the rest of the packet as a S7COMM or generic payload (PayloadLayer) - */ - void parseNextLayer() override; - - /** - * A static method that takes a byte array and detects whether it is a COTP - * @param[in] data A byte array - * @param[in] dataSize The byte array size (in bytes) - * @return True if the data looks like a valid COTP layer - */ - static bool isDataValid(const uint8_t *data, size_t dataSize); - - std::string toString() const override; - - OsiModelLayer getOsiModelLayer() const override { return OsiModelTransportLayer; } - - private: - cotphdr *getCotpHeader() const { return (cotphdr *)m_Data; } - }; +/** + * @class CotpLayer + * Represents a COTP (Connection Oriented Transport Protocol) + */ +class CotpLayer : public Layer { + public: + /** + * A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data (will be casted to @ref cotphdr) + * @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 + */ + CotpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : Layer(data, dataLen, prevLayer, packet) { + m_Protocol = COTP; + } + + /** + * A constructor that allocates a new COTP header + * @param[in] tpduNumber Protocol TPDU number + */ + explicit CotpLayer(uint8_t tpduNumber); + + virtual ~CotpLayer() {} + + /** + * @return COTP length + */ + uint8_t getLength() const; + + /** + * @return COTP PDU type + */ + uint8_t getPduType() const; + + /** + * @return COTP TPDU number + */ + uint8_t getTpduNumber() const; + + /** + * @return Size of @ref cotphdr + */ + size_t getHeaderLen() const override { return sizeof(cotphdr); } + + /** + * Set the value of the length + * @param[in] length The value of the length + */ + void setLength(uint8_t length) const; + + /** + * Set the value of the version + * @param[in] pduType The number of the PDU type + */ + void setPduType(uint8_t pduType) const; + + /** + * Set the value of the version + * @param[in] tpduNumber The value of the TPDU number + */ + void setTpduNumber(uint8_t tpduNumber) const; + + /** + * Does nothing for this layer + */ + void computeCalculateFields() override {} + + /** + * Currently parses the rest of the packet as a S7COMM or generic payload + * (PayloadLayer) + */ + void parseNextLayer() override; + + /** + * A static method that takes a byte array and detects whether it is a COTP + * @param[in] data A byte array + * @param[in] dataSize The byte array size (in bytes) + * @return True if the data looks like a valid COTP layer + */ + static bool isDataValid(const uint8_t* data, size_t dataSize); + + std::string toString() const override; + + OsiModelLayer getOsiModelLayer() const override { + return OsiModelTransportLayer; + } + + private: + cotphdr* getCotpHeader() const { return (cotphdr*)m_Data; } +}; } // namespace pcpp diff --git a/Packet++/header/DhcpLayer.h b/Packet++/header/DhcpLayer.h index 82d9791294..e82cd5377d 100644 --- a/Packet++/header/DhcpLayer.h +++ b/Packet++/header/DhcpLayer.h @@ -1,10 +1,10 @@ #ifndef PACKETPP_DHCP_LAYER #define PACKETPP_DHCP_LAYER -#include "Layer.h" -#include "TLVData.h" #include "IpAddress.h" +#include "Layer.h" #include "MacAddress.h" +#include "TLVData.h" #include /// @file @@ -13,814 +13,854 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - - /** - * @struct dhcp_header - * Represents a DHCP protocol header - */ - #pragma pack(push, 1) - struct dhcp_header - { - /** BootP opcode */ - uint8_t opCode; - /** Hardware type, set to 1 (Ethernet) by default */ - uint8_t hardwareType; - /** Hardware address length, set to 6 (MAC address length) by default */ - uint8_t hardwareAddressLength; - /** Hop count */ - uint8_t hops; - /** DHCP/BootP transaction ID */ - uint32_t transactionID; - /** The elapsed time, in seconds since the client sent its first BOOTREQUEST message */ - uint16_t secondsElapsed; - /** BootP flags */ - uint16_t flags; - /** Client IPv4 address */ - uint32_t clientIpAddress; - /** Your IPv4 address */ - uint32_t yourIpAddress; - /** Server IPv4 address */ - uint32_t serverIpAddress; - /** Gateway IPv4 address */ - uint32_t gatewayIpAddress; - /** Client hardware address, by default contains the MAC address (only 6 first bytes are used) */ - uint8_t clientHardwareAddress[16]; - /** BootP server name */ - uint8_t serverName[64]; - /** BootP boot file name */ - uint8_t bootFilename[128]; - /** DHCP magic number (set to the default value of 0x63538263) */ - uint32_t magicNumber; - }; - #pragma pack(pop) - - - /** - * BootP opcodes - */ - enum BootpOpCodes - { - /** BootP request */ - DHCP_BOOTREQUEST = 1, - /** BootP reply */ - DHCP_BOOTREPLY = 2 - }; - - /** - * DHCP message types - */ - enum DhcpMessageType - { - /** Unknown message type */ - DHCP_UNKNOWN_MSG_TYPE = 0, - /** Discover message type */ - DHCP_DISCOVER = 1, - /** Offer message type */ - DHCP_OFFER = 2, - /** Request message type */ - DHCP_REQUEST = 3, - /** Decline message type */ - DHCP_DECLINE = 4, - /** Acknowledge message type */ - DHCP_ACK = 5, - /** Non-acknowledge message type */ - DHCP_NAK = 6, - /** Release message type */ - DHCP_RELEASE = 7, - /** Inform message type */ - DHCP_INFORM = 8 - }; - - /** - * DHCP option types. - */ - enum DhcpOptionTypes - { - /** Unknown option type */ - DHCPOPT_UNKNOWN = -1, - /** Pad */ - DHCPOPT_PAD = 0, - /** Subnet Mask Value */ - DHCPOPT_SUBNET_MASK = 1, - /** Time Offset in Seconds from UTC */ - DHCPOPT_TIME_OFFSET = 2, - /** N/4 Router addresses */ - DHCPOPT_ROUTERS = 3, - /** N/4 Timeserver addresses */ - DHCPOPT_TIME_SERVERS = 4, - /** N/4 IEN-116 Server addresses */ - DHCPOPT_NAME_SERVERS = 5, - /** N/4 DNS Server addresses */ - DHCPOPT_DOMAIN_NAME_SERVERS = 6, - /** N/4 Logging Server addresses */ - DHCPOPT_LOG_SERVERS = 7, - /** N/4 Quotes Server addresses */ - DHCPOPT_QUOTES_SERVERS = 8, - /** N/4 Quotes Server addresses */ - DHCPOPT_LPR_SERVERS = 9, - /** N/4 Quotes Server addresses */ - DHCPOPT_IMPRESS_SERVERS = 10, - /** N/4 RLP Server addresses */ - DHCPOPT_RESOURCE_LOCATION_SERVERS = 11, - /** Hostname string */ - DHCPOPT_HOST_NAME = 12, - /** Size of boot file in 512 byte chunks */ - DHCPOPT_BOOT_SIZE = 13, - /** Client to dump and name the file to dump it to */ - DHCPOPT_MERIT_DUMP = 14, - /** The DNS domain name of the client */ - DHCPOPT_DOMAIN_NAME = 15, - /** Swap Server address */ - DHCPOPT_SWAP_SERVER = 16, - /** Path name for root disk */ - DHCPOPT_ROOT_PATH = 17, - /** Path name for more BOOTP info */ - DHCPOPT_EXTENSIONS_PATH = 18, - /** Enable/Disable IP Forwarding */ - DHCPOPT_IP_FORWARDING = 19, - /** Enable/Disable Source Routing */ - DHCPOPT_NON_LOCAL_SOURCE_ROUTING = 20, - /** Routing Policy Filters */ - DHCPOPT_POLICY_FILTER = 21, - /** Max Datagram Reassembly Size */ - DHCPOPT_MAX_DGRAM_REASSEMBLY = 22, - /** Default IP Time to Live */ - DEFAULT_IP_TTL = 23, - /** Path MTU Aging Timeout */ - DHCPOPT_PATH_MTU_AGING_TIMEOUT = 24, - /** Path MTU Plateau Table */ - PATH_MTU_PLATEAU_TABLE = 25, - /** Interface MTU Size */ - DHCPOPT_INTERFACE_MTU = 26, - /** All Subnets are Local */ - DHCPOPT_ALL_SUBNETS_LOCAL = 27, - /** Broadcast Address */ - DHCPOPT_BROADCAST_ADDRESS = 28, - /** Perform Mask Discovery */ - DHCPOPT_PERFORM_MASK_DISCOVERY = 29, - /** Provide Mask to Others */ - DHCPOPT_MASK_SUPPLIER = 30, - /** Perform Router Discovery */ - DHCPOPT_ROUTER_DISCOVERY = 31, - /** Router Solicitation Address */ - DHCPOPT_ROUTER_SOLICITATION_ADDRESS = 32, - /** Static Routing Table */ - DHCPOPT_STATIC_ROUTES = 33, - /** Trailer Encapsulation */ - DHCPOPT_TRAILER_ENCAPSULATION = 34, - /** ARP Cache Timeout */ - DHCPOPT_ARP_CACHE_TIMEOUT = 35, - /** IEEE802.3 Encapsulation */ - DHCPOPT_IEEE802_3_ENCAPSULATION = 36, - /** Default TCP Time to Live */ - DHCPOPT_DEFAULT_TCP_TTL = 37, - /** TCP Keepalive Interval */ - DHCPOPT_TCP_KEEPALIVE_INTERVAL = 38, - /** TCP Keepalive Garbage */ - DHCPOPT_TCP_KEEPALIVE_GARBAGE = 39, - /** NIS Domain Name */ - DHCPOPT_NIS_DOMAIN = 40, - /** NIS Server Addresses */ - DHCPOPT_NIS_SERVERS = 41, - /** NTP Server Addresses */ - DHCPOPT_NTP_SERVERS = 42, - /** Vendor Specific Information */ - DHCPOPT_VENDOR_ENCAPSULATED_OPTIONS = 43, - /** NETBIOS Name Servers */ - DHCPOPT_NETBIOS_NAME_SERVERS = 44, - /** NETBIOS Datagram Distribution */ - DHCPOPT_NETBIOS_DD_SERVER = 45, - /** NETBIOS Node Type */ - DHCPOPT_NETBIOS_NODE_TYPE = 46, - /** NETBIOS Scope */ - DHCPOPT_NETBIOS_SCOPE = 47, - /** X Window Font Server */ - DHCPOPT_FONT_SERVERS = 48, - /** X Window Display Manager */ - DHCPOPT_X_DISPLAY_MANAGER = 49, - /** Requested IP Address */ - DHCPOPT_DHCP_REQUESTED_ADDRESS = 50, - /** IP Address Lease Time */ - DHCPOPT_DHCP_LEASE_TIME = 51, - /** Overload "sname" or "file" */ - DHCPOPT_DHCP_OPTION_OVERLOAD = 52, - /** DHCP Message Type */ - DHCPOPT_DHCP_MESSAGE_TYPE = 53, - /** DHCP Server Identification */ - DHCPOPT_DHCP_SERVER_IDENTIFIER = 54, - /** Parameter Request List */ - DHCPOPT_DHCP_PARAMETER_REQUEST_LIST = 55, - /** DHCP Error Message */ - DHCPOPT_DHCP_MESSAGE = 56, - /** DHCP Maximum Message Size */ - DHCPOPT_DHCP_MAX_MESSAGE_SIZE = 57, - /** DHCP Renewal (T1) Time */ - DHCPOPT_DHCP_RENEWAL_TIME = 58, - /** DHCP Rebinding (T2) Time */ - DHCPOPT_DHCP_REBINDING_TIME = 59, - /** Class Identifier */ - DHCPOPT_VENDOR_CLASS_IDENTIFIER = 60, - /** Class Identifier */ - DHCPOPT_DHCP_CLIENT_IDENTIFIER = 61, - /** NetWare/IP Domain Name */ - DHCPOPT_NWIP_DOMAIN_NAME = 62, - /** NetWare/IP sub Options */ - DHCPOPT_NWIP_SUBOPTIONS = 63, - /** NIS+ v3 Client Domain Name */ - DHCPOPT_NIS_DOMAIN_NAME = 64, - /** NIS+ v3 Server Addresses */ - DHCPOPT_NIS_SERVER_ADDRESS = 65, - /** TFTP Server Name */ - DHCPOPT_TFTP_SERVER_NAME = 66, - /** Boot File Name */ - DHCPOPT_BOOTFILE_NAME = 67, - /** Home Agent Addresses */ - DHCPOPT_HOME_AGENT_ADDRESS = 68, - /** Simple Mail Server (SMTP) Addresses */ - DHCPOPT_SMTP_SERVER = 69, - /** Post Office (POP3) Server Addresses */ - DHCPOPT_POP3_SERVER = 70, - /** Network News (NNTP) Server Addresses */ - DHCPOPT_NNTP_SERVER = 71, - /** WWW Server Addresses */ - DHCPOPT_WWW_SERVER = 72, - /** Finger Server Addresses */ - DHCPOPT_FINGER_SERVER = 73, - /** Chat (IRC) Server Addresses */ - DHCPOPT_IRC_SERVER = 74, - /** StreetTalk Server Addresses */ - DHCPOPT_STREETTALK_SERVER = 75, - /** ST Directory Assist. Addresses */ - DHCPOPT_STDA_SERVER = 76, - /** User Class Information */ - DHCPOPT_USER_CLASS = 77, - /** Directory Agent Information */ - DHCPOPT_DIRECTORY_AGENT = 78, - /** Service Location Agent Scope */ - DHCPOPT_SERVICE_SCOPE = 79, - /** Rapid Commit */ - DHCPOPT_RAPID_COMMIT = 80, - /** Fully Qualified Domain Name */ - DHCPOPT_FQDN = 81, - /** Relay Agent Information */ - DHCPOPT_DHCP_AGENT_OPTIONS = 82, - /** Internet Storage Name Service */ - DHCPOPT_ISNS = 83, - /** Novell Directory Services */ - DHCPOPT_NDS_SERVERS = 85, - /** Novell Directory Services */ - DHCPOPT_NDS_TREE_NAME = 86, - /** Novell Directory Services */ - DHCPOPT_NDS_CONTEXT = 87, - /** BCMCS Controller Domain Name list */ - DHCPOPT_BCMCS_CONTROLLER_DOMAIN_NAME_LIST = 88, - /** BCMCS Controller IPv4 address option */ - DHCPOPT_BCMCS_CONTROLLER_IPV4_ADDRESS = 89, - /** Authentication */ - DHCPOPT_AUTHENTICATION = 90, - /** Client Last Transaction Time */ - DHCPOPT_CLIENT_LAST_TXN_TIME = 91, - /** Associated IP */ - DHCPOPT_ASSOCIATED_IP = 92, - /** Client System Architecture */ - DHCPOPT_CLIENT_SYSTEM = 93, - /** Client Network Device Interface */ - DHCPOPT_CLIENT_NDI = 94, - /** Lightweight Directory Access Protocol [ */ - DHCPOPT_LDAP = 95, - /** UUID/GUID-based Client Identifier */ - DHCPOPT_UUID_GUID = 97, - /** Open Group's User Authentication */ - DHCPOPT_USER_AUTH = 98, - /** GEOCONF_CIVIC */ - DHCPOPT_GEOCONF_CIVIC = 99, - /** IEEE 1003.1 TZ String */ - DHCPOPT_PCODE = 100, - /** Reference to the TZ Database */ - DHCPOPT_TCODE = 101, - /** NetInfo Parent Server Address */ - DHCPOPT_NETINFO_ADDRESS = 112, - /** NetInfo Parent Server Tag */ - DHCPOPT_NETINFO_TAG = 113, - /** URL */ - DHCPOPT_URL = 114, - /** DHCP Auto-Configuration */ - DHCPOPT_AUTO_CONFIG = 116, - /** Name Service Search */ - DHCPOPT_NAME_SERVICE_SEARCH = 117, - /** Subnet Selection Option */ - DHCPOPT_SUBNET_SELECTION = 118, - /** DNS Domain Search List */ - DHCPOPT_DOMAIN_SEARCH = 119, - /** SIP Servers DHCP Option */ - DHCPOPT_SIP_SERVERS = 120, - /** Classless Static Route Option */ - DHCPOPT_CLASSLESS_STATIC_ROUTE = 121, - /** CableLabs Client Configuration */ - DHCPOPT_CCC = 122, - /** GeoConf Option */ - DHCPOPT_GEOCONF = 123, - /** Vendor-Identifying Vendor Class */ - DHCPOPT_V_I_VENDOR_CLASS = 124, - /** Vendor-Identifying Vendor-Specific Information */ - DHCPOPT_V_I_VENDOR_OPTS = 125, - /** OPTION_PANA_AGENT */ - DHCPOPT_OPTION_PANA_AGENT = 136, - /** OPTION_V4_LOST */ - DHCPOPT_OPTION_V4_LOST = 137, - /** CAPWAP Access Controller addresses */ - DHCPOPT_OPTION_CAPWAP_AC_V4 = 138, - /** A Series Of Suboptions */ - DHCPOPT_OPTION_IPV4_ADDRESS_MOS = 139, - /** A Series Of Suboptions */ - DHCPOPT_OPTION_IPV4_FQDN_MOS = 140, - /** List of domain names to search for SIP User Agent Configuration */ - DHCPOPT_SIP_UA_CONFIG = 141, - /** ANDSF IPv4 Address Option for DHCPv4 */ - DHCPOPT_OPTION_IPV4_ADDRESS_ANDSF = 142, - /** Geospatial Location with Uncertainty [RF */ - DHCPOPT_GEOLOC = 144, - /** Forcerenew Nonce Capable */ - DHCPOPT_FORCERENEW_NONCE_CAPABLE = 145, - /** Information for selecting RDNSS */ - DHCPOPT_RDNSS_SELECTION = 146, - /** Status code and optional N byte text message describing status */ - DHCPOPT_STATUS_CODE = 151, - /** Absolute time (seconds since Jan 1, 1970) message was sent */ - DHCPOPT_BASE_TIME = 152, - /** Number of seconds in the past when client entered current state */ - DHCPOPT_START_TIME_OF_STATE = 153, - /** Absolute time (seconds since Jan 1, 1970) for beginning of query */ - DHCPOPT_QUERY_START_TIME = 154, - /** Absolute time (seconds since Jan 1, 1970) for end of query */ - DHCPOPT_QUERY_END_TIME = 155, - /** State of IP address */ - DHCPOPT_DHCP_STATE = 156, - /** Indicates information came from local or remote server */ - DHCPOPT_DATA_SOURCE = 157, - /** Includes one or multiple lists of PCP server IP addresses; each list is treated as a separate PCP server */ - DHCPOPT_OPTION_V4_PCP_SERVER = 158, - /** This option is used to configure a set of ports bound to a shared IPv4 address */ - DHCPOPT_OPTION_V4_PORTPARAMS = 159, - /** DHCP Captive-Portal */ - DHCPOPT_CAPTIVE_PORTAL = 160, - /** Manufacturer Usage Descriptions */ - DHCPOPT_OPTION_MUD_URL_V4 = 161, - /** Etherboot */ - DHCPOPT_ETHERBOOT = 175, - /** IP Telephone */ - DHCPOPT_IP_TELEPHONE = 176, - /** Magic string = F1:00:74:7E */ - DHCPOPT_PXELINUX_MAGIC = 208, - /** Configuration file */ - DHCPOPT_CONFIGURATION_FILE = 209, - /** Path Prefix Option */ - DHCPOPT_PATH_PREFIX = 210, - /** Reboot Time */ - DHCPOPT_REBOOT_TIME = 211, - /** OPTION_6RD with N/4 6rd BR addresses */ - DHCPOPT_OPTION_6RD = 212, - /** Access Network Domain Name */ - DHCPOPT_OPTION_V4_ACCESS_DOMAIN = 213, - /** Subnet Allocation Option */ - DHCPOPT_SUBNET_ALLOCATION = 220, - /** Virtual Subnet Selection (VSS) Option */ - DHCPOPT_VIRTUAL_SUBNET_SELECTION = 221, - /** End (last option) */ - DHCPOPT_END = 255 - }; - - - /** - * @class DhcpOption - * A wrapper class for DHCP options. This class does not create or modify DHCP option records, but rather - * serves as a wrapper and provides useful methods for setting and retrieving data to/from them - */ - class DhcpOption : public TLVRecord - { - public: - - /** - * A c'tor for this class that gets a pointer to the option raw data (byte array) - * @param[in] optionRawData A pointer to the option raw data - */ - explicit DhcpOption(uint8_t* optionRawData) : TLVRecord(optionRawData) { } - - /** - * A d'tor for this class, currently does nothing - */ - virtual ~DhcpOption() { } - - /** - * Retrieve DHCP option data as IPv4 address. Relevant only if option value is indeed an IPv4 address - * @return DHCP option data as IPv4 address - */ - IPv4Address getValueAsIpAddr() const - { - return getValueAs(); - } - - /** - * Set DHCP option data as IPv4 address. This method copies the 4 bytes of the IP address to the option value - * @param[in] addr The IPv4 address to set - * @param[in] valueOffset An optional parameter that specifies where to start set the option data (default set to 0). For example: - * if option data is 20 bytes long and you want to set the IP address in the 4 last bytes then use this method like this: - * setValueIpAddr(your_addr, 16) - */ - void setValueIpAddr(const IPv4Address& addr, int valueOffset = 0) - { - setValue(addr.toInt(), valueOffset); - } - - /** - * Retrieve DHCP option data as string. Relevant only if option value is indeed a string - * @param[in] valueOffset An optional parameter that specifies where to start copy the DHCP option data. For example: - * when retrieving Client FQDN option, you may ignore the flags and RCODE fields using this method like this: - * getValueAsString(3). The default is 0 - start copying from the beginning of option data - * @return DHCP option data as string - */ - std::string getValueAsString(int valueOffset = 0) const - { - if (m_Data == nullptr || m_Data->recordLen - valueOffset < 1) - return ""; - - return std::string((const char*)m_Data->recordValue + valueOffset, (int)m_Data->recordLen - valueOffset); - } - - /** - * Set DHCP option data as string. This method copies the string to the option value. If the string is longer than option length - * the string is trimmed so it will fit the option length - * @param[in] stringValue The string to set - * @param[in] valueOffset An optional parameter that specifies where to start set the option data (default set to 0). For example: - * if option data is 20 bytes long and you want to set a 6 char-long string in the 6 last bytes then use this method like this: - * setValueString("string", 14) - */ - void setValueString(const std::string& stringValue, int valueOffset = 0) - { - // calculate the maximum length of the destination buffer - size_t len = (size_t)m_Data->recordLen - (size_t)valueOffset; - - // use the length of input string if a buffer is large enough for whole string - if (stringValue.length() < len) - len = stringValue.length(); - - memcpy(m_Data->recordValue + valueOffset, stringValue.data(), len); - } - - /** - * Check if a pointer can be assigned to the TLV record data - * @param[in] recordRawData A pointer to the TLV record raw data - * @param[in] tlvDataLen The size of the TLV record raw data - * @return True if data is valid and can be assigned - */ - static bool canAssign(const uint8_t* recordRawData, size_t tlvDataLen) - { - auto data = (TLVRawData*)recordRawData; - if (data == nullptr) - return false; - - if (tlvDataLen < sizeof(TLVRawData::recordType)) - return false; - - if (data->recordType == (uint8_t)DHCPOPT_END || data->recordType == (uint8_t)DHCPOPT_PAD) - return true; - - return TLVRecord::canAssign(recordRawData, tlvDataLen); - } - - // implement abstract methods - - size_t getTotalSize() const - { - if (m_Data == nullptr) - return 0; - - if (m_Data->recordType == (uint8_t)DHCPOPT_END || m_Data->recordType == (uint8_t)DHCPOPT_PAD) - return sizeof(uint8_t); - - return sizeof(uint8_t) * 2 + (size_t)m_Data->recordLen; - } - - size_t getDataSize() const - { - if (m_Data == nullptr) - return 0; - - if (m_Data->recordType == (uint8_t)DHCPOPT_END || m_Data->recordType == (uint8_t)DHCPOPT_PAD) - return 0; - - return m_Data->recordLen; - } - }; - - - /** - * @class DhcpOptionBuilder - * A class for building DHCP options. This builder receives the option parameters in its c'tor, - * builds the DHCP option raw buffer and provides a build() method to get a DhcpOption object out of it - */ - class DhcpOptionBuilder : public TLVRecordBuilder - { - public: - - /** - * A c'tor for building DHCP options which their value is a byte array. The DhcpOption object can later - * be retrieved by calling build() - * @param[in] optionType DHCP option type - * @param[in] optionValue A buffer containing the option value. This buffer is read-only and isn't modified in any way - * @param[in] optionValueLen DHCP option value length in bytes - */ - DhcpOptionBuilder(DhcpOptionTypes optionType, const uint8_t* optionValue, uint8_t optionValueLen) : - TLVRecordBuilder((uint8_t)optionType, optionValue, optionValueLen) { } - - /** - * A c'tor for building DHCP options which have a 1-byte value. The DhcpOption object can later be retrieved - * by calling build() - * @param[in] optionType DHCP option type - * @param[in] optionValue A 1-byte option value - */ - DhcpOptionBuilder(DhcpOptionTypes optionType, uint8_t optionValue) : - TLVRecordBuilder((uint8_t)optionType, optionValue) { } - - /** - * A c'tor for building DHCP options which have a 2-byte value. The DhcpOption object can later be retrieved - * by calling build() - * @param[in] optionType DHCP option type - * @param[in] optionValue A 2-byte option value - */ - DhcpOptionBuilder(DhcpOptionTypes optionType, uint16_t optionValue) : - TLVRecordBuilder((uint8_t)optionType, optionValue) { } - - /** - * A c'tor for building DHCP options which have a 4-byte value. The DhcpOption object can later be retrieved - * by calling build() - * @param[in] optionType DHCP option type - * @param[in] optionValue A 4-byte option value - */ - DhcpOptionBuilder(DhcpOptionTypes optionType, uint32_t optionValue) : - TLVRecordBuilder((uint8_t)optionType, optionValue) { } - - /** - * A c'tor for building DHCP options which have an IPv4Address value. The DhcpOption object can later be - * retrieved by calling build() - * @param[in] optionType DHCP option type - * @param[in] optionValue The IPv4 address option value - */ - DhcpOptionBuilder(DhcpOptionTypes optionType, const IPv4Address& optionValue) : - TLVRecordBuilder((uint8_t)optionType, optionValue) { } - - /** - * A c'tor for building DHCP options which have a string value. The DhcpOption object can later be retrieved - * by calling build() - * @param[in] optionType DHCP option type - * @param[in] optionValue The string option value - */ - DhcpOptionBuilder(DhcpOptionTypes optionType, const std::string& optionValue) : - TLVRecordBuilder((uint8_t)optionType, optionValue) { } - - /** - * A copy c'tor which copies all the data from another instance of DhcpOptionBuilder - * @param[in] other The instance to copy from - */ - DhcpOptionBuilder(const DhcpOptionBuilder& other) : - TLVRecordBuilder(other) { } - - /** - * Assignment operator that copies all data from another instance of DhcpOptionBuilder - * @param[in] other The instance to assign from - * @return A reference to the assignee - */ - DhcpOptionBuilder& operator=(const DhcpOptionBuilder& other) - { - TLVRecordBuilder::operator=(other); - return *this; - } - - /** - * Build the DhcpOption object out of the parameters defined in the c'tor - * @return The DhcpOption object - */ - DhcpOption build() const; - }; - - - - /** - * @class DhcpLayer - * Represents a DHCP (Dynamic Host Configuration Protocol) protocol layer - */ - class DhcpLayer : public Layer - { - public: - - /** - * 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 - */ - DhcpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); - - /** - * A constructor that creates the layer from scratch. Adds a ::DHCPOPT_DHCP_MESSAGE_TYPE and a ::DHCPOPT_END - * options - * @param[in] msgType A DHCP message type to be set - * @param[in] clientMacAddr A client MAC address to set in dhcp_header#clientHardwareAddress field - */ - DhcpLayer(DhcpMessageType msgType, const MacAddress& clientMacAddr); - - /** - * A constructor that creates the layer from scratch with clean data - */ - DhcpLayer(); - - /** - * A destructor for this layer - */ - virtual ~DhcpLayer() {} - - /** - * Get a pointer to the DHCP header. Notice this points directly to the data, so every change will change the actual packet data - * @return A pointer to the @ref dhcp_header - */ - dhcp_header* getDhcpHeader() const { return (dhcp_header*)m_Data; } - - /** - * @return The BootP opcode of this message - */ - BootpOpCodes getOpCode() const { return (BootpOpCodes)getDhcpHeader()->opCode; } - - /** - * @return The client IPv4 address (as extracted from dhcp_header#clientIpAddress converted to IPv4Address object) - */ - IPv4Address getClientIpAddress() const { return getDhcpHeader()->clientIpAddress; } - - /** - * Set the client IPv4 address in dhcp_header#clientIpAddress - * @param[in] addr The IPv4 address to set - */ - void setClientIpAddress(const IPv4Address& addr) { getDhcpHeader()->clientIpAddress = addr.toInt(); } - - /** - * @return The server IPv4 address (as extracted from dhcp_header#serverIpAddress converted to IPv4Address object) - */ - IPv4Address getServerIpAddress() const { return getDhcpHeader()->serverIpAddress; } - - /** - * Set the server IPv4 address in dhcp_header#serverIpAddress - * @param[in] addr The IPv4 address to set - */ - void setServerIpAddress(const IPv4Address& addr) { getDhcpHeader()->serverIpAddress = addr.toInt(); } - - /** - * @return Your IPv4 address (as extracted from dhcp_header#yourIpAddress converted to IPv4Address object) - */ - IPv4Address getYourIpAddress() const { return getDhcpHeader()->yourIpAddress; } - - /** - * Set your IPv4 address in dhcp_header#yourIpAddress - * @param[in] addr The IPv4 address to set - */ - void setYourIpAddress(const IPv4Address& addr) { getDhcpHeader()->yourIpAddress = addr.toInt(); } - - /** - * @return Gateway IPv4 address (as extracted from dhcp_header#gatewayIpAddress converted to IPv4Address object) - */ - IPv4Address getGatewayIpAddress() const { return getDhcpHeader()->gatewayIpAddress; } - - /** - * Set the gateway IPv4 address in dhcp_header#gatewayIpAddress - * @param[in] addr The IPv4 address to set - */ - void setGatewayIpAddress(const IPv4Address& addr) { getDhcpHeader()->gatewayIpAddress = addr.toInt(); } - - /** - * @return The client MAC address as extracted from dhcp_header#clientHardwareAddress, assuming dhcp_header#hardwareType is 1 (Ethernet) - * and dhcp_header#hardwareAddressLength is 6 (MAC address length). Otherwise returns MacAddress#Zero - */ - MacAddress getClientHardwareAddress() const; - - /** - * Set a MAC address into the first 6 bytes of dhcp_header#clientHardwareAddress. This method also sets dhcp_header#hardwareType - * to 1 (Ethernet) and dhcp_header#hardwareAddressLength to 6 (MAC address length) - * @param[in] addr The MAC address to set - */ - void setClientHardwareAddress(const MacAddress& addr); - - /** - * @return DHCP message type as extracted from ::DHCPOPT_DHCP_MESSAGE_TYPE option. If this option doesn't exist the value of - * ::DHCP_UNKNOWN_MSG_TYPE is returned - */ - DhcpMessageType getMessageType() const; - - /** - * Set DHCP message type. This method searches for existing ::DHCPOPT_DHCP_MESSAGE_TYPE option. If found, it sets the requested - * message type as its value. If not, it creates a ::DHCPOPT_DHCP_MESSAGE_TYPE option and sets the requested message type as its - * value - * @param[in] msgType Message type to set - * @return True if message type was set successfully or false if msgType is ::DHCP_UNKNOWN_MSG_TYPE or if failed to add - * ::DHCPOPT_DHCP_MESSAGE_TYPE option - */ - bool setMessageType(DhcpMessageType msgType); - - /** - * @return The first DHCP option in the packet. If there are no DHCP options the returned value will contain - * a logical NULL (DhcpOption#isNull() == true) - */ - DhcpOption getFirstOptionData() const; - - /** - * Get the DHCP option that comes after a given option. If the given option was the last one, the - * returned value will contain a logical NULL (DhcpOption#isNull() == true) - * @param[in] dhcpOption A given DHCP option - * @return A DhcpOption object containing the option data that comes next, or logical NULL if the given DHCP - * option: (1) was the last one; (2) contains a logical NULL or (3) doesn't belong to this packet - */ - DhcpOption getNextOptionData(DhcpOption dhcpOption) const; - - /** - * Get a DHCP option by type - * @param[in] option DHCP option type - * @return A DhcpOption object containing the first DHCP option data that matches this type, or logical NULL - * (DhcpOption#isNull() == true) if no such option found - */ - DhcpOption getOptionData(DhcpOptionTypes option) const; - - /** - * @return The number of DHCP options in this layer - */ - size_t getOptionsCount() const; - - /** - * Add a new DHCP option at the end of the layer - * @param[in] optionBuilder A DhcpOptionBuilder object that contains the requested DHCP option data to add - * @return A DhcpOption object containing the newly added DHCP option data or logical NULL - * (DhcpOption#isNull() == true) if addition failed - */ - DhcpOption addOption(const DhcpOptionBuilder& optionBuilder); - - /** - * Add a new DHCP option after an existing one - * @param[in] optionBuilder A DhcpOptionBuilder object that contains the requested DHCP option data to add - * @param[in] prevOption The DHCP option type which the newly added option will come after - * @return A DhcpOption object containing the newly added DHCP option data or logical NULL - * (DhcpOption#isNull() == true) if addition failed - */ - DhcpOption addOptionAfter(const DhcpOptionBuilder& optionBuilder, DhcpOptionTypes prevOption); - - /** - * Remove an existing DHCP option from the layer - * @param[in] optionType The DHCP option type to remove - * @return True if DHCP option was successfully removed or false if type wasn't found or if removal failed - */ - bool removeOption(DhcpOptionTypes optionType); - - /** - * Remove all DHCP options in this layer - * @return True if all DHCP options were successfully removed or false if removal failed for some reason - */ - bool removeAllOptions(); - - // implement abstract methods - - /** - * Does nothing for this layer (DhcpLayer is always last) - */ - void parseNextLayer() {} - - /** - * @return The size of @ref dhcp_header + size of options - */ - size_t getHeaderLen() const { return m_DataLen; } - - /** - * Calculate the following fields: - * - @ref dhcp_header#magicNumber = DHCP magic number (0x63538263) - * - @ref dhcp_header#opCode = ::DHCP_BOOTREQUEST for message types: ::DHCP_DISCOVER, ::DHCP_REQUEST, ::DHCP_DECLINE, ::DHCP_RELEASE, - * ::DHCP_INFORM, ::DHCP_UNKNOWN_MSG_TYPE - * ::DHCP_BOOTREPLY for message types: ::DHCP_OFFER, ::DHCP_ACK, ::DHCP_NAK - * - @ref dhcp_header#hardwareType = 1 (Ethernet) - * - @ref dhcp_header#hardwareAddressLength = 6 (MAC address length) - */ - void computeCalculateFields(); - - std::string toString() const; - - OsiModelLayer getOsiModelLayer() const { return OsiModelApplicationLayer; } - - private: - - uint8_t* getOptionsBasePtr() const { return m_Data + sizeof(dhcp_header); } - - TLVRecordReader m_OptionReader; - - void initDhcpLayer(size_t numOfBytesToAllocate); - - DhcpOption addOptionAt(const DhcpOptionBuilder& optionBuilder, int offset); - }; -} +namespace pcpp { + +/** + * @struct dhcp_header + * Represents a DHCP protocol header + */ +#pragma pack(push, 1) +struct dhcp_header { + /** BootP opcode */ + uint8_t opCode; + /** Hardware type, set to 1 (Ethernet) by default */ + uint8_t hardwareType; + /** Hardware address length, set to 6 (MAC address length) by default */ + uint8_t hardwareAddressLength; + /** Hop count */ + uint8_t hops; + /** DHCP/BootP transaction ID */ + uint32_t transactionID; + /** The elapsed time, in seconds since the client sent its first BOOTREQUEST + * message */ + uint16_t secondsElapsed; + /** BootP flags */ + uint16_t flags; + /** Client IPv4 address */ + uint32_t clientIpAddress; + /** Your IPv4 address */ + uint32_t yourIpAddress; + /** Server IPv4 address */ + uint32_t serverIpAddress; + /** Gateway IPv4 address */ + uint32_t gatewayIpAddress; + /** Client hardware address, by default contains the MAC address (only 6 first + * bytes are used) */ + uint8_t clientHardwareAddress[16]; + /** BootP server name */ + uint8_t serverName[64]; + /** BootP boot file name */ + uint8_t bootFilename[128]; + /** DHCP magic number (set to the default value of 0x63538263) */ + uint32_t magicNumber; +}; +#pragma pack(pop) + +/** + * BootP opcodes + */ +enum BootpOpCodes { + /** BootP request */ + DHCP_BOOTREQUEST = 1, + /** BootP reply */ + DHCP_BOOTREPLY = 2 +}; + +/** + * DHCP message types + */ +enum DhcpMessageType { + /** Unknown message type */ + DHCP_UNKNOWN_MSG_TYPE = 0, + /** Discover message type */ + DHCP_DISCOVER = 1, + /** Offer message type */ + DHCP_OFFER = 2, + /** Request message type */ + DHCP_REQUEST = 3, + /** Decline message type */ + DHCP_DECLINE = 4, + /** Acknowledge message type */ + DHCP_ACK = 5, + /** Non-acknowledge message type */ + DHCP_NAK = 6, + /** Release message type */ + DHCP_RELEASE = 7, + /** Inform message type */ + DHCP_INFORM = 8 +}; + +/** + * DHCP option types. + */ +enum DhcpOptionTypes { + /** Unknown option type */ + DHCPOPT_UNKNOWN = -1, + /** Pad */ + DHCPOPT_PAD = 0, + /** Subnet Mask Value */ + DHCPOPT_SUBNET_MASK = 1, + /** Time Offset in Seconds from UTC */ + DHCPOPT_TIME_OFFSET = 2, + /** N/4 Router addresses */ + DHCPOPT_ROUTERS = 3, + /** N/4 Timeserver addresses */ + DHCPOPT_TIME_SERVERS = 4, + /** N/4 IEN-116 Server addresses */ + DHCPOPT_NAME_SERVERS = 5, + /** N/4 DNS Server addresses */ + DHCPOPT_DOMAIN_NAME_SERVERS = 6, + /** N/4 Logging Server addresses */ + DHCPOPT_LOG_SERVERS = 7, + /** N/4 Quotes Server addresses */ + DHCPOPT_QUOTES_SERVERS = 8, + /** N/4 Quotes Server addresses */ + DHCPOPT_LPR_SERVERS = 9, + /** N/4 Quotes Server addresses */ + DHCPOPT_IMPRESS_SERVERS = 10, + /** N/4 RLP Server addresses */ + DHCPOPT_RESOURCE_LOCATION_SERVERS = 11, + /** Hostname string */ + DHCPOPT_HOST_NAME = 12, + /** Size of boot file in 512 byte chunks */ + DHCPOPT_BOOT_SIZE = 13, + /** Client to dump and name the file to dump it to */ + DHCPOPT_MERIT_DUMP = 14, + /** The DNS domain name of the client */ + DHCPOPT_DOMAIN_NAME = 15, + /** Swap Server address */ + DHCPOPT_SWAP_SERVER = 16, + /** Path name for root disk */ + DHCPOPT_ROOT_PATH = 17, + /** Path name for more BOOTP info */ + DHCPOPT_EXTENSIONS_PATH = 18, + /** Enable/Disable IP Forwarding */ + DHCPOPT_IP_FORWARDING = 19, + /** Enable/Disable Source Routing */ + DHCPOPT_NON_LOCAL_SOURCE_ROUTING = 20, + /** Routing Policy Filters */ + DHCPOPT_POLICY_FILTER = 21, + /** Max Datagram Reassembly Size */ + DHCPOPT_MAX_DGRAM_REASSEMBLY = 22, + /** Default IP Time to Live */ + DEFAULT_IP_TTL = 23, + /** Path MTU Aging Timeout */ + DHCPOPT_PATH_MTU_AGING_TIMEOUT = 24, + /** Path MTU Plateau Table */ + PATH_MTU_PLATEAU_TABLE = 25, + /** Interface MTU Size */ + DHCPOPT_INTERFACE_MTU = 26, + /** All Subnets are Local */ + DHCPOPT_ALL_SUBNETS_LOCAL = 27, + /** Broadcast Address */ + DHCPOPT_BROADCAST_ADDRESS = 28, + /** Perform Mask Discovery */ + DHCPOPT_PERFORM_MASK_DISCOVERY = 29, + /** Provide Mask to Others */ + DHCPOPT_MASK_SUPPLIER = 30, + /** Perform Router Discovery */ + DHCPOPT_ROUTER_DISCOVERY = 31, + /** Router Solicitation Address */ + DHCPOPT_ROUTER_SOLICITATION_ADDRESS = 32, + /** Static Routing Table */ + DHCPOPT_STATIC_ROUTES = 33, + /** Trailer Encapsulation */ + DHCPOPT_TRAILER_ENCAPSULATION = 34, + /** ARP Cache Timeout */ + DHCPOPT_ARP_CACHE_TIMEOUT = 35, + /** IEEE802.3 Encapsulation */ + DHCPOPT_IEEE802_3_ENCAPSULATION = 36, + /** Default TCP Time to Live */ + DHCPOPT_DEFAULT_TCP_TTL = 37, + /** TCP Keepalive Interval */ + DHCPOPT_TCP_KEEPALIVE_INTERVAL = 38, + /** TCP Keepalive Garbage */ + DHCPOPT_TCP_KEEPALIVE_GARBAGE = 39, + /** NIS Domain Name */ + DHCPOPT_NIS_DOMAIN = 40, + /** NIS Server Addresses */ + DHCPOPT_NIS_SERVERS = 41, + /** NTP Server Addresses */ + DHCPOPT_NTP_SERVERS = 42, + /** Vendor Specific Information */ + DHCPOPT_VENDOR_ENCAPSULATED_OPTIONS = 43, + /** NETBIOS Name Servers */ + DHCPOPT_NETBIOS_NAME_SERVERS = 44, + /** NETBIOS Datagram Distribution */ + DHCPOPT_NETBIOS_DD_SERVER = 45, + /** NETBIOS Node Type */ + DHCPOPT_NETBIOS_NODE_TYPE = 46, + /** NETBIOS Scope */ + DHCPOPT_NETBIOS_SCOPE = 47, + /** X Window Font Server */ + DHCPOPT_FONT_SERVERS = 48, + /** X Window Display Manager */ + DHCPOPT_X_DISPLAY_MANAGER = 49, + /** Requested IP Address */ + DHCPOPT_DHCP_REQUESTED_ADDRESS = 50, + /** IP Address Lease Time */ + DHCPOPT_DHCP_LEASE_TIME = 51, + /** Overload "sname" or "file" */ + DHCPOPT_DHCP_OPTION_OVERLOAD = 52, + /** DHCP Message Type */ + DHCPOPT_DHCP_MESSAGE_TYPE = 53, + /** DHCP Server Identification */ + DHCPOPT_DHCP_SERVER_IDENTIFIER = 54, + /** Parameter Request List */ + DHCPOPT_DHCP_PARAMETER_REQUEST_LIST = 55, + /** DHCP Error Message */ + DHCPOPT_DHCP_MESSAGE = 56, + /** DHCP Maximum Message Size */ + DHCPOPT_DHCP_MAX_MESSAGE_SIZE = 57, + /** DHCP Renewal (T1) Time */ + DHCPOPT_DHCP_RENEWAL_TIME = 58, + /** DHCP Rebinding (T2) Time */ + DHCPOPT_DHCP_REBINDING_TIME = 59, + /** Class Identifier */ + DHCPOPT_VENDOR_CLASS_IDENTIFIER = 60, + /** Class Identifier */ + DHCPOPT_DHCP_CLIENT_IDENTIFIER = 61, + /** NetWare/IP Domain Name */ + DHCPOPT_NWIP_DOMAIN_NAME = 62, + /** NetWare/IP sub Options */ + DHCPOPT_NWIP_SUBOPTIONS = 63, + /** NIS+ v3 Client Domain Name */ + DHCPOPT_NIS_DOMAIN_NAME = 64, + /** NIS+ v3 Server Addresses */ + DHCPOPT_NIS_SERVER_ADDRESS = 65, + /** TFTP Server Name */ + DHCPOPT_TFTP_SERVER_NAME = 66, + /** Boot File Name */ + DHCPOPT_BOOTFILE_NAME = 67, + /** Home Agent Addresses */ + DHCPOPT_HOME_AGENT_ADDRESS = 68, + /** Simple Mail Server (SMTP) Addresses */ + DHCPOPT_SMTP_SERVER = 69, + /** Post Office (POP3) Server Addresses */ + DHCPOPT_POP3_SERVER = 70, + /** Network News (NNTP) Server Addresses */ + DHCPOPT_NNTP_SERVER = 71, + /** WWW Server Addresses */ + DHCPOPT_WWW_SERVER = 72, + /** Finger Server Addresses */ + DHCPOPT_FINGER_SERVER = 73, + /** Chat (IRC) Server Addresses */ + DHCPOPT_IRC_SERVER = 74, + /** StreetTalk Server Addresses */ + DHCPOPT_STREETTALK_SERVER = 75, + /** ST Directory Assist. Addresses */ + DHCPOPT_STDA_SERVER = 76, + /** User Class Information */ + DHCPOPT_USER_CLASS = 77, + /** Directory Agent Information */ + DHCPOPT_DIRECTORY_AGENT = 78, + /** Service Location Agent Scope */ + DHCPOPT_SERVICE_SCOPE = 79, + /** Rapid Commit */ + DHCPOPT_RAPID_COMMIT = 80, + /** Fully Qualified Domain Name */ + DHCPOPT_FQDN = 81, + /** Relay Agent Information */ + DHCPOPT_DHCP_AGENT_OPTIONS = 82, + /** Internet Storage Name Service */ + DHCPOPT_ISNS = 83, + /** Novell Directory Services */ + DHCPOPT_NDS_SERVERS = 85, + /** Novell Directory Services */ + DHCPOPT_NDS_TREE_NAME = 86, + /** Novell Directory Services */ + DHCPOPT_NDS_CONTEXT = 87, + /** BCMCS Controller Domain Name list */ + DHCPOPT_BCMCS_CONTROLLER_DOMAIN_NAME_LIST = 88, + /** BCMCS Controller IPv4 address option */ + DHCPOPT_BCMCS_CONTROLLER_IPV4_ADDRESS = 89, + /** Authentication */ + DHCPOPT_AUTHENTICATION = 90, + /** Client Last Transaction Time */ + DHCPOPT_CLIENT_LAST_TXN_TIME = 91, + /** Associated IP */ + DHCPOPT_ASSOCIATED_IP = 92, + /** Client System Architecture */ + DHCPOPT_CLIENT_SYSTEM = 93, + /** Client Network Device Interface */ + DHCPOPT_CLIENT_NDI = 94, + /** Lightweight Directory Access Protocol [ */ + DHCPOPT_LDAP = 95, + /** UUID/GUID-based Client Identifier */ + DHCPOPT_UUID_GUID = 97, + /** Open Group's User Authentication */ + DHCPOPT_USER_AUTH = 98, + /** GEOCONF_CIVIC */ + DHCPOPT_GEOCONF_CIVIC = 99, + /** IEEE 1003.1 TZ String */ + DHCPOPT_PCODE = 100, + /** Reference to the TZ Database */ + DHCPOPT_TCODE = 101, + /** NetInfo Parent Server Address */ + DHCPOPT_NETINFO_ADDRESS = 112, + /** NetInfo Parent Server Tag */ + DHCPOPT_NETINFO_TAG = 113, + /** URL */ + DHCPOPT_URL = 114, + /** DHCP Auto-Configuration */ + DHCPOPT_AUTO_CONFIG = 116, + /** Name Service Search */ + DHCPOPT_NAME_SERVICE_SEARCH = 117, + /** Subnet Selection Option */ + DHCPOPT_SUBNET_SELECTION = 118, + /** DNS Domain Search List */ + DHCPOPT_DOMAIN_SEARCH = 119, + /** SIP Servers DHCP Option */ + DHCPOPT_SIP_SERVERS = 120, + /** Classless Static Route Option */ + DHCPOPT_CLASSLESS_STATIC_ROUTE = 121, + /** CableLabs Client Configuration */ + DHCPOPT_CCC = 122, + /** GeoConf Option */ + DHCPOPT_GEOCONF = 123, + /** Vendor-Identifying Vendor Class */ + DHCPOPT_V_I_VENDOR_CLASS = 124, + /** Vendor-Identifying Vendor-Specific Information */ + DHCPOPT_V_I_VENDOR_OPTS = 125, + /** OPTION_PANA_AGENT */ + DHCPOPT_OPTION_PANA_AGENT = 136, + /** OPTION_V4_LOST */ + DHCPOPT_OPTION_V4_LOST = 137, + /** CAPWAP Access Controller addresses */ + DHCPOPT_OPTION_CAPWAP_AC_V4 = 138, + /** A Series Of Suboptions */ + DHCPOPT_OPTION_IPV4_ADDRESS_MOS = 139, + /** A Series Of Suboptions */ + DHCPOPT_OPTION_IPV4_FQDN_MOS = 140, + /** List of domain names to search for SIP User Agent Configuration */ + DHCPOPT_SIP_UA_CONFIG = 141, + /** ANDSF IPv4 Address Option for DHCPv4 */ + DHCPOPT_OPTION_IPV4_ADDRESS_ANDSF = 142, + /** Geospatial Location with Uncertainty [RF */ + DHCPOPT_GEOLOC = 144, + /** Forcerenew Nonce Capable */ + DHCPOPT_FORCERENEW_NONCE_CAPABLE = 145, + /** Information for selecting RDNSS */ + DHCPOPT_RDNSS_SELECTION = 146, + /** Status code and optional N byte text message describing status */ + DHCPOPT_STATUS_CODE = 151, + /** Absolute time (seconds since Jan 1, 1970) message was sent */ + DHCPOPT_BASE_TIME = 152, + /** Number of seconds in the past when client entered current state */ + DHCPOPT_START_TIME_OF_STATE = 153, + /** Absolute time (seconds since Jan 1, 1970) for beginning of query */ + DHCPOPT_QUERY_START_TIME = 154, + /** Absolute time (seconds since Jan 1, 1970) for end of query */ + DHCPOPT_QUERY_END_TIME = 155, + /** State of IP address */ + DHCPOPT_DHCP_STATE = 156, + /** Indicates information came from local or remote server */ + DHCPOPT_DATA_SOURCE = 157, + /** Includes one or multiple lists of PCP server IP addresses; each list is + treated as a separate PCP server */ + DHCPOPT_OPTION_V4_PCP_SERVER = 158, + /** This option is used to configure a set of ports bound to a shared IPv4 + address */ + DHCPOPT_OPTION_V4_PORTPARAMS = 159, + /** DHCP Captive-Portal */ + DHCPOPT_CAPTIVE_PORTAL = 160, + /** Manufacturer Usage Descriptions */ + DHCPOPT_OPTION_MUD_URL_V4 = 161, + /** Etherboot */ + DHCPOPT_ETHERBOOT = 175, + /** IP Telephone */ + DHCPOPT_IP_TELEPHONE = 176, + /** Magic string = F1:00:74:7E */ + DHCPOPT_PXELINUX_MAGIC = 208, + /** Configuration file */ + DHCPOPT_CONFIGURATION_FILE = 209, + /** Path Prefix Option */ + DHCPOPT_PATH_PREFIX = 210, + /** Reboot Time */ + DHCPOPT_REBOOT_TIME = 211, + /** OPTION_6RD with N/4 6rd BR addresses */ + DHCPOPT_OPTION_6RD = 212, + /** Access Network Domain Name */ + DHCPOPT_OPTION_V4_ACCESS_DOMAIN = 213, + /** Subnet Allocation Option */ + DHCPOPT_SUBNET_ALLOCATION = 220, + /** Virtual Subnet Selection (VSS) Option */ + DHCPOPT_VIRTUAL_SUBNET_SELECTION = 221, + /** End (last option) */ + DHCPOPT_END = 255 +}; + +/** + * @class DhcpOption + * A wrapper class for DHCP options. This class does not create or modify DHCP + * option records, but rather serves as a wrapper and provides useful methods + * for setting and retrieving data to/from them + */ +class DhcpOption : public TLVRecord { + public: + /** + * A c'tor for this class that gets a pointer to the option raw data (byte + * array) + * @param[in] optionRawData A pointer to the option raw data + */ + explicit DhcpOption(uint8_t* optionRawData) : TLVRecord(optionRawData) {} + + /** + * A d'tor for this class, currently does nothing + */ + virtual ~DhcpOption() {} + + /** + * Retrieve DHCP option data as IPv4 address. Relevant only if option value is + * indeed an IPv4 address + * @return DHCP option data as IPv4 address + */ + IPv4Address getValueAsIpAddr() const { return getValueAs(); } + + /** + * Set DHCP option data as IPv4 address. This method copies the 4 bytes of the + * IP address to the option value + * @param[in] addr The IPv4 address to set + * @param[in] valueOffset An optional parameter that specifies where to start + * set the option data (default set to 0). For example: if option data is 20 + * bytes long and you want to set the IP address in the 4 last bytes then use + * this method like this: setValueIpAddr(your_addr, 16) + */ + void setValueIpAddr(const IPv4Address& addr, int valueOffset = 0) { + setValue(addr.toInt(), valueOffset); + } + + /** + * Retrieve DHCP option data as string. Relevant only if option value is + * indeed a string + * @param[in] valueOffset An optional parameter that specifies where to start + * copy the DHCP option data. For example: when retrieving Client FQDN option, + * you may ignore the flags and RCODE fields using this method like this: + * getValueAsString(3). The default is 0 - start copying from the beginning of + * option data + * @return DHCP option data as string + */ + std::string getValueAsString(int valueOffset = 0) const { + if (m_Data == nullptr || m_Data->recordLen - valueOffset < 1) + return ""; + + return std::string((const char*)m_Data->recordValue + valueOffset, + (int)m_Data->recordLen - valueOffset); + } + + /** + * Set DHCP option data as string. This method copies the string to the option + * value. If the string is longer than option length the string is trimmed so + * it will fit the option length + * @param[in] stringValue The string to set + * @param[in] valueOffset An optional parameter that specifies where to start + * set the option data (default set to 0). For example: if option data is 20 + * bytes long and you want to set a 6 char-long string in the 6 last bytes + * then use this method like this: setValueString("string", 14) + */ + void setValueString(const std::string& stringValue, int valueOffset = 0) { + // calculate the maximum length of the destination buffer + size_t len = (size_t)m_Data->recordLen - (size_t)valueOffset; + + // use the length of input string if a buffer is large enough for whole + // string + if (stringValue.length() < len) + len = stringValue.length(); + + memcpy(m_Data->recordValue + valueOffset, stringValue.data(), len); + } + + /** + * Check if a pointer can be assigned to the TLV record data + * @param[in] recordRawData A pointer to the TLV record raw data + * @param[in] tlvDataLen The size of the TLV record raw data + * @return True if data is valid and can be assigned + */ + static bool canAssign(const uint8_t* recordRawData, size_t tlvDataLen) { + auto data = (TLVRawData*)recordRawData; + if (data == nullptr) + return false; + + if (tlvDataLen < sizeof(TLVRawData::recordType)) + return false; + + if (data->recordType == (uint8_t)DHCPOPT_END || + data->recordType == (uint8_t)DHCPOPT_PAD) + return true; + + return TLVRecord::canAssign(recordRawData, tlvDataLen); + } + + // implement abstract methods + + size_t getTotalSize() const { + if (m_Data == nullptr) + return 0; + + if (m_Data->recordType == (uint8_t)DHCPOPT_END || + m_Data->recordType == (uint8_t)DHCPOPT_PAD) + return sizeof(uint8_t); + + return sizeof(uint8_t) * 2 + (size_t)m_Data->recordLen; + } + + size_t getDataSize() const { + if (m_Data == nullptr) + return 0; + + if (m_Data->recordType == (uint8_t)DHCPOPT_END || + m_Data->recordType == (uint8_t)DHCPOPT_PAD) + return 0; + + return m_Data->recordLen; + } +}; + +/** + * @class DhcpOptionBuilder + * A class for building DHCP options. This builder receives the option + * parameters in its c'tor, builds the DHCP option raw buffer and provides a + * build() method to get a DhcpOption object out of it + */ +class DhcpOptionBuilder : public TLVRecordBuilder { + public: + /** + * A c'tor for building DHCP options which their value is a byte array. The + * DhcpOption object can later be retrieved by calling build() + * @param[in] optionType DHCP option type + * @param[in] optionValue A buffer containing the option value. This buffer is + * read-only and isn't modified in any way + * @param[in] optionValueLen DHCP option value length in bytes + */ + DhcpOptionBuilder(DhcpOptionTypes optionType, const uint8_t* optionValue, + uint8_t optionValueLen) + : TLVRecordBuilder((uint8_t)optionType, optionValue, optionValueLen) {} + + /** + * A c'tor for building DHCP options which have a 1-byte value. The DhcpOption + * object can later be retrieved by calling build() + * @param[in] optionType DHCP option type + * @param[in] optionValue A 1-byte option value + */ + DhcpOptionBuilder(DhcpOptionTypes optionType, uint8_t optionValue) + : TLVRecordBuilder((uint8_t)optionType, optionValue) {} + + /** + * A c'tor for building DHCP options which have a 2-byte value. The DhcpOption + * object can later be retrieved by calling build() + * @param[in] optionType DHCP option type + * @param[in] optionValue A 2-byte option value + */ + DhcpOptionBuilder(DhcpOptionTypes optionType, uint16_t optionValue) + : TLVRecordBuilder((uint8_t)optionType, optionValue) {} + + /** + * A c'tor for building DHCP options which have a 4-byte value. The DhcpOption + * object can later be retrieved by calling build() + * @param[in] optionType DHCP option type + * @param[in] optionValue A 4-byte option value + */ + DhcpOptionBuilder(DhcpOptionTypes optionType, uint32_t optionValue) + : TLVRecordBuilder((uint8_t)optionType, optionValue) {} + + /** + * A c'tor for building DHCP options which have an IPv4Address value. The + * DhcpOption object can later be retrieved by calling build() + * @param[in] optionType DHCP option type + * @param[in] optionValue The IPv4 address option value + */ + DhcpOptionBuilder(DhcpOptionTypes optionType, const IPv4Address& optionValue) + : TLVRecordBuilder((uint8_t)optionType, optionValue) {} + + /** + * A c'tor for building DHCP options which have a string value. The DhcpOption + * object can later be retrieved by calling build() + * @param[in] optionType DHCP option type + * @param[in] optionValue The string option value + */ + DhcpOptionBuilder(DhcpOptionTypes optionType, const std::string& optionValue) + : TLVRecordBuilder((uint8_t)optionType, optionValue) {} + + /** + * A copy c'tor which copies all the data from another instance of + * DhcpOptionBuilder + * @param[in] other The instance to copy from + */ + DhcpOptionBuilder(const DhcpOptionBuilder& other) : TLVRecordBuilder(other) {} + + /** + * Assignment operator that copies all data from another instance of + * DhcpOptionBuilder + * @param[in] other The instance to assign from + * @return A reference to the assignee + */ + DhcpOptionBuilder& operator=(const DhcpOptionBuilder& other) { + TLVRecordBuilder::operator=(other); + return *this; + } + + /** + * Build the DhcpOption object out of the parameters defined in the c'tor + * @return The DhcpOption object + */ + DhcpOption build() const; +}; + +/** + * @class DhcpLayer + * Represents a DHCP (Dynamic Host Configuration Protocol) protocol layer + */ +class DhcpLayer : public Layer { + public: + /** + * 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 + */ + DhcpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); + + /** + * A constructor that creates the layer from scratch. Adds a + * ::DHCPOPT_DHCP_MESSAGE_TYPE and a ::DHCPOPT_END options + * @param[in] msgType A DHCP message type to be set + * @param[in] clientMacAddr A client MAC address to set in + * dhcp_header#clientHardwareAddress field + */ + DhcpLayer(DhcpMessageType msgType, const MacAddress& clientMacAddr); + + /** + * A constructor that creates the layer from scratch with clean data + */ + DhcpLayer(); + + /** + * A destructor for this layer + */ + virtual ~DhcpLayer() {} + + /** + * Get a pointer to the DHCP header. Notice this points directly to the data, + * so every change will change the actual packet data + * @return A pointer to the @ref dhcp_header + */ + dhcp_header* getDhcpHeader() const { return (dhcp_header*)m_Data; } + + /** + * @return The BootP opcode of this message + */ + BootpOpCodes getOpCode() const { + return (BootpOpCodes)getDhcpHeader()->opCode; + } + + /** + * @return The client IPv4 address (as extracted from + * dhcp_header#clientIpAddress converted to IPv4Address object) + */ + IPv4Address getClientIpAddress() const { + return getDhcpHeader()->clientIpAddress; + } + + /** + * Set the client IPv4 address in dhcp_header#clientIpAddress + * @param[in] addr The IPv4 address to set + */ + void setClientIpAddress(const IPv4Address& addr) { + getDhcpHeader()->clientIpAddress = addr.toInt(); + } + + /** + * @return The server IPv4 address (as extracted from + * dhcp_header#serverIpAddress converted to IPv4Address object) + */ + IPv4Address getServerIpAddress() const { + return getDhcpHeader()->serverIpAddress; + } + + /** + * Set the server IPv4 address in dhcp_header#serverIpAddress + * @param[in] addr The IPv4 address to set + */ + void setServerIpAddress(const IPv4Address& addr) { + getDhcpHeader()->serverIpAddress = addr.toInt(); + } + + /** + * @return Your IPv4 address (as extracted from dhcp_header#yourIpAddress + * converted to IPv4Address object) + */ + IPv4Address getYourIpAddress() const { + return getDhcpHeader()->yourIpAddress; + } + + /** + * Set your IPv4 address in dhcp_header#yourIpAddress + * @param[in] addr The IPv4 address to set + */ + void setYourIpAddress(const IPv4Address& addr) { + getDhcpHeader()->yourIpAddress = addr.toInt(); + } + + /** + * @return Gateway IPv4 address (as extracted from + * dhcp_header#gatewayIpAddress converted to IPv4Address object) + */ + IPv4Address getGatewayIpAddress() const { + return getDhcpHeader()->gatewayIpAddress; + } + + /** + * Set the gateway IPv4 address in dhcp_header#gatewayIpAddress + * @param[in] addr The IPv4 address to set + */ + void setGatewayIpAddress(const IPv4Address& addr) { + getDhcpHeader()->gatewayIpAddress = addr.toInt(); + } + + /** + * @return The client MAC address as extracted from + * dhcp_header#clientHardwareAddress, assuming dhcp_header#hardwareType is 1 + * (Ethernet) and dhcp_header#hardwareAddressLength is 6 (MAC address length). + * Otherwise returns MacAddress#Zero + */ + MacAddress getClientHardwareAddress() const; + + /** + * Set a MAC address into the first 6 bytes of + * dhcp_header#clientHardwareAddress. This method also sets + * dhcp_header#hardwareType to 1 (Ethernet) and + * dhcp_header#hardwareAddressLength to 6 (MAC address length) + * @param[in] addr The MAC address to set + */ + void setClientHardwareAddress(const MacAddress& addr); + + /** + * @return DHCP message type as extracted from ::DHCPOPT_DHCP_MESSAGE_TYPE + * option. If this option doesn't exist the value of + * ::DHCP_UNKNOWN_MSG_TYPE is returned + */ + DhcpMessageType getMessageType() const; + + /** + * Set DHCP message type. This method searches for existing + * ::DHCPOPT_DHCP_MESSAGE_TYPE option. If found, it sets the requested message + * type as its value. If not, it creates a ::DHCPOPT_DHCP_MESSAGE_TYPE option + * and sets the requested message type as its value + * @param[in] msgType Message type to set + * @return True if message type was set successfully or false if msgType is + * ::DHCP_UNKNOWN_MSG_TYPE or if failed to add + * ::DHCPOPT_DHCP_MESSAGE_TYPE option + */ + bool setMessageType(DhcpMessageType msgType); + + /** + * @return The first DHCP option in the packet. If there are no DHCP options + * the returned value will contain a logical NULL (DhcpOption#isNull() == + * true) + */ + DhcpOption getFirstOptionData() const; + + /** + * Get the DHCP option that comes after a given option. If the given option + * was the last one, the returned value will contain a logical NULL + * (DhcpOption#isNull() == true) + * @param[in] dhcpOption A given DHCP option + * @return A DhcpOption object containing the option data that comes next, or + * logical NULL if the given DHCP option: (1) was the last one; (2) contains a + * logical NULL or (3) doesn't belong to this packet + */ + DhcpOption getNextOptionData(DhcpOption dhcpOption) const; + + /** + * Get a DHCP option by type + * @param[in] option DHCP option type + * @return A DhcpOption object containing the first DHCP option data that + * matches this type, or logical NULL (DhcpOption#isNull() == true) if no such + * option found + */ + DhcpOption getOptionData(DhcpOptionTypes option) const; + + /** + * @return The number of DHCP options in this layer + */ + size_t getOptionsCount() const; + + /** + * Add a new DHCP option at the end of the layer + * @param[in] optionBuilder A DhcpOptionBuilder object that contains the + * requested DHCP option data to add + * @return A DhcpOption object containing the newly added DHCP option data or + * logical NULL (DhcpOption#isNull() == true) if addition failed + */ + DhcpOption addOption(const DhcpOptionBuilder& optionBuilder); + + /** + * Add a new DHCP option after an existing one + * @param[in] optionBuilder A DhcpOptionBuilder object that contains the + * requested DHCP option data to add + * @param[in] prevOption The DHCP option type which the newly added option + * will come after + * @return A DhcpOption object containing the newly added DHCP option data or + * logical NULL (DhcpOption#isNull() == true) if addition failed + */ + DhcpOption addOptionAfter(const DhcpOptionBuilder& optionBuilder, + DhcpOptionTypes prevOption); + + /** + * Remove an existing DHCP option from the layer + * @param[in] optionType The DHCP option type to remove + * @return True if DHCP option was successfully removed or false if type + * wasn't found or if removal failed + */ + bool removeOption(DhcpOptionTypes optionType); + + /** + * Remove all DHCP options in this layer + * @return True if all DHCP options were successfully removed or false if + * removal failed for some reason + */ + bool removeAllOptions(); + + // implement abstract methods + + /** + * Does nothing for this layer (DhcpLayer is always last) + */ + void parseNextLayer() {} + + /** + * @return The size of @ref dhcp_header + size of options + */ + size_t getHeaderLen() const { return m_DataLen; } + + /** + * Calculate the following fields: + * - @ref dhcp_header#magicNumber = DHCP magic number (0x63538263) + * - @ref dhcp_header#opCode = ::DHCP_BOOTREQUEST for message types: + * ::DHCP_DISCOVER, ::DHCP_REQUEST, ::DHCP_DECLINE, ::DHCP_RELEASE, + * ::DHCP_INFORM, ::DHCP_UNKNOWN_MSG_TYPE + * ::DHCP_BOOTREPLY for message types: + * ::DHCP_OFFER, ::DHCP_ACK, ::DHCP_NAK + * - @ref dhcp_header#hardwareType = 1 (Ethernet) + * - @ref dhcp_header#hardwareAddressLength = 6 (MAC address length) + */ + void computeCalculateFields(); + + std::string toString() const; + + OsiModelLayer getOsiModelLayer() const { return OsiModelApplicationLayer; } + + private: + uint8_t* getOptionsBasePtr() const { return m_Data + sizeof(dhcp_header); } + + TLVRecordReader m_OptionReader; + + void initDhcpLayer(size_t numOfBytesToAllocate); + + DhcpOption addOptionAt(const DhcpOptionBuilder& optionBuilder, int offset); +}; +} // namespace pcpp #endif /* PACKETPP_DHCP_LAYER */ diff --git a/Packet++/header/DhcpV6Layer.h b/Packet++/header/DhcpV6Layer.h index 0799cb99a8..c6d6fff09f 100644 --- a/Packet++/header/DhcpV6Layer.h +++ b/Packet++/header/DhcpV6Layer.h @@ -10,443 +10,461 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - /** - * DHCPv6 message types - */ - enum DhcpV6MessageType - { - /** Unknown message type */ - DHCPV6_UNKNOWN_MSG_TYPE = 0, - /** Solicit message type (Client to Server) */ - DHCPV6_SOLICIT = 1, - /** Advertise message type (Server to Client) */ - DHCPV6_ADVERTISE = 2, - /** Request message type (Client to Server) */ - DHCPV6_REQUEST = 3, - /** Confirm message type (Client to Server) */ - DHCPV6_CONFIRM = 4, - /** Renew message type (Client to Server) */ - DHCPV6_RENEW = 5, - /** Rebind message type (Client to Server) */ - DHCPV6_REBIND = 6, - /** Reply message type (Server to Client) */ - DHCPV6_REPLY = 7, - /** Release message type (Client to Server) */ - DHCPV6_RELEASE = 8, - /** Decline message type (Client to Server) */ - DHCPV6_DECLINE = 9, - /** Reconfigure message type (Server to Client) */ - DHCPV6_RECONFIGURE = 10, - /** Information-Request message type (Client to Server) */ - DHCPV6_INFORMATION_REQUEST = 11, - /** Relay-Forward message type (Relay agent to Server) */ - DHCPV6_RELAY_FORWARD = 12, - /** Relay-Reply message type (Server to Relay agent) */ - DHCPV6_RELAY_REPLY = 13 - }; - - /** - * DHCPv6 option types. - * Resources for more information: - * - https://onlinelibrary.wiley.com/doi/pdf/10.1002/9781118073810.app2 - * - https://datatracker.ietf.org/doc/html/rfc5970 - * - https://datatracker.ietf.org/doc/html/rfc6607 - * - https://datatracker.ietf.org/doc/html/rfc8520 - */ - enum DhcpV6OptionType - { - /** Unknown option type */ - DHCPV6_OPT_UNKNOWN = 0, - /** Client Identifier (DUID of client) */ - DHCPV6_OPT_CLIENTID = 1, - /** Server Identifier (DUID of server) */ - DHCPV6_OPT_SERVERID = 2, - /** Identity Association for Non-temporary addresses */ - DHCPV6_OPT_IA_NA = 3, - /** Identity Association for Temporary addresses */ - DHCPV6_OPT_IA_TA = 4, - /** IA Address option */ - DHCPV6_OPT_IAADDR = 5, - /** Option Request Option */ - DHCPV6_OPT_ORO = 6, - /** Preference setting */ - DHCPV6_OPT_PREFERENCE = 7, - /** The amount of time since the client began the current DHCP transaction */ - DHCPV6_OPT_ELAPSED_TIME = 8, - /** The DHCP message being relayed by a relay agent */ - DHCPV6_OPT_RELAY_MSG = 9, - /** Authentication information */ - DHCPV6_OPT_AUTH = 11, - /** Server unicast */ - DHCPV6_OPT_UNICAST = 12, - /** Status code */ - DHCPV6_OPT_STATUS_CODE = 13, - /** Rapid commit */ - DHCPV6_OPT_RAPID_COMMIT = 14, - /** User class */ - DHCPV6_OPT_USER_CLASS = 15, - /** Vendor class */ - DHCPV6_OPT_VENDOR_CLASS = 16, - /** Vendor specific information */ - DHCPV6_OPT_VENDOR_OPTS = 17, - /** Interface ID */ - DHCPV6_OPT_INTERFACE_ID = 18, - /** Reconfigure Message */ - DHCPV6_OPT_RECONF_MSG = 19, - /** Reconfigure Accept */ - DHCPV6_OPT_RECONF_ACCEPT = 20, - /** SIP Servers Domain Name */ - DHCPV6_OPT_SIP_SERVERS_D = 21, - /** SIP Servers IPv6 Address List */ - DHCPV6_OPT_SIP_SERVERS_A = 22, - /** DNS Recursive Name Server */ - DHCPV6_OPT_DNS_SERVERS = 23, - /** Domain Search List */ - DHCPV6_OPT_DOMAIN_LIST = 24, - /** Identity Association for Prefix Delegation */ - DHCPV6_OPT_IA_PD = 25, - /** IA_PD Prefix */ - DHCPV6_OPT_IAPREFIX = 26, - /** Network Information Service (NIS) Servers */ - DHCPV6_OPT_NIS_SERVERS = 27, - /** Network Information Service v2 (NIS+) Servers */ - DHCPV6_OPT_NISP_SERVERS = 28, - /** Network Information Service (NIS) domain name */ - DHCPV6_OPT_NIS_DOMAIN_NAME = 29, - /** Network Information Service v2 (NIS+) domain name */ - DHCPV6_OPT_NISP_DOMAIN_NAME = 30, - /** Simple Network Time Protocol (SNTP) servers */ - DHCPV6_OPT_SNTP_SERVERS = 31, - /** Information Refresh */ - DHCPV6_OPT_INFORMATION_REFRESH_TIME = 32, - /** Broadcast and Multicast Service (BCMCS) Domain Name List */ - DHCPV6_OPT_BCMCS_SERVER_D = 33, - /** Broadcast and Multicast Service (BCMCS) IPv6 Address List */ - DHCPV6_OPT_BCMCS_SERVER_A = 34, - /** Geographical location in civic (e.g., postal) format */ - DHCPV6_OPT_GEOCONF_CIVIC = 36, - /** Relay Agent Remote ID */ - DHCPV6_OPT_REMOTE_ID = 37, - /** Relay Agent Subscriber ID */ - DHCPV6_OPT_SUBSCRIBER_ID = 38, - /** FQDN */ - DHCPV6_OPT_CLIENT_FQDN = 39, - /** One or more IPv6 addresses associated with PANA (Protocol for carrying Authentication for Network Access) Authentication Agents */ - DHCPV6_OPT_PANA_AGENT = 40, - /** Time zone to be used by the client in IEEE 1003.1 format */ - DHCPV6_OPT_NEW_POSIX_TIMEZONE = 41, - /** Time zone (TZ) database entry referred to by entry name */ - DHCPV6_OPT_NEW_TZDB_TIMEZONE = 42, - /** Relay Agent Echo Request */ - DHCPV6_OPT_ERO = 43, - /** Query option */ - DHCPV6_OPT_LQ_QUERY = 44, - /** Client Data */ - DHCPV6_OPT_CLIENT_DATA = 45, - /** Client Last Transaction Time */ - DHCPV6_OPT_CLT_TIME = 46, - /** Relay data */ - DHCPV6_OPT_LQ_RELAY_DATA = 47, - /** Client link */ - DHCPV6_OPT_LQ_CLIENT_LINK = 48, - /** Mobile IPv6 Home Network Information */ - DHCPV6_OPT_MIP6_HNINF = 49, - /** Mobile IPv6 Relay Agent */ - DHCPV6_OPT_MIP6_RELAY = 50, - /** Location to Service Translation (LoST) server domain name */ - DHCPV6_OPT_V6_LOST = 51, - /** Access Points (CAPWAP) Access Controller IPv6 addresses */ - DHCPV6_OPT_CAPWAP_AC_V6 = 52, - /** DHCPv6 Bulk LeaseQuery */ - DHCPV6_OPT_RELAY_ID = 53, - /** List of IPv6 addresses for servers providing particular types of IEEE 802.21 Mobility Service (MoS) */ - DHCPV6_OPT_IPH6_ADDRESS_MOS = 54, - /** List of FQDNs for servers providing particular types of IEEE 802.21 Mobility Service (MoS) */ - DHCPV6_OPT_IPV6_FQDN_MOS = 55, - /** Network Time Protocol (NTP) or Simple NTP (SNTP) Server Location */ - DHCPV6_OPT_NTP_SERVER = 56, - /** Boot File Uniform Resource Locator (URL) */ - DHCPV6_OPT_BOOTFILE_URL = 59, - /** Boot File Parameters */ - DHCPV6_OPT_BOOTFILE_PARAM = 60, - /** Client System Architecture Type */ - DHCPV6_OPT_CLIENT_ARCH_TYPE = 61, - /** Client Network Interface Identifier */ - DHCPV6_OPT_NII = 62, - /** ERP Local Domain Name */ - DHCPV6_OPT_ERP_LOCAL_DOMAIN_NAME = 65, - /** Relay supplied options */ - DHCPV6_OPT_RELAY_SUPPLIED_OPTIONS = 66, - /** Virtual Subnet Selection */ - DHCPV6_OPT_VSS = 68, - /** Client link layer */ - DHCPV6_OPT_CLIENT_LINKLAYER_ADDR = 79, - /** Manufacturer Usage Description */ - DHCPV6_OPT_MUD_URL = 112 - }; - - /** - * @class DhcpV6Option - * A wrapper class for DHCPv6 options. This class does not create or modify DHCP option records, but rather - * serves as a wrapper and provides useful methods for setting and retrieving data to/from them - */ - class DhcpV6Option : public TLVRecord - { - public: - /** - * A c'tor for this class that gets a pointer to the option raw data (byte array) - * @param[in] optionRawData A pointer to the option raw data - */ - explicit DhcpV6Option(uint8_t* optionRawData) : TLVRecord(optionRawData) { } - - /** - * A d'tor for this class, currently does nothing - */ - virtual ~DhcpV6Option() { } - - /** - * @return The option type converted to ::DhcpV6OptionType enum - */ - DhcpV6OptionType getType() const; - - /** - * @return The raw option value (byte array) as a hex string - */ - std::string getValueAsHexString() const; - - // implement abstract methods - - size_t getTotalSize() const; - size_t getDataSize() const; - }; - - /** - * @class DhcpV6OptionBuilder - * A class for building DHCPv6 options. This builder receives the option parameters in its c'tor, - * builds the DHCPv6 option raw buffer and provides a build() method to get a DhcpV6Option object out of it - */ - class DhcpV6OptionBuilder : public TLVRecordBuilder - { - public: - /** - * A c'tor for building DHCPv6 options from a string representing the hex stream of the raw byte value. - * The DhcpV6Option object can later be retrieved by calling build() - * @param[in] optionType DHCPv6 option type - * @param[in] optionValueAsHexStream The value as a hex stream string - */ - DhcpV6OptionBuilder(DhcpV6OptionType optionType, const std::string& optionValueAsHexStream) : - TLVRecordBuilder(static_cast(optionType), optionValueAsHexStream, true) { } - - /** - * A c'tor for building DHCPv6 options from a byte array representing their value. The DhcpV6Option object can be later - * retrieved by calling build() - * @param[in] optionType DHCPv6 option type - * @param[in] optionValue A buffer containing the option value. This buffer is read-only and isn't modified in any way. - * @param[in] optionValueLen Option value length in bytes - */ - DhcpV6OptionBuilder(DhcpV6OptionType optionType, const uint8_t* optionValue, uint8_t optionValueLen) : - TLVRecordBuilder(static_cast(optionType), optionValue, optionValueLen) {} - - /** - * Build the DhcpV6Option object out of the parameters defined in the c'tor - * @return The DhcpV6Option object - */ - DhcpV6Option build() const; - }; - - /** - * @struct dhcpv6_header - * Represents the basic DHCPv6 protocol header - */ - struct dhcpv6_header - { - /** DHCPv6 message type */ - uint8_t messageType; - /** DHCPv6 transaction ID (first byte) */ - uint8_t transactionId1; - /** DHCPv6 transaction ID (second byte) */ - uint8_t transactionId2; - /** DHCPv6 transaction ID (last byte) */ - uint8_t transactionId3; - }; - - /** - * @class DhcpV6Layer - * Represents a DHCPv6 (Dynamic Host Configuration Protocol version 6) protocol layer - */ - class DhcpV6Layer : public Layer - { - public: - - /** - * 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 - */ - DhcpV6Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); - - /** - * A constructor that creates the layer from scratch - * @param[in] messageType A DHCPv6 message type to be set - * @param[in] transactionId The transaction ID to be set. Notice the transaction ID is 3-byte long so the value shouldn't exceed 0xFFFFFF - */ - DhcpV6Layer(DhcpV6MessageType messageType, uint32_t transactionId); - - /** - * @return The message type of this DHCPv6 message - */ - DhcpV6MessageType getMessageType() const; - - /** - * @return The string value of the message type of this DHCPv6 message - */ - std::string getMessageTypeAsString() const; - - /** - * Set the message type for this layer - * @param[in] messageType The message type to set - */ - void setMessageType(DhcpV6MessageType messageType); - - /** - * @return The transaction ID of this DHCPv6 message - */ - uint32_t getTransactionID() const; - - /** - * Set the transaction ID for this DHCPv6 message - * @param[in] transactionId The transaction ID value to set - */ - void setTransactionID(uint32_t transactionId) const; - - /** - * @return The first DHCPv6 option in the packet. If there are no DHCPv6 options the returned value will contain - * a logical NULL (DhcpV6Option#isNull() == true) - */ - DhcpV6Option getFirstOptionData() const; - - /** - * Get the DHCPv6 option that comes after a given option. If the given option was the last one, the - * returned value will contain a logical NULL (DhcpV6Option#isNull() == true) - * @param[in] dhcpv6Option A given DHCPv6 option - * @return A DhcpV6Option object containing the option data that comes next, or logical NULL if the given DHCPv6 - * option: (1) was the last one; (2) contains a logical NULL or (3) doesn't belong to this packet - */ - DhcpV6Option getNextOptionData(DhcpV6Option dhcpv6Option) const; - - /** - * Get a DHCPv6 option by type - * @param[in] option DHCPv6 option type - * @return A DhcpV6OptionType object containing the first DHCP option data that matches this type, or logical NULL - * (DhcpV6Option#isNull() == true) if no such option found - */ - DhcpV6Option getOptionData(DhcpV6OptionType option) const; - - /** - * @return The number of DHCPv6 options in this layer - */ - size_t getOptionCount() const; - - /** - * Add a new DHCPv6 option at the end of the layer - * @param[in] optionBuilder A DhcpV6OptionBuilder object that contains the requested DHCPv6 option data to add - * @return A DhcpV6Option object containing the newly added DHCP option data or logical NULL - * (DhcpV6Option#isNull() == true) if addition failed - */ - DhcpV6Option addOption(const DhcpV6OptionBuilder& optionBuilder); - - /** - * Add a new DHCPv6 option after an existing one - * @param[in] optionBuilder A DhcpV6OptionBuilder object that contains the requested DHCPv6 option data to add - * @param[in] optionType The DHCPv6 option type which the newly added option will come after - * @return A DhcpV6Option object containing the newly added DHCPv6 option data or logical NULL - * (DhcpV6Option#isNull() == true) if addition failed - */ - DhcpV6Option addOptionAfter(const DhcpV6OptionBuilder& optionBuilder, DhcpV6OptionType optionType); - - /** - * Add a new DHCPv6 option before an existing one - * @param[in] optionBuilder A DhcpV6OptionBuilder object that contains the requested DHCPv6 option data to add - * @param[in] optionType The DHCPv6 option type which the newly added option will come before - * @return A DhcpV6Option object containing the newly added DHCPv6 option data or logical NULL - * (DhcpV6Option#isNull() == true) if addition failed - */ - DhcpV6Option addOptionBefore(const DhcpV6OptionBuilder& optionBuilder, DhcpV6OptionType optionType); - - /** - * Remove an existing DHCPv6 option from the layer - * @param[in] optionType The DHCPv6 option type to remove - * @return True if DHCPv6 option was successfully removed or false if type wasn't found or if removal failed - */ - bool removeOption(DhcpV6OptionType optionType); - - /** - * Remove all DHCPv6 options in this layer - * @return True if all DHCPv6 options were successfully removed or false if removal failed for some reason - */ - bool removeAllOptions(); - - /** - * A static method that checks whether a port is considered as a DHCPv6 port - * @param[in] port The port number to check - * @return True if this is a DHCPv6 port number, false otherwise - */ - static inline bool isDhcpV6Port(uint16_t port); - - /** - * A static method that validates the input data - * @param[in] data The pointer to the beginning of a byte stream of an DHCPv6 layer - * @param[in] dataLen The length of the byte stream - * @return True if the data is valid and can represent an DHCPv6 layer - */ - static inline bool isDataValid(const uint8_t* data, size_t dataLen); - - // implement abstract methods - - /** - * Does nothing for this layer (DhcpV6Layer is always last) - */ - void parseNextLayer() {} - - /** - * @return The size of @ref dhcpv6_header + size of options - */ - size_t getHeaderLen() const { return m_DataLen; } - - /** - * Does nothing for this layer - */ - void computeCalculateFields() {} - - std::string toString() const; - - OsiModelLayer getOsiModelLayer() const { return OsiModelApplicationLayer; } - - private: - uint8_t* getOptionsBasePtr() const { return m_Data + sizeof(dhcpv6_header); } - dhcpv6_header* getDhcpHeader() const { return (dhcpv6_header*)m_Data; } - DhcpV6Option addOptionAt(const DhcpV6OptionBuilder& optionBuilder, int offset); - - TLVRecordReader m_OptionReader; - }; - - - // implementation of inline methods - - bool DhcpV6Layer::isDhcpV6Port(uint16_t port) - { - return (port == 546) || (port == 547); - - } - - bool DhcpV6Layer::isDataValid(const uint8_t* data, size_t dataLen) - { - return data && dataLen >= sizeof(dhcpv6_header); - } +namespace pcpp { +/** + * DHCPv6 message types + */ +enum DhcpV6MessageType { + /** Unknown message type */ + DHCPV6_UNKNOWN_MSG_TYPE = 0, + /** Solicit message type (Client to Server) */ + DHCPV6_SOLICIT = 1, + /** Advertise message type (Server to Client) */ + DHCPV6_ADVERTISE = 2, + /** Request message type (Client to Server) */ + DHCPV6_REQUEST = 3, + /** Confirm message type (Client to Server) */ + DHCPV6_CONFIRM = 4, + /** Renew message type (Client to Server) */ + DHCPV6_RENEW = 5, + /** Rebind message type (Client to Server) */ + DHCPV6_REBIND = 6, + /** Reply message type (Server to Client) */ + DHCPV6_REPLY = 7, + /** Release message type (Client to Server) */ + DHCPV6_RELEASE = 8, + /** Decline message type (Client to Server) */ + DHCPV6_DECLINE = 9, + /** Reconfigure message type (Server to Client) */ + DHCPV6_RECONFIGURE = 10, + /** Information-Request message type (Client to Server) */ + DHCPV6_INFORMATION_REQUEST = 11, + /** Relay-Forward message type (Relay agent to Server) */ + DHCPV6_RELAY_FORWARD = 12, + /** Relay-Reply message type (Server to Relay agent) */ + DHCPV6_RELAY_REPLY = 13 +}; + +/** + * DHCPv6 option types. + * Resources for more information: + * - https://onlinelibrary.wiley.com/doi/pdf/10.1002/9781118073810.app2 + * - https://datatracker.ietf.org/doc/html/rfc5970 + * - https://datatracker.ietf.org/doc/html/rfc6607 + * - https://datatracker.ietf.org/doc/html/rfc8520 + */ +enum DhcpV6OptionType { + /** Unknown option type */ + DHCPV6_OPT_UNKNOWN = 0, + /** Client Identifier (DUID of client) */ + DHCPV6_OPT_CLIENTID = 1, + /** Server Identifier (DUID of server) */ + DHCPV6_OPT_SERVERID = 2, + /** Identity Association for Non-temporary addresses */ + DHCPV6_OPT_IA_NA = 3, + /** Identity Association for Temporary addresses */ + DHCPV6_OPT_IA_TA = 4, + /** IA Address option */ + DHCPV6_OPT_IAADDR = 5, + /** Option Request Option */ + DHCPV6_OPT_ORO = 6, + /** Preference setting */ + DHCPV6_OPT_PREFERENCE = 7, + /** The amount of time since the client began the current DHCP transaction */ + DHCPV6_OPT_ELAPSED_TIME = 8, + /** The DHCP message being relayed by a relay agent */ + DHCPV6_OPT_RELAY_MSG = 9, + /** Authentication information */ + DHCPV6_OPT_AUTH = 11, + /** Server unicast */ + DHCPV6_OPT_UNICAST = 12, + /** Status code */ + DHCPV6_OPT_STATUS_CODE = 13, + /** Rapid commit */ + DHCPV6_OPT_RAPID_COMMIT = 14, + /** User class */ + DHCPV6_OPT_USER_CLASS = 15, + /** Vendor class */ + DHCPV6_OPT_VENDOR_CLASS = 16, + /** Vendor specific information */ + DHCPV6_OPT_VENDOR_OPTS = 17, + /** Interface ID */ + DHCPV6_OPT_INTERFACE_ID = 18, + /** Reconfigure Message */ + DHCPV6_OPT_RECONF_MSG = 19, + /** Reconfigure Accept */ + DHCPV6_OPT_RECONF_ACCEPT = 20, + /** SIP Servers Domain Name */ + DHCPV6_OPT_SIP_SERVERS_D = 21, + /** SIP Servers IPv6 Address List */ + DHCPV6_OPT_SIP_SERVERS_A = 22, + /** DNS Recursive Name Server */ + DHCPV6_OPT_DNS_SERVERS = 23, + /** Domain Search List */ + DHCPV6_OPT_DOMAIN_LIST = 24, + /** Identity Association for Prefix Delegation */ + DHCPV6_OPT_IA_PD = 25, + /** IA_PD Prefix */ + DHCPV6_OPT_IAPREFIX = 26, + /** Network Information Service (NIS) Servers */ + DHCPV6_OPT_NIS_SERVERS = 27, + /** Network Information Service v2 (NIS+) Servers */ + DHCPV6_OPT_NISP_SERVERS = 28, + /** Network Information Service (NIS) domain name */ + DHCPV6_OPT_NIS_DOMAIN_NAME = 29, + /** Network Information Service v2 (NIS+) domain name */ + DHCPV6_OPT_NISP_DOMAIN_NAME = 30, + /** Simple Network Time Protocol (SNTP) servers */ + DHCPV6_OPT_SNTP_SERVERS = 31, + /** Information Refresh */ + DHCPV6_OPT_INFORMATION_REFRESH_TIME = 32, + /** Broadcast and Multicast Service (BCMCS) Domain Name List */ + DHCPV6_OPT_BCMCS_SERVER_D = 33, + /** Broadcast and Multicast Service (BCMCS) IPv6 Address List */ + DHCPV6_OPT_BCMCS_SERVER_A = 34, + /** Geographical location in civic (e.g., postal) format */ + DHCPV6_OPT_GEOCONF_CIVIC = 36, + /** Relay Agent Remote ID */ + DHCPV6_OPT_REMOTE_ID = 37, + /** Relay Agent Subscriber ID */ + DHCPV6_OPT_SUBSCRIBER_ID = 38, + /** FQDN */ + DHCPV6_OPT_CLIENT_FQDN = 39, + /** One or more IPv6 addresses associated with PANA (Protocol for carrying + Authentication for Network Access) Authentication Agents */ + DHCPV6_OPT_PANA_AGENT = 40, + /** Time zone to be used by the client in IEEE 1003.1 format */ + DHCPV6_OPT_NEW_POSIX_TIMEZONE = 41, + /** Time zone (TZ) database entry referred to by entry name */ + DHCPV6_OPT_NEW_TZDB_TIMEZONE = 42, + /** Relay Agent Echo Request */ + DHCPV6_OPT_ERO = 43, + /** Query option */ + DHCPV6_OPT_LQ_QUERY = 44, + /** Client Data */ + DHCPV6_OPT_CLIENT_DATA = 45, + /** Client Last Transaction Time */ + DHCPV6_OPT_CLT_TIME = 46, + /** Relay data */ + DHCPV6_OPT_LQ_RELAY_DATA = 47, + /** Client link */ + DHCPV6_OPT_LQ_CLIENT_LINK = 48, + /** Mobile IPv6 Home Network Information */ + DHCPV6_OPT_MIP6_HNINF = 49, + /** Mobile IPv6 Relay Agent */ + DHCPV6_OPT_MIP6_RELAY = 50, + /** Location to Service Translation (LoST) server domain name */ + DHCPV6_OPT_V6_LOST = 51, + /** Access Points (CAPWAP) Access Controller IPv6 addresses */ + DHCPV6_OPT_CAPWAP_AC_V6 = 52, + /** DHCPv6 Bulk LeaseQuery */ + DHCPV6_OPT_RELAY_ID = 53, + /** List of IPv6 addresses for servers providing particular types of IEEE + 802.21 Mobility Service (MoS) */ + DHCPV6_OPT_IPH6_ADDRESS_MOS = 54, + /** List of FQDNs for servers providing particular types of IEEE 802.21 + Mobility Service (MoS) */ + DHCPV6_OPT_IPV6_FQDN_MOS = 55, + /** Network Time Protocol (NTP) or Simple NTP (SNTP) Server Location */ + DHCPV6_OPT_NTP_SERVER = 56, + /** Boot File Uniform Resource Locator (URL) */ + DHCPV6_OPT_BOOTFILE_URL = 59, + /** Boot File Parameters */ + DHCPV6_OPT_BOOTFILE_PARAM = 60, + /** Client System Architecture Type */ + DHCPV6_OPT_CLIENT_ARCH_TYPE = 61, + /** Client Network Interface Identifier */ + DHCPV6_OPT_NII = 62, + /** ERP Local Domain Name */ + DHCPV6_OPT_ERP_LOCAL_DOMAIN_NAME = 65, + /** Relay supplied options */ + DHCPV6_OPT_RELAY_SUPPLIED_OPTIONS = 66, + /** Virtual Subnet Selection */ + DHCPV6_OPT_VSS = 68, + /** Client link layer */ + DHCPV6_OPT_CLIENT_LINKLAYER_ADDR = 79, + /** Manufacturer Usage Description */ + DHCPV6_OPT_MUD_URL = 112 +}; +/** + * @class DhcpV6Option + * A wrapper class for DHCPv6 options. This class does not create or modify DHCP + * option records, but rather serves as a wrapper and provides useful methods + * for setting and retrieving data to/from them + */ +class DhcpV6Option : public TLVRecord { + public: + /** + * A c'tor for this class that gets a pointer to the option raw data (byte + * array) + * @param[in] optionRawData A pointer to the option raw data + */ + explicit DhcpV6Option(uint8_t* optionRawData) : TLVRecord(optionRawData) {} + + /** + * A d'tor for this class, currently does nothing + */ + virtual ~DhcpV6Option() {} + + /** + * @return The option type converted to ::DhcpV6OptionType enum + */ + DhcpV6OptionType getType() const; + + /** + * @return The raw option value (byte array) as a hex string + */ + std::string getValueAsHexString() const; + + // implement abstract methods + + size_t getTotalSize() const; + size_t getDataSize() const; +}; + +/** + * @class DhcpV6OptionBuilder + * A class for building DHCPv6 options. This builder receives the option + * parameters in its c'tor, builds the DHCPv6 option raw buffer and provides a + * build() method to get a DhcpV6Option object out of it + */ +class DhcpV6OptionBuilder : public TLVRecordBuilder { + public: + /** + * A c'tor for building DHCPv6 options from a string representing the hex + * stream of the raw byte value. The DhcpV6Option object can later be + * retrieved by calling build() + * @param[in] optionType DHCPv6 option type + * @param[in] optionValueAsHexStream The value as a hex stream string + */ + DhcpV6OptionBuilder(DhcpV6OptionType optionType, + const std::string& optionValueAsHexStream) + : TLVRecordBuilder(static_cast(optionType), + optionValueAsHexStream, true) {} + + /** + * A c'tor for building DHCPv6 options from a byte array representing their + * value. The DhcpV6Option object can be later retrieved by calling build() + * @param[in] optionType DHCPv6 option type + * @param[in] optionValue A buffer containing the option value. This buffer is + * read-only and isn't modified in any way. + * @param[in] optionValueLen Option value length in bytes + */ + DhcpV6OptionBuilder(DhcpV6OptionType optionType, const uint8_t* optionValue, + uint8_t optionValueLen) + : TLVRecordBuilder(static_cast(optionType), optionValue, + optionValueLen) {} + + /** + * Build the DhcpV6Option object out of the parameters defined in the c'tor + * @return The DhcpV6Option object + */ + DhcpV6Option build() const; +}; + +/** + * @struct dhcpv6_header + * Represents the basic DHCPv6 protocol header + */ +struct dhcpv6_header { + /** DHCPv6 message type */ + uint8_t messageType; + /** DHCPv6 transaction ID (first byte) */ + uint8_t transactionId1; + /** DHCPv6 transaction ID (second byte) */ + uint8_t transactionId2; + /** DHCPv6 transaction ID (last byte) */ + uint8_t transactionId3; +}; + +/** + * @class DhcpV6Layer + * Represents a DHCPv6 (Dynamic Host Configuration Protocol version 6) protocol + * layer + */ +class DhcpV6Layer : public Layer { + public: + /** + * 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 + */ + DhcpV6Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); + + /** + * A constructor that creates the layer from scratch + * @param[in] messageType A DHCPv6 message type to be set + * @param[in] transactionId The transaction ID to be set. Notice the + * transaction ID is 3-byte long so the value shouldn't exceed 0xFFFFFF + */ + DhcpV6Layer(DhcpV6MessageType messageType, uint32_t transactionId); + + /** + * @return The message type of this DHCPv6 message + */ + DhcpV6MessageType getMessageType() const; + + /** + * @return The string value of the message type of this DHCPv6 message + */ + std::string getMessageTypeAsString() const; + + /** + * Set the message type for this layer + * @param[in] messageType The message type to set + */ + void setMessageType(DhcpV6MessageType messageType); + + /** + * @return The transaction ID of this DHCPv6 message + */ + uint32_t getTransactionID() const; + + /** + * Set the transaction ID for this DHCPv6 message + * @param[in] transactionId The transaction ID value to set + */ + void setTransactionID(uint32_t transactionId) const; + + /** + * @return The first DHCPv6 option in the packet. If there are no DHCPv6 + * options the returned value will contain a logical NULL + * (DhcpV6Option#isNull() == true) + */ + DhcpV6Option getFirstOptionData() const; + + /** + * Get the DHCPv6 option that comes after a given option. If the given option + * was the last one, the returned value will contain a logical NULL + * (DhcpV6Option#isNull() == true) + * @param[in] dhcpv6Option A given DHCPv6 option + * @return A DhcpV6Option object containing the option data that comes next, + * or logical NULL if the given DHCPv6 option: (1) was the last one; (2) + * contains a logical NULL or (3) doesn't belong to this packet + */ + DhcpV6Option getNextOptionData(DhcpV6Option dhcpv6Option) const; + + /** + * Get a DHCPv6 option by type + * @param[in] option DHCPv6 option type + * @return A DhcpV6OptionType object containing the first DHCP option data + * that matches this type, or logical NULL (DhcpV6Option#isNull() == true) if + * no such option found + */ + DhcpV6Option getOptionData(DhcpV6OptionType option) const; + + /** + * @return The number of DHCPv6 options in this layer + */ + size_t getOptionCount() const; + + /** + * Add a new DHCPv6 option at the end of the layer + * @param[in] optionBuilder A DhcpV6OptionBuilder object that contains the + * requested DHCPv6 option data to add + * @return A DhcpV6Option object containing the newly added DHCP option data + * or logical NULL (DhcpV6Option#isNull() == true) if addition failed + */ + DhcpV6Option addOption(const DhcpV6OptionBuilder& optionBuilder); + + /** + * Add a new DHCPv6 option after an existing one + * @param[in] optionBuilder A DhcpV6OptionBuilder object that contains the + * requested DHCPv6 option data to add + * @param[in] optionType The DHCPv6 option type which the newly added option + * will come after + * @return A DhcpV6Option object containing the newly added DHCPv6 option data + * or logical NULL (DhcpV6Option#isNull() == true) if addition failed + */ + DhcpV6Option addOptionAfter(const DhcpV6OptionBuilder& optionBuilder, + DhcpV6OptionType optionType); + + /** + * Add a new DHCPv6 option before an existing one + * @param[in] optionBuilder A DhcpV6OptionBuilder object that contains the + * requested DHCPv6 option data to add + * @param[in] optionType The DHCPv6 option type which the newly added option + * will come before + * @return A DhcpV6Option object containing the newly added DHCPv6 option data + * or logical NULL (DhcpV6Option#isNull() == true) if addition failed + */ + DhcpV6Option addOptionBefore(const DhcpV6OptionBuilder& optionBuilder, + DhcpV6OptionType optionType); + + /** + * Remove an existing DHCPv6 option from the layer + * @param[in] optionType The DHCPv6 option type to remove + * @return True if DHCPv6 option was successfully removed or false if type + * wasn't found or if removal failed + */ + bool removeOption(DhcpV6OptionType optionType); + + /** + * Remove all DHCPv6 options in this layer + * @return True if all DHCPv6 options were successfully removed or false if + * removal failed for some reason + */ + bool removeAllOptions(); + + /** + * A static method that checks whether a port is considered as a DHCPv6 port + * @param[in] port The port number to check + * @return True if this is a DHCPv6 port number, false otherwise + */ + static inline bool isDhcpV6Port(uint16_t port); + + /** + * A static method that validates the input data + * @param[in] data The pointer to the beginning of a byte stream of an DHCPv6 + * layer + * @param[in] dataLen The length of the byte stream + * @return True if the data is valid and can represent an DHCPv6 layer + */ + static inline bool isDataValid(const uint8_t* data, size_t dataLen); + + // implement abstract methods + + /** + * Does nothing for this layer (DhcpV6Layer is always last) + */ + void parseNextLayer() {} + + /** + * @return The size of @ref dhcpv6_header + size of options + */ + size_t getHeaderLen() const { return m_DataLen; } + + /** + * Does nothing for this layer + */ + void computeCalculateFields() {} + + std::string toString() const; + + OsiModelLayer getOsiModelLayer() const { return OsiModelApplicationLayer; } + + private: + uint8_t* getOptionsBasePtr() const { return m_Data + sizeof(dhcpv6_header); } + dhcpv6_header* getDhcpHeader() const { return (dhcpv6_header*)m_Data; } + DhcpV6Option addOptionAt(const DhcpV6OptionBuilder& optionBuilder, + int offset); + + TLVRecordReader m_OptionReader; +}; + +// implementation of inline methods + +bool DhcpV6Layer::isDhcpV6Port(uint16_t port) { + return (port == 546) || (port == 547); +} + +bool DhcpV6Layer::isDataValid(const uint8_t* data, size_t dataLen) { + return data && dataLen >= sizeof(dhcpv6_header); } -# endif // PACKETPP_DHCPV6_LAYER + +} // namespace pcpp +#endif // PACKETPP_DHCPV6_LAYER diff --git a/Packet++/header/DnsLayer.h b/Packet++/header/DnsLayer.h index ffb5a56703..538869638a 100644 --- a/Packet++/header/DnsLayer.h +++ b/Packet++/header/DnsLayer.h @@ -12,552 +12,645 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - - /** - * @struct dnshdr - * Represents the fixed part of the DNS header, meaning the part that doesn't include the DNS data (queries, answers, authorities - * and additional records) - */ +namespace pcpp { + +/** + * @struct dnshdr + * Represents the fixed part of the DNS header, meaning the part that doesn't + * include the DNS data (queries, answers, authorities and additional records) + */ #pragma pack(push, 1) - struct dnshdr - { - /** DNS query identification */ - uint16_t transactionID; +struct dnshdr { + /** DNS query identification */ + uint16_t transactionID; #if (BYTE_ORDER == LITTLE_ENDIAN) - uint16_t - /** Recursion desired flag */ - recursionDesired:1, - /** Truncated flag */ - truncation:1, - /** Authoritative answer flag */ - authoritativeAnswer:1, - /** Operation Code */ - opcode:4, - /** Query/Response flag */ - queryOrResponse:1, - /** Return Code */ - responseCode:4, - /** Checking disabled flag */ - checkingDisabled:1, - /** Authenticated data flag */ - authenticData:1, - /** Zero flag (Reserved) */ - zero:1, - /** Recursion available flag */ - recursionAvailable:1; + uint16_t + /** Recursion desired flag */ + recursionDesired : 1, + /** Truncated flag */ + truncation : 1, + /** Authoritative answer flag */ + authoritativeAnswer : 1, + /** Operation Code */ + opcode : 4, + /** Query/Response flag */ + queryOrResponse : 1, + /** Return Code */ + responseCode : 4, + /** Checking disabled flag */ + checkingDisabled : 1, + /** Authenticated data flag */ + authenticData : 1, + /** Zero flag (Reserved) */ + zero : 1, + /** Recursion available flag */ + recursionAvailable : 1; #elif (BYTE_ORDER == BIG_ENDIAN) - uint16_t - /** Query/Response flag */ - queryOrResponse:1, - /** Operation Code */ - opcode:4, - /** Authoritative answer flag */ - authoritativeAnswer:1, - /** Truncated flag */ - truncation:1, - /** Recursion desired flag */ - recursionDesired:1, - /** Recursion available flag */ - recursionAvailable:1, - /** Zero flag (Reserved) */ - zero:1, - /** Authenticated data flag */ - authenticData:1, - /** Checking disabled flag */ - checkingDisabled:1, - /** Return Code */ - responseCode:4; + uint16_t + /** Query/Response flag */ + queryOrResponse : 1, + /** Operation Code */ + opcode : 4, + /** Authoritative answer flag */ + authoritativeAnswer : 1, + /** Truncated flag */ + truncation : 1, + /** Recursion desired flag */ + recursionDesired : 1, + /** Recursion available flag */ + recursionAvailable : 1, + /** Zero flag (Reserved) */ + zero : 1, + /** Authenticated data flag */ + authenticData : 1, + /** Checking disabled flag */ + checkingDisabled : 1, + /** Return Code */ + responseCode : 4; #endif - /** Number of DNS query records in packet */ - uint16_t numberOfQuestions; - /** Number of DNS answer records in packet */ - uint16_t numberOfAnswers; - /** Number of authority records in packet */ - uint16_t numberOfAuthority; - /** Number of additional records in packet */ - uint16_t numberOfAdditional; - }; + /** Number of DNS query records in packet */ + uint16_t numberOfQuestions; + /** Number of DNS answer records in packet */ + uint16_t numberOfAnswers; + /** Number of authority records in packet */ + uint16_t numberOfAuthority; + /** Number of additional records in packet */ + uint16_t numberOfAdditional; +}; #pragma pack(pop) +// forward declarations +class DnsQuery; +class IDnsResource; +class DnsResource; +class IDnsResourceData; + +/** + * @class DnsLayer + * Represents the DNS protocol layer + */ +class DnsLayer : public Layer { + friend class IDnsResource; + friend class DnsQuery; + friend class DnsResource; + + public: + /** + * 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 + */ + DnsLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); + + /** + * A constructor that creates an empty DNS layer: all members of dnshdr are + * set to 0 and layer will contain no records + */ + DnsLayer(); + + /** + * A copy constructor for this layer + * @param[in] other The DNS layer to copy from + */ + DnsLayer(const DnsLayer& other); + + /** + * An assignment operator for this layer + * @param[in] other The DNS layer to assign + * @return A reference to the assignee + */ + DnsLayer& operator=(const DnsLayer& other); + + virtual ~DnsLayer(); + + /** + * Get a pointer to the DNS header (as opposed to the DNS data which is the + * queries, answers, etc. Data can be retrieved through the other methods of + * this layer. Notice the return value points directly to the data, so every + * change will change the actual packet data + * @return A pointer to the @ref dnshdr + */ + dnshdr* getDnsHeader() const; + + /** + * Searches for a DNS query by its name field. Notice this method returns only + * a query which its name equals to the requested name. If several queries + * match the requested name, the first one will be returned. If no queries + * match the requested name, NULL will be returned + * @param[in] name The name of the query to search + * @param[in] exactMatch Indicate whether to match the whole name or just a + * part of it + * @return The first matching DNS query or NULL if no queries were found + */ + DnsQuery* getQuery(const std::string& name, bool exactMatch) const; + + /** + * @return The first DNS query in the packet or NULL if packet doesn't contain + * any queries + */ + DnsQuery* getFirstQuery() const; + + /** + * Get the DNS query following a certain query + * @param[in] query A pointer to a DNS query that exist in the packet + * @return The DNS query following 'query'. If 'query' is NULL or 'query' is + * the last query in the packet NULL will be returned + */ + DnsQuery* getNextQuery(DnsQuery* query) const; + + /** + * @return The number of DNS queries in the packet + */ + size_t getQueryCount() const; + + /** + * Add a new DNS query to the layer + * @param[in] name The value that shall be set in the name field of the query + * @param[in] dnsType The value that shall be set in the DNS type field of the + * query + * @param[in] dnsClass The value that shall be set in the DNS class field of + * the query + * @return A pointer to the newly created DNS query or NULL if query could not + * be created (an appropriate error log message will be printed in this case) + */ + DnsQuery* addQuery(const std::string& name, DnsType dnsType, + DnsClass dnsClass); + + /** + * Add a new DNS query similar to an already existing DNS query. All query + * fields will be copied from the existing query + * @param[in] copyQuery The record to create the new record from. copyQuery + * won't be changed in any way + * @return A pointer to the newly created DNS query or NULL if query could not + * be created (an appropriate error log message will be printed in this case) + */ + DnsQuery* addQuery(DnsQuery* const copyQuery); + + /** + * Remove an existing query by name. If several queries matches the name, the + * first match will be removed + * @param[in] queryNameToRemove The name of the query to remove + * @param[in] exactMatch Indicate whether to match the whole name or just a + * part of it + * @return True if query was found and successfully removed or false if query + * was not found or couldn't be removed + */ + bool removeQuery(const std::string& queryNameToRemove, bool exactMatch); + + /** + * Remove an existing query + * @param[in] queryToRemove A pointer to the query to remove + * @return True if query was found and successfully removed or false if query + * was not found or couldn't be removed + */ + bool removeQuery(DnsQuery* queryToRemove); + + /** + * Searches for a DNS answer by its name field. Notice this method returns + * only an answer which its name equals to the requested name. If several + * answers match the requested name, the first one will be returned. If no + * answers match the requested name, NULL will be returned + * @param[in] name The name of the answer to search + * @param[in] exactMatch Indicate whether to match the whole name or just a + * part of it + * @return The first matching DNS answer or NULL if no answers were found + */ + DnsResource* getAnswer(const std::string& name, bool exactMatch) const; + + /** + * @return The first DNS answer in the packet or NULL if packet doesn't + * contain any answers + */ + DnsResource* getFirstAnswer() const; + + /** + * Get the DNS answer following a certain answer + * @param[in] answer A pointer to a DNS answer that exist in the packet + * @return The DNS answer following 'answer'. If 'answer' is NULL or 'answer' + * is the last answer in the packet NULL will be returned + */ + DnsResource* getNextAnswer(DnsResource* answer) const; + + /** + * @return The number of DNS answers in the packet + */ + size_t getAnswerCount() const; + + /** + * Add a new DNS answer to the layer + * @param[in] name The value that shall be set in the name field of the answer + * @param[in] dnsType The value that shall be set in the DNS type field of the + * answer + * @param[in] dnsClass The value that shall be set in the DNS class field of + * the answer + * @param[in] ttl The value that shall be set in the 'time-to-leave' field of + * the answer + * @param[in] data The answer data to be set. The type of the data should + * match the type of the DNS record (for example: DNS record of type A should + * have data of type IPv4DnsResourceData. Please see DnsResource#setData() for + * more info on this + * @return A pointer to the newly created DNS answer or NULL if answer could + * not be created (an appropriate error log message will be printed in this + * case) + */ + DnsResource* addAnswer(const std::string& name, DnsType dnsType, + DnsClass dnsClass, uint32_t ttl, + IDnsResourceData* data); + + /** + * Add a new DNS answer similar to an already existing DNS answer. All answer + * fields will be copied from the existing answer + * @param[in] copyAnswer The record to create the new record from. copyAnswer + * won't be changed in any way + * @return A pointer to the newly created DNS answer or NULL if query could + * not be created (an appropriate error log message will be printed in this + * case) + */ + DnsResource* addAnswer(DnsResource* const copyAnswer); + + /** + * Remove an existing answer by name. If several answers matches the name, the + * first match will be removed + * @param[in] answerNameToRemove The name of the answer to remove + * @param[in] exactMatch Indicate whether to match the whole name or just a + * part of it + * @return True if answer was found and successfully removed or false if + * answer was not found or couldn't be removed + */ + bool removeAnswer(const std::string& answerNameToRemove, bool exactMatch); + + /** + * Remove an existing answer + * @param[in] answerToRemove A pointer to the answer to remove + * @return True if answer was found and successfully removed or false if + * answer was not found or couldn't be removed + */ + bool removeAnswer(DnsResource* answerToRemove); + + /** + * Searches for a DNS authority by its name field. Notice this method returns + * only an authority which its name equals to the requested name. If several + * authorities match the requested name, the first one will be returned. If no + * authorities match the requested name, NULL will be returned + * @param[in] name The name of the authority to search + * @param[in] exactMatch Indicate whether to match the whole name or just a + * part of it + * @return The first matching DNS authority or NULL if no authorities were + * found + */ + DnsResource* getAuthority(const std::string& name, bool exactMatch) const; + + /** + * @return The first DNS authority in the packet or NULL if packet doesn't + * contain any authorities + */ + DnsResource* getFirstAuthority() const; + + /** + * Get the DNS authority following a certain authority + * @param[in] authority A pointer to a DNS authority that exist in the packet + * @return The DNS authority following 'authority'. If 'authority' is NULL or + * 'authority' is the last authority in the packet NULL will be returned + */ + DnsResource* getNextAuthority(DnsResource* authority) const; + + /** + * @return The number of DNS authorities in the packet + */ + size_t getAuthorityCount() const; + + /** + * Add a new DNS authority to the layer + * @param[in] name The value that shall be set in the name field of the + * authority + * @param[in] dnsType The value that shall be set in the DNS type field of the + * authority + * @param[in] dnsClass The value that shall be set in the DNS class field of + * the authority + * @param[in] ttl The value that shall be set in the 'time-to-leave' field of + * the authority + * @param[in] data The authority data to be set. The type of the data should + * match the type of the DNS record (for example: DNS record of type A should + * have data of type IPv4DnsResourceData. Please see DnsResource#setData() for + * more info on this + * @return A pointer to the newly created DNS authority or NULL if authority + * could not be created (an appropriate error log message will be printed in + * this case) + */ + DnsResource* addAuthority(const std::string& name, DnsType dnsType, + DnsClass dnsClass, uint32_t ttl, + IDnsResourceData* data); + + /** + * Add a new DNS authority similar to an already existing DNS authority. All + * authority fields will be copied from the existing authority + * @param[in] copyAuthority The record to create the new record from. + * copyAuthority won't be changed in any way + * @return A pointer to the newly created DNS authority or NULL if query could + * not be created (an appropriate error log message will be printed in this + * case) + */ + DnsResource* addAuthority(DnsResource* const copyAuthority); + + /** + * Remove an existing authority by name. If several authorities matches the + * name, the first match will be removed + * @param[in] authorityNameToRemove The name of the authority to remove + * @param[in] exactMatch Indicate whether to match the whole name or just a + * part of it + * @return True if authority was found and successfully removed or false if + * authority was not found or couldn't be removed + */ + bool removeAuthority(const std::string& authorityNameToRemove, + bool exactMatch); + + /** + * Remove an existing authority + * @param[in] authorityToRemove A pointer to the authority to remove + * @return True if authority was found and successfully removed or false if + * authority was not found or couldn't be removed + */ + bool removeAuthority(DnsResource* authorityToRemove); + + /** + * Searches for a DNS additional record by its name field. Notice this method + * returns only an additional record which its name equals to the requested + * name. If several additional records match the requested name, the first one + * will be returned. If no additional records match the requested name, NULL + * will be returned + * @param[in] name The name of the additional record to search + * @param[in] exactMatch Indicate whether to match the whole name or just a + * part of it + * @return The first matching DNS additional record or NULL if no additional + * records were found + */ + DnsResource* getAdditionalRecord(const std::string& name, + bool exactMatch) const; + + /** + * @return The first DNS additional record in the packet or NULL if packet + * doesn't contain any additional records + */ + DnsResource* getFirstAdditionalRecord() const; + + /** + * Get the DNS additional record following a certain additional record + * @param[in] additionalRecord A pointer to a DNS additional record that exist + * in the packet + * @return The DNS additional record following 'additionalRecord'. If + * 'additionalRecord' is NULL or 'additionalRecord' is the last additional + * record in the packet NULL will be returned + */ + DnsResource* getNextAdditionalRecord(DnsResource* additionalRecord) const; + + /** + * @return The number of DNS additional records in the packet + */ + size_t getAdditionalRecordCount() const; + + /** + * Add a new DNS additional record to the layer + * @param[in] name The value that shall be set in the name field of the + * additional record + * @param[in] dnsType The value that shall be set in the DNS type field of the + * additional record + * @param[in] dnsClass The value that shall be set in the DNS class field of + * the additional record + * @param[in] ttl The value that shall be set in the 'time-to-leave' field of + * the additional record + * @param[in] data The additional record data to be set. The type of the data + * should match the type of the DNS record (for example: DNS record of type A + * should have data of type IPv4DnsResourceData. Please see + * DnsResource#setData() for more info on this + * @return A pointer to the newly created DNS additional record or NULL if + * additional record could not be created (an appropriate error log message + * will be printed in this case) + */ + DnsResource* addAdditionalRecord(const std::string& name, DnsType dnsType, + DnsClass dnsClass, uint32_t ttl, + IDnsResourceData* data); + + /** + * Add a new DNS additional record to the layer that doesn't have DNS class + * and TTL. Instead these bytes may contains some arbitrary data. In the + * future I may add support for these kinds of additional data records. For + * now, these bytes are set as raw + * @param[in] name The value that shall be set in the name field of the + * additional record + * @param[in] dnsType The value that shall be set in the DNS type field of the + * additional record + * @param[in] customData1 Two bytes of the arbitrary data that will be set in + * the offset usually used for the DNS class + * @param[in] customData2 Four bytes of the arbitrary data that will be set in + * the offset usually used for the TTL + * @param[in] data The additional record data to be set. The type of the data + * should match the type of the DNS record. (for example: DNS record of type A + * should have data of type IPv4DnsResourceData. Please see + * DnsResource#setData() for more info on this + * @return A pointer to the newly created DNS additional record or NULL if + * additional record could not be created (an appropriate error log message + * will be printed in this case) + */ + DnsResource* addAdditionalRecord(const std::string& name, DnsType dnsType, + uint16_t customData1, uint32_t customData2, + IDnsResourceData* data); + + /** + * Add a new DNS additional record similar to an already existing DNS + * additional record. All additional record fields will be copied from the + * existing additional record + * @param[in] copyAdditionalRecord The record to create the new record from. + * copyAdditionalRecord won't be changed in any way + * @return A pointer to the newly created DNS additional record or NULL if + * query could not be created (an appropriate error log message will be + * printed in this case) + */ + DnsResource* addAdditionalRecord(DnsResource* const copyAdditionalRecord); + + /** + * Remove an existing additional record by name. If several additional records + * matches the name, the first match will be removed + * @param[in] additionalRecordNameToRemove The name of the additional record + * to remove + * @param[in] exactMatch Indicate whether to match the whole name or just a + * part of it + * @return True if additional record was found and successfully removed or + * false if additional record was not found or couldn't be removed + */ + bool removeAdditionalRecord(const std::string& additionalRecordNameToRemove, + bool exactMatch); + + /** + * Remove an existing additional record + * @param[in] additionalRecordToRemove A pointer to the additional record to + * remove + * @return True if additional record was found and successfully removed or + * false if additional record was not found or couldn't be removed + */ + bool removeAdditionalRecord(DnsResource* additionalRecordToRemove); + + // implement abstract methods + + /** + * Does nothing for this layer (DnsLayer is always last) + */ + void parseNextLayer() {} + + /** + * @return The size of the DNS data in the packet including he DNS header and + * size of all queries, answers, authorities and additional records + */ + size_t getHeaderLen() const { return m_DataLen; } // No layer above DNS + + /** + * Does nothing for this layer + */ + virtual void computeCalculateFields() {} + + std::string toString() const; + + OsiModelLayer getOsiModelLayer() const { return OsiModelApplicationLayer; } + + /** + * A static method that checks whether the port is considered as DNS + * @param[in] port The port number to be checked + * @return True if the port is associated with the DNS protocol + */ + static inline bool isDnsPort(uint16_t port); + + /** + * A static method that validates the input data + * @param[in] data The pointer to the beginning of a byte stream of a DNS + * packet + * @param[in] dataLen The length of the byte stream + * @param[in] dnsOverTcp Should be set to "true" if this is DNS is over TCP, + * otherwise set to "false" (which is also the default value) + * @return True if the data is valid and can represent a DNS packet + */ + static inline bool isDataValid(const uint8_t* data, size_t dataLen, + bool dnsOverTcp = false); + + protected: + DnsLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet, + size_t offsetAdjustment); + explicit DnsLayer(size_t offsetAdjustment); + + private: + IDnsResource* m_ResourceList; + DnsQuery* m_FirstQuery; + DnsResource* m_FirstAnswer; + DnsResource* m_FirstAuthority; + DnsResource* m_FirstAdditional; + uint16_t m_OffsetAdjustment; + + size_t getBasicHeaderSize(); + void init(size_t offsetAdjustment, bool callParseResource); + void initNewLayer(size_t offsetAdjustment); + + IDnsResource* getFirstResource(DnsResourceType resType) const; + void setFirstResource(DnsResourceType resType, IDnsResource* resource); + + using Layer::extendLayer; + bool extendLayer(int offsetInLayer, size_t numOfBytesToExtend, + IDnsResource* resource); + + using Layer::shortenLayer; + bool shortenLayer(int offsetInLayer, size_t numOfBytesToShorten, + IDnsResource* resource); + + IDnsResource* getResourceByName(IDnsResource* startFrom, size_t resourceCount, + const std::string& name, + bool exactMatch) const; + + void parseResources(); + + DnsResource* addResource(DnsResourceType resType, const std::string& name, + DnsType dnsType, DnsClass dnsClass, uint32_t ttl, + IDnsResourceData* data); + + bool removeResource(IDnsResource* resourceToRemove); +}; - // forward declarations - class DnsQuery; - class IDnsResource; - class DnsResource; - class IDnsResourceData; - - - /** - * @class DnsLayer - * Represents the DNS protocol layer - */ - class DnsLayer : public Layer - { - friend class IDnsResource; - friend class DnsQuery; - friend class DnsResource; - - public: - - /** - * 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 - */ - DnsLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); - - /** - * A constructor that creates an empty DNS layer: all members of dnshdr are set to 0 and layer will contain no records - */ - DnsLayer(); - - /** - * A copy constructor for this layer - * @param[in] other The DNS layer to copy from - */ - DnsLayer(const DnsLayer& other); - - /** - * An assignment operator for this layer - * @param[in] other The DNS layer to assign - * @return A reference to the assignee - */ - DnsLayer& operator=(const DnsLayer& other); - - virtual ~DnsLayer(); - - /** - * Get a pointer to the DNS header (as opposed to the DNS data which is the queries, answers, etc. Data can be retrieved through the - * other methods of this layer. Notice the return value points directly to the data, so every change will change the actual packet data - * @return A pointer to the @ref dnshdr - */ - dnshdr* getDnsHeader() const; - - /** - * Searches for a DNS query by its name field. Notice this method returns only a query which its name equals to the requested name. If - * several queries match the requested name, the first one will be returned. If no queries match the requested name, NULL will be returned - * @param[in] name The name of the query to search - * @param[in] exactMatch Indicate whether to match the whole name or just a part of it - * @return The first matching DNS query or NULL if no queries were found - */ - DnsQuery* getQuery(const std::string& name, bool exactMatch) const; - - /** - * @return The first DNS query in the packet or NULL if packet doesn't contain any queries - */ - DnsQuery* getFirstQuery() const; - - /** - * Get the DNS query following a certain query - * @param[in] query A pointer to a DNS query that exist in the packet - * @return The DNS query following 'query'. If 'query' is NULL or 'query' is the last query in the packet NULL will be returned - */ - DnsQuery* getNextQuery(DnsQuery* query) const; - - /** - * @return The number of DNS queries in the packet - */ - size_t getQueryCount() const; - - /** - * Add a new DNS query to the layer - * @param[in] name The value that shall be set in the name field of the query - * @param[in] dnsType The value that shall be set in the DNS type field of the query - * @param[in] dnsClass The value that shall be set in the DNS class field of the query - * @return A pointer to the newly created DNS query or NULL if query could not be created (an appropriate error log message will be - * printed in this case) - */ - DnsQuery* addQuery(const std::string& name, DnsType dnsType, DnsClass dnsClass); - - /** - * Add a new DNS query similar to an already existing DNS query. All query fields will be copied from the existing query - * @param[in] copyQuery The record to create the new record from. copyQuery won't be changed in any way - * @return A pointer to the newly created DNS query or NULL if query could not be created (an appropriate error log message will be - * printed in this case) - */ - DnsQuery* addQuery(DnsQuery* const copyQuery); - - /** - * Remove an existing query by name. If several queries matches the name, the first match will be removed - * @param[in] queryNameToRemove The name of the query to remove - * @param[in] exactMatch Indicate whether to match the whole name or just a part of it - * @return True if query was found and successfully removed or false if query was not found or couldn't be removed - */ - bool removeQuery(const std::string& queryNameToRemove, bool exactMatch); - - /** - * Remove an existing query - * @param[in] queryToRemove A pointer to the query to remove - * @return True if query was found and successfully removed or false if query was not found or couldn't be removed - */ - bool removeQuery(DnsQuery* queryToRemove); - - /** - * Searches for a DNS answer by its name field. Notice this method returns only an answer which its name equals to the requested name. If - * several answers match the requested name, the first one will be returned. If no answers match the requested name, NULL will be returned - * @param[in] name The name of the answer to search - * @param[in] exactMatch Indicate whether to match the whole name or just a part of it - * @return The first matching DNS answer or NULL if no answers were found - */ - DnsResource* getAnswer(const std::string& name, bool exactMatch) const; - - /** - * @return The first DNS answer in the packet or NULL if packet doesn't contain any answers - */ - DnsResource* getFirstAnswer() const; - - /** - * Get the DNS answer following a certain answer - * @param[in] answer A pointer to a DNS answer that exist in the packet - * @return The DNS answer following 'answer'. If 'answer' is NULL or 'answer' is the last answer in the packet NULL will be returned - */ - DnsResource* getNextAnswer(DnsResource* answer) const; - - /** - * @return The number of DNS answers in the packet - */ - size_t getAnswerCount() const; - - /** - * Add a new DNS answer to the layer - * @param[in] name The value that shall be set in the name field of the answer - * @param[in] dnsType The value that shall be set in the DNS type field of the answer - * @param[in] dnsClass The value that shall be set in the DNS class field of the answer - * @param[in] ttl The value that shall be set in the 'time-to-leave' field of the answer - * @param[in] data The answer data to be set. The type of the data should match the type of the DNS record - * (for example: DNS record of type A should have data of type IPv4DnsResourceData. Please see DnsResource#setData() - * for more info on this - * @return A pointer to the newly created DNS answer or NULL if answer could not be created (an appropriate error log message will be - * printed in this case) - */ - DnsResource* addAnswer(const std::string& name, DnsType dnsType, DnsClass dnsClass, uint32_t ttl, IDnsResourceData* data); - - /** - * Add a new DNS answer similar to an already existing DNS answer. All answer fields will be copied from the existing answer - * @param[in] copyAnswer The record to create the new record from. copyAnswer won't be changed in any way - * @return A pointer to the newly created DNS answer or NULL if query could not be created (an appropriate error log message will be - * printed in this case) - */ - DnsResource* addAnswer(DnsResource* const copyAnswer); - - /** - * Remove an existing answer by name. If several answers matches the name, the first match will be removed - * @param[in] answerNameToRemove The name of the answer to remove - * @param[in] exactMatch Indicate whether to match the whole name or just a part of it - * @return True if answer was found and successfully removed or false if answer was not found or couldn't be removed - */ - bool removeAnswer(const std::string& answerNameToRemove, bool exactMatch); - - /** - * Remove an existing answer - * @param[in] answerToRemove A pointer to the answer to remove - * @return True if answer was found and successfully removed or false if answer was not found or couldn't be removed - */ - bool removeAnswer(DnsResource* answerToRemove); - - - /** - * Searches for a DNS authority by its name field. Notice this method returns only an authority which its name equals to the requested name. If - * several authorities match the requested name, the first one will be returned. If no authorities match the requested name, NULL will be returned - * @param[in] name The name of the authority to search - * @param[in] exactMatch Indicate whether to match the whole name or just a part of it - * @return The first matching DNS authority or NULL if no authorities were found - */ - DnsResource* getAuthority(const std::string& name, bool exactMatch) const; - - /** - * @return The first DNS authority in the packet or NULL if packet doesn't contain any authorities - */ - DnsResource* getFirstAuthority() const; - - /** - * Get the DNS authority following a certain authority - * @param[in] authority A pointer to a DNS authority that exist in the packet - * @return The DNS authority following 'authority'. If 'authority' is NULL or 'authority' is the last authority in the packet NULL will be returned - */ - DnsResource* getNextAuthority(DnsResource* authority) const; - - /** - * @return The number of DNS authorities in the packet - */ - size_t getAuthorityCount() const; - - /** - * Add a new DNS authority to the layer - * @param[in] name The value that shall be set in the name field of the authority - * @param[in] dnsType The value that shall be set in the DNS type field of the authority - * @param[in] dnsClass The value that shall be set in the DNS class field of the authority - * @param[in] ttl The value that shall be set in the 'time-to-leave' field of the authority - * @param[in] data The authority data to be set. The type of the data should match the type of the DNS record - * (for example: DNS record of type A should have data of type IPv4DnsResourceData. Please see DnsResource#setData() - * for more info on this - * @return A pointer to the newly created DNS authority or NULL if authority could not be created (an appropriate error log message will be - * printed in this case) - */ - DnsResource* addAuthority(const std::string& name, DnsType dnsType, DnsClass dnsClass, uint32_t ttl, IDnsResourceData* data); - - /** - * Add a new DNS authority similar to an already existing DNS authority. All authority fields will be copied from the existing authority - * @param[in] copyAuthority The record to create the new record from. copyAuthority won't be changed in any way - * @return A pointer to the newly created DNS authority or NULL if query could not be created (an appropriate error log message will be - * printed in this case) - */ - DnsResource* addAuthority(DnsResource* const copyAuthority); - - /** - * Remove an existing authority by name. If several authorities matches the name, the first match will be removed - * @param[in] authorityNameToRemove The name of the authority to remove - * @param[in] exactMatch Indicate whether to match the whole name or just a part of it - * @return True if authority was found and successfully removed or false if authority was not found or couldn't be removed - */ - bool removeAuthority(const std::string& authorityNameToRemove, bool exactMatch); - - /** - * Remove an existing authority - * @param[in] authorityToRemove A pointer to the authority to remove - * @return True if authority was found and successfully removed or false if authority was not found or couldn't be removed - */ - bool removeAuthority(DnsResource* authorityToRemove); - - - /** - * Searches for a DNS additional record by its name field. Notice this method returns only an additional record which its name equals to - * the requested name. If several additional records match the requested name, the first one will be returned. If no additional records - * match the requested name, NULL will be returned - * @param[in] name The name of the additional record to search - * @param[in] exactMatch Indicate whether to match the whole name or just a part of it - * @return The first matching DNS additional record or NULL if no additional records were found - */ - DnsResource* getAdditionalRecord(const std::string& name, bool exactMatch) const; - - /** - * @return The first DNS additional record in the packet or NULL if packet doesn't contain any additional records - */ - DnsResource* getFirstAdditionalRecord() const; - - /** - * Get the DNS additional record following a certain additional record - * @param[in] additionalRecord A pointer to a DNS additional record that exist in the packet - * @return The DNS additional record following 'additionalRecord'. If 'additionalRecord' is NULL or 'additionalRecord' is the - * last additional record in the packet NULL will be returned - */ - DnsResource* getNextAdditionalRecord(DnsResource* additionalRecord) const; - - /** - * @return The number of DNS additional records in the packet - */ - size_t getAdditionalRecordCount() const; - - /** - * Add a new DNS additional record to the layer - * @param[in] name The value that shall be set in the name field of the additional record - * @param[in] dnsType The value that shall be set in the DNS type field of the additional record - * @param[in] dnsClass The value that shall be set in the DNS class field of the additional record - * @param[in] ttl The value that shall be set in the 'time-to-leave' field of the additional record - * @param[in] data The additional record data to be set. The type of the data should match the type of the DNS record - * (for example: DNS record of type A should have data of type IPv4DnsResourceData. Please see DnsResource#setData() - * for more info on this - * @return A pointer to the newly created DNS additional record or NULL if additional record could not be created (an appropriate error - * log message will be printed in this case) - */ - DnsResource* addAdditionalRecord(const std::string& name, DnsType dnsType, DnsClass dnsClass, uint32_t ttl, IDnsResourceData* data); - - /** - * Add a new DNS additional record to the layer that doesn't have DNS class and TTL. Instead these bytes may contains some arbitrary - * data. In the future I may add support for these kinds of additional data records. For now, these bytes are set as raw - * @param[in] name The value that shall be set in the name field of the additional record - * @param[in] dnsType The value that shall be set in the DNS type field of the additional record - * @param[in] customData1 Two bytes of the arbitrary data that will be set in the offset usually used for the DNS class - * @param[in] customData2 Four bytes of the arbitrary data that will be set in the offset usually used for the TTL - * @param[in] data The additional record data to be set. The type of the data should match the type of the DNS record. - * (for example: DNS record of type A should have data of type IPv4DnsResourceData. Please see DnsResource#setData() - * for more info on this - * @return A pointer to the newly created DNS additional record or NULL if additional record could not be created (an appropriate error - * log message will be printed in this case) - */ - DnsResource* addAdditionalRecord(const std::string& name, DnsType dnsType, uint16_t customData1, uint32_t customData2, IDnsResourceData* data); - - /** - * Add a new DNS additional record similar to an already existing DNS additional record. All additional record fields will be copied from the - * existing additional record - * @param[in] copyAdditionalRecord The record to create the new record from. copyAdditionalRecord won't be changed in any way - * @return A pointer to the newly created DNS additional record or NULL if query could not be created (an appropriate error log message will - * be printed in this case) - */ - DnsResource* addAdditionalRecord(DnsResource* const copyAdditionalRecord); - - /** - * Remove an existing additional record by name. If several additional records matches the name, the first match will be removed - * @param[in] additionalRecordNameToRemove The name of the additional record to remove - * @param[in] exactMatch Indicate whether to match the whole name or just a part of it - * @return True if additional record was found and successfully removed or false if additional record was not found or couldn't be removed - */ - bool removeAdditionalRecord(const std::string& additionalRecordNameToRemove, bool exactMatch); - - /** - * Remove an existing additional record - * @param[in] additionalRecordToRemove A pointer to the additional record to remove - * @return True if additional record was found and successfully removed or false if additional record was not found or couldn't be removed - */ - bool removeAdditionalRecord(DnsResource* additionalRecordToRemove); - - // implement abstract methods - - /** - * Does nothing for this layer (DnsLayer is always last) - */ - void parseNextLayer() {} - - /** - * @return The size of the DNS data in the packet including he DNS header and size of all queries, answers, authorities and additional - * records - */ - size_t getHeaderLen() const { return m_DataLen; } //No layer above DNS - - /** - * Does nothing for this layer - */ - virtual void computeCalculateFields() {} - - std::string toString() const; - - OsiModelLayer getOsiModelLayer() const { return OsiModelApplicationLayer; } - - /** - * A static method that checks whether the port is considered as DNS - * @param[in] port The port number to be checked - * @return True if the port is associated with the DNS protocol - */ - static inline bool isDnsPort(uint16_t port); - - /** - * A static method that validates the input data - * @param[in] data The pointer to the beginning of a byte stream of a DNS packet - * @param[in] dataLen The length of the byte stream - * @param[in] dnsOverTcp Should be set to "true" if this is DNS is over TCP, otherwise set to "false" - * (which is also the default value) - * @return True if the data is valid and can represent a DNS packet - */ - static inline bool isDataValid(const uint8_t* data, size_t dataLen, bool dnsOverTcp = false); - - protected: - DnsLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet, size_t offsetAdjustment); - explicit DnsLayer(size_t offsetAdjustment); - - private: - IDnsResource* m_ResourceList; - DnsQuery* m_FirstQuery; - DnsResource* m_FirstAnswer; - DnsResource* m_FirstAuthority; - DnsResource* m_FirstAdditional; - uint16_t m_OffsetAdjustment; - - size_t getBasicHeaderSize(); - void init(size_t offsetAdjustment, bool callParseResource); - void initNewLayer(size_t offsetAdjustment); - - IDnsResource* getFirstResource(DnsResourceType resType) const; - void setFirstResource(DnsResourceType resType, IDnsResource* resource); - - using Layer::extendLayer; - bool extendLayer(int offsetInLayer, size_t numOfBytesToExtend, IDnsResource* resource); - - using Layer::shortenLayer; - bool shortenLayer(int offsetInLayer, size_t numOfBytesToShorten, IDnsResource* resource); - - IDnsResource* getResourceByName(IDnsResource* startFrom, size_t resourceCount, const std::string& name, bool exactMatch) const; - - void parseResources(); - - DnsResource* addResource(DnsResourceType resType, const std::string& name, DnsType dnsType, DnsClass dnsClass, - uint32_t ttl, IDnsResourceData* data); - - bool removeResource(IDnsResource* resourceToRemove); - - }; - - - - /** - * @class DnsOverTcpLayer - * Represents the DNS over TCP layer. - * DNS over TCP is described here: https://tools.ietf.org/html/rfc7766 . - * It is very similar to DNS over UDP, except for one field: TCP message length which is added in the beginning of the message - * before the other DNS data properties. The rest of the data is similar. - * - * Note: DNS over TCP can spread over more than one packet, but this implementation doesn't support this use-case and assumes - * the whole message fits in a single packet. - */ - class DnsOverTcpLayer : public DnsLayer - { - public: - - /** - * 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 - */ - DnsOverTcpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) - : DnsLayer(data, dataLen, prevLayer, packet, sizeof(uint16_t)) {} - - /** - * A constructor that creates an empty DNS layer: all members of dnshdr are set to 0 and layer will contain no records - */ - DnsOverTcpLayer() : DnsLayer(sizeof(uint16_t)) {} - - /** - * A copy constructor for this layer - * @param[in] other The DNS over TCP layer to copy from - */ - DnsOverTcpLayer(const DnsOverTcpLayer& other) : DnsLayer(other) {} - - /** - * @return The value of the TCP message length as described in https://tools.ietf.org/html/rfc7766#section-8 - */ - uint16_t getTcpMessageLength(); - - /** - * Set the TCP message length value as described in https://tools.ietf.org/html/rfc7766#section-8 - * @param[in] value The value to set - */ - void setTcpMessageLength(uint16_t value); - - - // overridden methods - - /** - * Calculate the TCP message length field - */ - void computeCalculateFields(); - }; - - - // implementation of inline methods - - bool DnsLayer::isDnsPort(uint16_t port) - { - switch (port) - { - case 53: - case 5353: - case 5355: - return true; - default: - return false; - } - } - - bool DnsLayer::isDataValid(const uint8_t* data, size_t dataLen, bool dnsOverTcp) - { - size_t minSize = sizeof(dnshdr) + (dnsOverTcp ? sizeof(uint16_t) : 0); - return data && dataLen >= minSize; - } +/** + * @class DnsOverTcpLayer + * Represents the DNS over TCP layer. + * DNS over TCP is described here: https://tools.ietf.org/html/rfc7766 . + * It is very similar to DNS over UDP, except for one field: TCP message length + * which is added in the beginning of the message before the other DNS data + * properties. The rest of the data is similar. + * + * Note: DNS over TCP can spread over more than one packet, but this + * implementation doesn't support this use-case and assumes the whole message + * fits in a single packet. + */ +class DnsOverTcpLayer : public DnsLayer { + public: + /** + * 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 + */ + DnsOverTcpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet) + : DnsLayer(data, dataLen, prevLayer, packet, sizeof(uint16_t)) {} + + /** + * A constructor that creates an empty DNS layer: all members of dnshdr are + * set to 0 and layer will contain no records + */ + DnsOverTcpLayer() : DnsLayer(sizeof(uint16_t)) {} + + /** + * A copy constructor for this layer + * @param[in] other The DNS over TCP layer to copy from + */ + DnsOverTcpLayer(const DnsOverTcpLayer& other) : DnsLayer(other) {} + + /** + * @return The value of the TCP message length as described in + * https://tools.ietf.org/html/rfc7766#section-8 + */ + uint16_t getTcpMessageLength(); + + /** + * Set the TCP message length value as described in + * https://tools.ietf.org/html/rfc7766#section-8 + * @param[in] value The value to set + */ + void setTcpMessageLength(uint16_t value); + + // overridden methods + + /** + * Calculate the TCP message length field + */ + void computeCalculateFields(); +}; + +// implementation of inline methods + +bool DnsLayer::isDnsPort(uint16_t port) { + switch (port) { + case 53: + case 5353: + case 5355: + return true; + default: + return false; + } +} + +bool DnsLayer::isDataValid(const uint8_t* data, size_t dataLen, + bool dnsOverTcp) { + size_t minSize = sizeof(dnshdr) + (dnsOverTcp ? sizeof(uint16_t) : 0); + return data && dataLen >= minSize; +} } // namespace pcpp diff --git a/Packet++/header/DnsLayerEnums.h b/Packet++/header/DnsLayerEnums.h index ab8376b5d6..53914c45ab 100644 --- a/Packet++/header/DnsLayerEnums.h +++ b/Packet++/header/DnsLayerEnums.h @@ -7,153 +7,147 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - /** - * An enum for all possible DNS record types - */ - enum DnsType - { - /** IPv4 address record */ - DNS_TYPE_A = 1, - /** Name Server record */ - DNS_TYPE_NS, - /** Obsolete, replaced by MX */ - DNS_TYPE_MD, - /** Obsolete, replaced by MX */ - DNS_TYPE_MF, - /** Canonical name record */ - DNS_TYPE_CNAME, - /** Start of Authority record */ - DNS_TYPE_SOA, - /** mailbox domain name record */ - DNS_TYPE_MB, - /** mail group member record */ - DNS_TYPE_MG, - /** mail rename domain name record */ - DNS_TYPE_MR, - /** NULL record */ - DNS_TYPE_NULL_R, - /** well known service description record */ - DNS_TYPE_WKS, - /** Pointer record */ - DNS_TYPE_PTR, - /** Host information record */ - DNS_TYPE_HINFO, - /** mailbox or mail list information record */ - DNS_TYPE_MINFO, - /** Mail exchanger record */ - DNS_TYPE_MX, - /** Text record */ - DNS_TYPE_TXT, - /** Responsible person record */ - DNS_TYPE_RP, - /** AFS database record */ - DNS_TYPE_AFSDB, - /** DNS X25 resource record */ - DNS_TYPE_X25, - /** Integrated Services Digital Network record */ - DNS_TYPE_ISDN, - /** Route Through record */ - DNS_TYPE_RT, - /** network service access point address record */ - DNS_TYPE_NSAP, - /** network service access point address pointer record */ - DNS_TYPE_NSAP_PTR, - /** Signature record */ - DNS_TYPE_SIG, - /** Key record */ - DNS_TYPE_KEY, - /** Mail Mapping Information record */ - DNS_TYPE_PX, - /** DNS Geographical Position record */ - DNS_TYPE_GPOS, - /** IPv6 address record */ - DNS_TYPE_AAAA, - /** Location record */ - DNS_TYPE_LOC, - /** Obsolete record */ - DNS_TYPE_NXT, - /** DNS Endpoint Identifier record */ - DNS_TYPE_EID, - /** DNS Nimrod Locator record */ - DNS_TYPE_NIMLOC, - /** Service locator record */ - DNS_TYPE_SRV, - /** Asynchronous Transfer Mode address record */ - DNS_TYPE_ATMA, - /** Naming Authority Pointer record */ - DNS_TYPE_NAPTR, - /** Key eXchanger record */ - DNS_TYPE_KX, - /** Certificate record */ - DNS_TYPE_CERT, - /** Obsolete, replaced by AAAA type */ - DNS_TYPE_A6, - /** Delegation Name record */ - DNS_TYPE_DNAM, - /** Kitchen sink record */ - DNS_TYPE_SINK, - /** Option record */ - DNS_TYPE_OPT, - /** Address Prefix List record */ - DNS_TYPE_APL, - /** Delegation signer record */ - DNS_TYPE_DS, - /** SSH Public Key Fingerprint record */ - DNS_TYPE_SSHFP, - /** IPsec Key record */ - DNS_TYPE_IPSECKEY, - /** DNSSEC signature record */ - DNS_TYPE_RRSIG, - /** Next-Secure record */ - DNS_TYPE_NSEC, - /** DNS Key record */ - DNS_TYPE_DNSKEY, - /** DHCP identifier record */ - DNS_TYPE_DHCID, - /** NSEC record version 3 */ - DNS_TYPE_NSEC3, - /** NSEC3 parameters */ - DNS_TYPE_NSEC3PARAM, - /** All cached records */ - DNS_TYPE_ALL = 255 - }; - - - /** - * An enum for all possible DNS classes - */ - enum DnsClass - { - /** Internet class */ - DNS_CLASS_IN = 1, - /** Internet class with QU flag set to True */ - DNS_CLASS_IN_QU = 32769, - /** Chaos class */ - DNS_CLASS_CH = 3, - /** Hesiod class */ - DNS_CLASS_HS = 4, - /** ANY class */ - DNS_CLASS_ANY = 255 - }; +namespace pcpp { +/** + * An enum for all possible DNS record types + */ +enum DnsType { + /** IPv4 address record */ + DNS_TYPE_A = 1, + /** Name Server record */ + DNS_TYPE_NS, + /** Obsolete, replaced by MX */ + DNS_TYPE_MD, + /** Obsolete, replaced by MX */ + DNS_TYPE_MF, + /** Canonical name record */ + DNS_TYPE_CNAME, + /** Start of Authority record */ + DNS_TYPE_SOA, + /** mailbox domain name record */ + DNS_TYPE_MB, + /** mail group member record */ + DNS_TYPE_MG, + /** mail rename domain name record */ + DNS_TYPE_MR, + /** NULL record */ + DNS_TYPE_NULL_R, + /** well known service description record */ + DNS_TYPE_WKS, + /** Pointer record */ + DNS_TYPE_PTR, + /** Host information record */ + DNS_TYPE_HINFO, + /** mailbox or mail list information record */ + DNS_TYPE_MINFO, + /** Mail exchanger record */ + DNS_TYPE_MX, + /** Text record */ + DNS_TYPE_TXT, + /** Responsible person record */ + DNS_TYPE_RP, + /** AFS database record */ + DNS_TYPE_AFSDB, + /** DNS X25 resource record */ + DNS_TYPE_X25, + /** Integrated Services Digital Network record */ + DNS_TYPE_ISDN, + /** Route Through record */ + DNS_TYPE_RT, + /** network service access point address record */ + DNS_TYPE_NSAP, + /** network service access point address pointer record */ + DNS_TYPE_NSAP_PTR, + /** Signature record */ + DNS_TYPE_SIG, + /** Key record */ + DNS_TYPE_KEY, + /** Mail Mapping Information record */ + DNS_TYPE_PX, + /** DNS Geographical Position record */ + DNS_TYPE_GPOS, + /** IPv6 address record */ + DNS_TYPE_AAAA, + /** Location record */ + DNS_TYPE_LOC, + /** Obsolete record */ + DNS_TYPE_NXT, + /** DNS Endpoint Identifier record */ + DNS_TYPE_EID, + /** DNS Nimrod Locator record */ + DNS_TYPE_NIMLOC, + /** Service locator record */ + DNS_TYPE_SRV, + /** Asynchronous Transfer Mode address record */ + DNS_TYPE_ATMA, + /** Naming Authority Pointer record */ + DNS_TYPE_NAPTR, + /** Key eXchanger record */ + DNS_TYPE_KX, + /** Certificate record */ + DNS_TYPE_CERT, + /** Obsolete, replaced by AAAA type */ + DNS_TYPE_A6, + /** Delegation Name record */ + DNS_TYPE_DNAM, + /** Kitchen sink record */ + DNS_TYPE_SINK, + /** Option record */ + DNS_TYPE_OPT, + /** Address Prefix List record */ + DNS_TYPE_APL, + /** Delegation signer record */ + DNS_TYPE_DS, + /** SSH Public Key Fingerprint record */ + DNS_TYPE_SSHFP, + /** IPsec Key record */ + DNS_TYPE_IPSECKEY, + /** DNSSEC signature record */ + DNS_TYPE_RRSIG, + /** Next-Secure record */ + DNS_TYPE_NSEC, + /** DNS Key record */ + DNS_TYPE_DNSKEY, + /** DHCP identifier record */ + DNS_TYPE_DHCID, + /** NSEC record version 3 */ + DNS_TYPE_NSEC3, + /** NSEC3 parameters */ + DNS_TYPE_NSEC3PARAM, + /** All cached records */ + DNS_TYPE_ALL = 255 +}; +/** + * An enum for all possible DNS classes + */ +enum DnsClass { + /** Internet class */ + DNS_CLASS_IN = 1, + /** Internet class with QU flag set to True */ + DNS_CLASS_IN_QU = 32769, + /** Chaos class */ + DNS_CLASS_CH = 3, + /** Hesiod class */ + DNS_CLASS_HS = 4, + /** ANY class */ + DNS_CLASS_ANY = 255 +}; - /** - * An enum for representing the 4 types of possible DNS records - */ - enum DnsResourceType - { - /** DNS query record */ - DnsQueryType = 0, - /** DNS answer record */ - DnsAnswerType = 1, - /** DNS authority record */ - DnsAuthorityType = 2, - /** DNS additional record */ - DnsAdditionalType = 3 - }; +/** + * An enum for representing the 4 types of possible DNS records + */ +enum DnsResourceType { + /** DNS query record */ + DnsQueryType = 0, + /** DNS answer record */ + DnsAnswerType = 1, + /** DNS authority record */ + DnsAuthorityType = 2, + /** DNS additional record */ + DnsAdditionalType = 3 +}; -} +} // namespace pcpp #endif // PACKETPP_DNS_LAYER_ENUMS diff --git a/Packet++/header/DnsResource.h b/Packet++/header/DnsResource.h index 89b4a55707..683484e0aa 100644 --- a/Packet++/header/DnsResource.h +++ b/Packet++/header/DnsResource.h @@ -4,9 +4,9 @@ #include "DnsLayer.h" #include "DnsLayerEnums.h" #include "DnsResourceData.h" +#include #include #include -#include /// @file @@ -14,224 +14,250 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - // forward declarations - class DnsLayer; - class IDnsResourceData; - class DnsResourceDataPtr; - - - /** - * @class IDnsResource - * An abstract class for representing all types of DNS records. This class gives access to all available record data such as DNS type, class, - * name, type of record, etc. The DnsLayer holds an instance of (inherited type of) this class for each DNS record in the DNS packet - */ - class IDnsResource - { - protected: - friend class DnsLayer; - friend class IDnsResourceData; - - protected: - DnsLayer* m_DnsLayer; - size_t m_OffsetInLayer; - IDnsResource* m_NextResource; - std::string m_DecodedName; - size_t m_NameLength; - uint8_t* m_ExternalRawData; - - IDnsResource(DnsLayer* dnsLayer, size_t offsetInLayer); - - IDnsResource(uint8_t* emptyRawData); - - size_t decodeName(const char* encodedName, char* result, int iteration = 1); - void encodeName(const std::string& decodedName, char* result, size_t& resultLen); - - IDnsResource* getNextResource() const { return m_NextResource; } - void setNexResource(IDnsResource* next) { m_NextResource = next; } - - uint8_t* getRawData() const; - - void setDnsLayer(DnsLayer* dnsLayer, size_t offsetInLayer); - - public: - - virtual ~IDnsResource() {} - - /** - * @return The DNS type of this record - */ - DnsType getDnsType() const; - - /** - * Set DNS type for this record - * @param[in] newType The type to set - */ - void setDnsType(DnsType newType); - - /** - * @return The DNS class of this record - */ - DnsClass getDnsClass() const; - - /** - * Set DNS class for this record - * @param[in] newClass The class to set - */ - void setDnsClass(DnsClass newClass); - - /** - * @return The name of this record - */ - const std::string& getName() const { return m_DecodedName; } - - /** - * @return The record name's offset in the packet - */ - size_t getNameOffset() const { return m_OffsetInLayer; } - - /** - * Set the name of this record. The input name can be a standard hostname (e.g 'google.com'), or it may contain - * a pointer to another string in the packet (as explained here: http://www.zytrax.com/books/dns/ch15/#name). - * The pointer is used to reduce the DNS packet size and avoid unnecessary duplications. In case you - * want to use a pointer in your string you should use the following format: 'some.domain.#{offset}' where '#{offset}' - * is a the offset from the start of the layer. For example: if the string 'yahoo.com' already appears in offset - * 12 in the packet and you want to set the name of the current record to 'my.subdomain.yahoo.com' you may use - * the following string: 'my.subdomain.#12'. This will result in writing 'my.subdomain' and a pointer to offset 12.
- * Please notice the new name can be shorter or longer of the old name, so this method can cause the packet to be - * shorten or extended - * @param[in] newName The name to set - * @return True if name was set successfully or false if input string is malformed or if an error occurred - */ - bool setName(const std::string& newName); - - - // abstract methods - - /** - * @return The total size in bytes of this record - */ - virtual size_t getSize() const = 0; - - /** - * @return The type of this record (query, answer, authority, additional) - */ - virtual DnsResourceType getType() const = 0; - }; - - - /** - * @class DnsQuery - * Representing a DNS query record - */ - class DnsQuery : public IDnsResource - { - friend class DnsLayer; - - private: - DnsQuery(DnsLayer* dnsLayer, size_t offsetInLayer) : IDnsResource(dnsLayer, offsetInLayer) {} - - explicit DnsQuery(uint8_t* emptyRawData) : IDnsResource(emptyRawData) {} - - public: - virtual ~DnsQuery() {} - - // implementation of abstract methods - virtual size_t getSize() const { return m_NameLength + 2 * sizeof(uint16_t); } - virtual DnsResourceType getType() const { return DnsQueryType; } - }; - - - /** - * @class DnsResource - * Representing DNS record other than DNS query - */ - class DnsResource : public IDnsResource - { - friend class DnsLayer; - - private: - DnsResourceType m_ResourceType; - - DnsResource(DnsLayer* dnsLayer, size_t offsetInLayer, DnsResourceType resourceType) : IDnsResource(dnsLayer, offsetInLayer) { m_ResourceType = resourceType; } - - DnsResource(uint8_t* emptyRawData, DnsResourceType resType) : IDnsResource(emptyRawData), m_ResourceType(resType) {} - - public: - virtual ~DnsResource() {} - - /** - * @return The time-to-leave value for this record - */ - uint32_t getTTL() const; - - /** - * Set time-to-leave value for this record - * @param[in] newTTL The new TTL value to set - */ - void setTTL(uint32_t newTTL); - - /** - * @return The data length value for this record (taken from the "data length" field of the record) - */ - size_t getDataLength() const; - - /** - * @return A smart pointer to an IDnsResourceData object that contains the DNS resource data. It is guaranteed that the - * smart pointer will always point to an object and never to NULL. The specific object type depends on the DNS type of this record:
- * - For type A (::DNS_TYPE_A): the return value is a smart pointer to IPv4DnsResourceData object that contains the IPv4 address
- * - For type AAAA (::DNS_TYPE_AAAA): the return value is a smart pointer to IPv6DnsResourceData object that contains the IPv6 address
- * - For types NS, CNAME, DNAME, PTR (::DNS_TYPE_NS, ::DNS_TYPE_CNAME, ::DNS_TYPE_DNAM, ::DNS_TYPE_PTR): the return value is - * a smart pointer to StringDnsResourceData object that contains the name
- * - For type MX (::DNS_TYPE_MX): the return value is a smart pointer to MxDnsResourceData object that contains the MX data (preference and - * mail exchange name)
- * - For all other types: the return value is a smart pointer to GenericDnsResourceData which contains a byte array of the data - */ - DnsResourceDataPtr getData() const; - - /** - * @return The offset of data in the DNS layer - */ - size_t getDataOffset() const; - - /** - * Set resource data. The given IDnsResourceData input object is validated against the DNS type of the resource. For example: if DNS type is A - * and data isn't of type IPv4DnsResourceData (which contains the IPv4 address) a log error will be printed and the method will return false. - * This method currently supports the following DNS types:
- * - ::DNS_TYPE_A (IPv4 address) - data is expected to be a pointer to IPv4DnsResourceData with a valid IPv4 address - * - ::DNS_TYPE_AAAA (IPv6 address) - data is expected to be a pointer to IPv6DnsResourceData with a valid IPv6 address - * - ::DNS_TYPE_NS, ::DNS_TYPE_CNAME, ::DNS_TYPE_DNAM, ::DNS_TYPE_PTR (name data) - data is expected to be a pointer to StringDnsResourceData - * object that contains a host name, e.g: 'www.google.com' - * - ::DNS_TYPE_MX (MX data) - data is expected to be a pointer to MxDnsResourceData object that contains the MX data - * - else: data is expected to be a pointer to GenericDnsResourceData object that contains a valid hex string (valid hex string means a string - * which has an even number of characters representing a valid hex data. e.g: '0d0a45569a9b') - * @param[in] data The pointer to the data object, as described above - * @return True if data was properly set or false if data is illegal or method couldn't extend or shorted the packet (appropriate error log is - * printed in all cases) - */ - bool setData(IDnsResourceData* data); - - /** - * Some records don't have a DNS class and the bytes used for storing the DNS class are used for other purpose. This method enables the - * user to receive these bytes - * @return The value stored in this place - */ - uint16_t getCustomDnsClass() const; - - /** - * Some records don't have a DNS class and the bytes used for storing the DNS class are used for other purpose. This method enables the - * user to set these bytes - * @param[in] customValue The value to set - */ - void setCustomDnsClass(uint16_t customValue); - - // implementation of abstract methods - virtual size_t getSize() const { return m_NameLength + 3 * sizeof(uint16_t) + sizeof(uint32_t) + getDataLength(); } - virtual DnsResourceType getType() const { return m_ResourceType; } - - }; - -} +namespace pcpp { +// forward declarations +class DnsLayer; +class IDnsResourceData; +class DnsResourceDataPtr; + +/** + * @class IDnsResource + * An abstract class for representing all types of DNS records. This class gives + * access to all available record data such as DNS type, class, name, type of + * record, etc. The DnsLayer holds an instance of (inherited type of) this class + * for each DNS record in the DNS packet + */ +class IDnsResource { + protected: + friend class DnsLayer; + friend class IDnsResourceData; + + protected: + DnsLayer* m_DnsLayer; + size_t m_OffsetInLayer; + IDnsResource* m_NextResource; + std::string m_DecodedName; + size_t m_NameLength; + uint8_t* m_ExternalRawData; + + IDnsResource(DnsLayer* dnsLayer, size_t offsetInLayer); + + IDnsResource(uint8_t* emptyRawData); + + size_t decodeName(const char* encodedName, char* result, int iteration = 1); + void encodeName(const std::string& decodedName, char* result, + size_t& resultLen); + + IDnsResource* getNextResource() const { return m_NextResource; } + void setNexResource(IDnsResource* next) { m_NextResource = next; } + + uint8_t* getRawData() const; + + void setDnsLayer(DnsLayer* dnsLayer, size_t offsetInLayer); + + public: + virtual ~IDnsResource() {} + + /** + * @return The DNS type of this record + */ + DnsType getDnsType() const; + + /** + * Set DNS type for this record + * @param[in] newType The type to set + */ + void setDnsType(DnsType newType); + + /** + * @return The DNS class of this record + */ + DnsClass getDnsClass() const; + + /** + * Set DNS class for this record + * @param[in] newClass The class to set + */ + void setDnsClass(DnsClass newClass); + + /** + * @return The name of this record + */ + const std::string& getName() const { return m_DecodedName; } + + /** + * @return The record name's offset in the packet + */ + size_t getNameOffset() const { return m_OffsetInLayer; } + + /** + * Set the name of this record. The input name can be a standard hostname (e.g + * 'google.com'), or it may contain a pointer to another string in the packet + * (as explained here: http://www.zytrax.com/books/dns/ch15/#name). The + * pointer is used to reduce the DNS packet size and avoid unnecessary + * duplications. In case you want to use a pointer in your string you should + * use the following format: 'some.domain.#{offset}' where '#{offset}' is a + * the offset from the start of the layer. For example: if the string + * 'yahoo.com' already appears in offset 12 in the packet and you want to set + * the name of the current record to 'my.subdomain.yahoo.com' you may use the + * following string: 'my.subdomain.#12'. This will result in writing + * 'my.subdomain' and a pointer to offset 12.
Please notice the new name + * can be shorter or longer of the old name, so this method can cause the + * packet to be shorten or extended + * @param[in] newName The name to set + * @return True if name was set successfully or false if input string is + * malformed or if an error occurred + */ + bool setName(const std::string& newName); + + // abstract methods + + /** + * @return The total size in bytes of this record + */ + virtual size_t getSize() const = 0; + + /** + * @return The type of this record (query, answer, authority, additional) + */ + virtual DnsResourceType getType() const = 0; +}; + +/** + * @class DnsQuery + * Representing a DNS query record + */ +class DnsQuery : public IDnsResource { + friend class DnsLayer; + + private: + DnsQuery(DnsLayer* dnsLayer, size_t offsetInLayer) + : IDnsResource(dnsLayer, offsetInLayer) {} + + explicit DnsQuery(uint8_t* emptyRawData) : IDnsResource(emptyRawData) {} + + public: + virtual ~DnsQuery() {} + + // implementation of abstract methods + virtual size_t getSize() const { return m_NameLength + 2 * sizeof(uint16_t); } + virtual DnsResourceType getType() const { return DnsQueryType; } +}; + +/** + * @class DnsResource + * Representing DNS record other than DNS query + */ +class DnsResource : public IDnsResource { + friend class DnsLayer; + + private: + DnsResourceType m_ResourceType; + + DnsResource(DnsLayer* dnsLayer, size_t offsetInLayer, + DnsResourceType resourceType) + : IDnsResource(dnsLayer, offsetInLayer) { + m_ResourceType = resourceType; + } + + DnsResource(uint8_t* emptyRawData, DnsResourceType resType) + : IDnsResource(emptyRawData), m_ResourceType(resType) {} + + public: + virtual ~DnsResource() {} + + /** + * @return The time-to-leave value for this record + */ + uint32_t getTTL() const; + + /** + * Set time-to-leave value for this record + * @param[in] newTTL The new TTL value to set + */ + void setTTL(uint32_t newTTL); + + /** + * @return The data length value for this record (taken from the "data length" + * field of the record) + */ + size_t getDataLength() const; + + /** + * @return A smart pointer to an IDnsResourceData object that contains the DNS + * resource data. It is guaranteed that the smart pointer will always point to + * an object and never to NULL. The specific object type depends on the DNS + * type of this record:
+ * - For type A (::DNS_TYPE_A): the return value is a smart pointer to + * IPv4DnsResourceData object that contains the IPv4 address
+ * - For type AAAA (::DNS_TYPE_AAAA): the return value is a smart pointer to + * IPv6DnsResourceData object that contains the IPv6 address
+ * - For types NS, CNAME, DNAME, PTR (::DNS_TYPE_NS, ::DNS_TYPE_CNAME, + * ::DNS_TYPE_DNAM, ::DNS_TYPE_PTR): the return value is a smart pointer to + * StringDnsResourceData object that contains the name
+ * - For type MX (::DNS_TYPE_MX): the return value is a smart pointer to + * MxDnsResourceData object that contains the MX data (preference and mail + * exchange name)
+ * - For all other types: the return value is a smart pointer to + * GenericDnsResourceData which contains a byte array of the data + */ + DnsResourceDataPtr getData() const; + + /** + * @return The offset of data in the DNS layer + */ + size_t getDataOffset() const; + + /** + * Set resource data. The given IDnsResourceData input object is validated + * against the DNS type of the resource. For example: if DNS type is A and + * data isn't of type IPv4DnsResourceData (which contains the IPv4 address) a + * log error will be printed and the method will return false. This method + * currently supports the following DNS types:
+ * - ::DNS_TYPE_A (IPv4 address) - data is expected to be a pointer to + * IPv4DnsResourceData with a valid IPv4 address + * - ::DNS_TYPE_AAAA (IPv6 address) - data is expected to be a pointer to + * IPv6DnsResourceData with a valid IPv6 address + * - ::DNS_TYPE_NS, ::DNS_TYPE_CNAME, ::DNS_TYPE_DNAM, ::DNS_TYPE_PTR (name + * data) - data is expected to be a pointer to StringDnsResourceData object + * that contains a host name, e.g: 'www.google.com' + * - ::DNS_TYPE_MX (MX data) - data is expected to be a pointer to + * MxDnsResourceData object that contains the MX data + * - else: data is expected to be a pointer to GenericDnsResourceData object + * that contains a valid hex string (valid hex string means a string which has + * an even number of characters representing a valid hex data. e.g: + * '0d0a45569a9b') + * @param[in] data The pointer to the data object, as described above + * @return True if data was properly set or false if data is illegal or method + * couldn't extend or shorted the packet (appropriate error log is printed in + * all cases) + */ + bool setData(IDnsResourceData* data); + + /** + * Some records don't have a DNS class and the bytes used for storing the DNS + * class are used for other purpose. This method enables the user to receive + * these bytes + * @return The value stored in this place + */ + uint16_t getCustomDnsClass() const; + + /** + * Some records don't have a DNS class and the bytes used for storing the DNS + * class are used for other purpose. This method enables the user to set these + * bytes + * @param[in] customValue The value to set + */ + void setCustomDnsClass(uint16_t customValue); + + // implementation of abstract methods + virtual size_t getSize() const { + return m_NameLength + 3 * sizeof(uint16_t) + sizeof(uint32_t) + + getDataLength(); + } + virtual DnsResourceType getType() const { return m_ResourceType; } +}; + +} // namespace pcpp #endif // PACKETPP_DNS_RESOURCE diff --git a/Packet++/header/DnsResourceData.h b/Packet++/header/DnsResourceData.h index ee44f92405..b529ea3d05 100644 --- a/Packet++/header/DnsResourceData.h +++ b/Packet++/header/DnsResourceData.h @@ -4,8 +4,8 @@ #include "DnsResource.h" #include "IpAddress.h" #include -#include #include +#include /// @file @@ -13,387 +13,442 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - - //Visual studio has always been stupid about returning something useful for __cplusplus - //Only recently was this fixed - and even then it requires a specific hack to the command line during build - //Its easier/more consistent to test _MSC_VER in VS - //https://docs.microsoft.com/en-us/cpp/build/reference/zc-cplusplus?view=vs-2017 - - #if __cplusplus > 199711L || _MSC_VER >= 1800 //Maybe this can be 1600 for VS2010 - #define PCPP_SMART_PTR(T) std::unique_ptr - #else - #define PCPP_SMART_PTR(T) std::auto_ptr - #endif - - // forward declarations - class IDnsResource; - - /** - * @class IDnsResourceData - * A wrapper class for storing DNS RR (resource record) data. This is the base class which introduces several abstract - * methods for derived classes to implement for setting and retrieving the stored data. Each derived class will store - * different type of DNS RR data and implement these methods accordingly (for example: IPv4/IPv6 addresses, MX data, - * hostnames, raw byte data etc.) - */ - class IDnsResourceData - { - protected: - - // unimplemented private copy c'tor - IDnsResourceData(const IDnsResourceData& other); - IDnsResourceData() { } - - size_t decodeName(const char* encodedName, char* result, IDnsResource* dnsResource) const; - void encodeName(const std::string& decodedName, char* result, size_t& resultLen, IDnsResource* dnsResource) const; - - public: - /** - * A virtual d'tor, does nothing - */ - virtual ~IDnsResourceData() { } - - /** - * A templated method which takes a class that derives from IDnsResourceData as the template argument and - * checks whether this instance is of this type - * @return True if this instance is of the requested type, false otherwise - */ - template - bool isTypeOf() const { return dynamic_cast(this) != NULL; } - - /** - * A templated method which take a class that derives from IDnsResourceData as the template argument and tries to - * cast the current instance as that type - * @return A pointer to the current instance casted as the requested type or NULL if this instance isn't of this type - */ - template - IDnsResourceDataType* castAs() { return dynamic_cast(this); } - - /** - * @return A string that represents the current DNS RR data - */ - virtual std::string toString() const = 0; - - /** - * Convert the DNS RR data into a byte array - * @param[out] arr A pointer to a pre-allocated byte array where the result will be written to - * @param[out] arrLength A reference to a 2-byte number where the result array length will be written to - * @param[in] dnsResource A pointer to a DNS resource object where this DNS RR data will be stored - * @return True if the DNS RR data was successfully converted into a byte array and written to the given array or - * false if stored DNS RR data is invalid or if it could not be written to the given array - */ - virtual bool toByteArr(uint8_t* arr, size_t& arrLength, IDnsResource* dnsResource) const = 0; - }; - - - /** - * @class DnsResourceDataPtr - * A smart pointer class that holds pointers of type IDnsResourceData. This object is used in DnsResource#getData() - */ - class DnsResourceDataPtr : public PCPP_SMART_PTR(IDnsResourceData) - { - public: - - /** - * A c'tor to this class - * @param[in] ptr A pointer to IDnsResourceData - */ - explicit DnsResourceDataPtr(IDnsResourceData* ptr) : PCPP_SMART_PTR(IDnsResourceData)(ptr) {} - - //Visual studio has always been stupid about returning something useful for __cplusplus - //Only recently was this fixed - and even then it requires a specific hack to the command line during build - //Its easier/more consistent to test _MSC_VER in VS - //https://docs.microsoft.com/en-us/cpp/build/reference/zc-cplusplus?view=vs-2017 - -#if __cplusplus <= 199711L && _MSC_VER < 1800 //Maybe this can be 1600 for VS2010 - DnsResourceDataPtr(const DnsResourceDataPtr& other) : PCPP_SMART_PTR(IDnsResourceData)((DnsResourceDataPtr&)other) {} +namespace pcpp { + +// Visual studio has always been stupid about returning something useful for +// __cplusplus Only recently was this fixed - and even then it requires a +// specific hack to the command line during build Its easier/more consistent to +// test _MSC_VER in VS +// https://docs.microsoft.com/en-us/cpp/build/reference/zc-cplusplus?view=vs-2017 + +#if __cplusplus > 199711L || \ + _MSC_VER >= 1800 // Maybe this can be 1600 for VS2010 +#define PCPP_SMART_PTR(T) std::unique_ptr +#else +#define PCPP_SMART_PTR(T) std::auto_ptr #endif - /** - * A templated method which takes a class that derives from IDnsResourceData as the template argument and - * checks whether the pointer stored in this object is of this type - * @return True if the stored pointer is of the requested type, false otherwise - */ - template - bool isTypeOf() const { return get()->isTypeOf(); } - - /** - * A templated method which take a class that derives from IDnsResourceData as the template argument and tries to - * cast the pointer stored in this object as that type - * @return A pointer to the stored pointer casted as the requested type or NULL if it isn't of this type - */ - template - IDnsResourceDataType* castAs() { return get()->castAs();} - }; - - - /** - * @class StringDnsResourceData - * A class that represents DNS RR string data, mainly used in DNS RRs that store hostnames (like CNAME, DNAME, NS, etc.) - */ - class StringDnsResourceData : public IDnsResourceData - { - private: - std::string m_Data; - - public: - - /** - * A c'tor for this class - * @param[in] data The string data to store in this object. If this string represents a hostname it's possible - * to include a pointer to another string in the DNS layer (as explained here: http://www.zytrax.com/books/dns/ch15/#name). - * These pointers are often used to reduce the DNS packet size and avoid unnecessary duplications. The way to include pointers - * in a hostname string is to use the following format: 'some.domain.#{offset}' where '#{offset}' is the offset from the - * start of the DNS layer. For example: if the string 'yahoo.com' already appears in offset 12 in the packet and you want - * to set the DNS RR data as 'my.subdomain.yahoo.com' you may use the following string: 'my.subdomain.#12'. - * This will result in writing 'my.subdomain' and a pointer to offset 12 - */ - explicit StringDnsResourceData(const std::string& data) : m_Data(data) {} - - StringDnsResourceData(const uint8_t* dataPtr, size_t dataLen, IDnsResource* dnsResource); - - ~StringDnsResourceData() {} - - /** - * Equality operator overload for this class that compares the strings stored in each object - * @param[in] other The object to compare with - * @return True if the string data is the same in both objects, false otherwise - */ - bool operator==(const StringDnsResourceData& other) const { return m_Data == other.m_Data; } - - // implement abstract methods - - std::string toString() const { return m_Data; } - bool toByteArr(uint8_t* arr, size_t& arrLength, IDnsResource* dnsResource) const; - }; - - - /** - * @class IPv4DnsResourceData - * A class that represents DNS RR IPv4 data, mainly used in DNS RRs of type ::DNS_TYPE_A - */ - class IPv4DnsResourceData : public IDnsResourceData - { - private: - IPv4Address m_Data; - - public: - - /** - * A c'tor for this class - * @param[in] dataPtr A byte array of size 4 that contains an IPv4 address (each byte represents 1 octet) - * @param[in] dataLen The byte array size, expected to be 4 - */ - IPv4DnsResourceData(const uint8_t* dataPtr, size_t dataLen); - - /** - * A c'tor for this class - * @param[in] addr The IPv4 address to store in this object - */ - explicit IPv4DnsResourceData(const IPv4Address& addr) : m_Data(addr) {} - - /** - * A c'tor for this class - * @param[in] addrAsString A string representation of an IPv4 address to store in this object - */ - explicit IPv4DnsResourceData(const std::string& addrAsString) : m_Data(addrAsString) {} - - /** - * Equality operator overload for this class that compares the IPv4 addresses stored in each object - * @param[in] other The object to compare with - * @return True if IPv4 addresses are the same in both objects, false otherwise - */ - bool operator==(const IPv4DnsResourceData& other) const { return m_Data == other.m_Data; } - - /** - * @return The IPv4 address stored in this object - */ - IPv4Address getIpAddress() const { return m_Data; } - - // implement abstract methods - - std::string toString() const { return m_Data.toString(); } - bool toByteArr(uint8_t* arr, size_t& arrLength, IDnsResource* dnsResource) const; - }; - - - /** - * @class IPv6DnsResourceData - * A class that represents DNS RR IPv6 data, mainly used in DNS RRs of type ::DNS_TYPE_AAAA - */ - class IPv6DnsResourceData : public IDnsResourceData - { - private: - IPv6Address m_Data; - - public: - - /** - * A c'tor for this class - * @param[in] dataPtr A byte array of size 16 that contains an IPv6 address (each byte represents 1 octet) - * @param[in] dataLen The byte array size, expected to be 16 - */ - IPv6DnsResourceData(const uint8_t* dataPtr, size_t dataLen); - - /** - * A c'tor for this class - * @param[in] addr The IPv6 address to store in this object - */ - explicit IPv6DnsResourceData(const IPv6Address& addr) : m_Data(addr) {} - - /** - * A c'tor for this class - * @param[in] addrAsString A string representation of an IPv6 address to store in this object - */ - explicit IPv6DnsResourceData(const std::string& addrAsString) : m_Data(addrAsString) {} - - /** - * Equality operator overload for this class that compares the IPv6 addresses stored in each object - * @param[in] other The object to compare with - * @return True if IPv6 addresses are the same in both objects, false otherwise - */ - bool operator==(const IPv6DnsResourceData& other) const { return m_Data == other.m_Data; } - - /** - * @return The IPv6 address stored in this object - */ - IPv6Address getIpAddress() const { return m_Data; } - - // implement abstract methods - - std::string toString() const { return m_Data.toString(); } - bool toByteArr(uint8_t* arr, size_t& arrLength, IDnsResource* dnsResource) const; - }; - - - /** - * @class MxDnsResourceData - * A class that represents DNS RR mail exchange (MX) data, used in DNS RRs of type ::DNS_TYPE_MX - */ - class MxDnsResourceData : public IDnsResourceData - { - public: - - /** - * A struct that represents mail exchange (MX) data - */ - struct MxData - { - /** Preference value */ - uint16_t preference; - /** Mail exchange hostname */ - std::string mailExchange; - }; - - /** - * A c'tor for this class - * @param[in] dataPtr A byte array that contains the raw MX data (as written in the DNS packet) - * @param[in] dataLen The byte array size - * @param[in] dnsResource A pointer to a DNS resource object where this DNS RR data will be stored - */ - MxDnsResourceData(uint8_t* dataPtr, size_t dataLen, IDnsResource* dnsResource); - - /** - * A c'tor for this class - * @param[in] preference The MX preference value to store in this object - * @param[in] mailExchange The MX hostname value to store in this object. It's possible to include a pointer to - * another string in the DNS layer (as explained here: http://www.zytrax.com/books/dns/ch15/#name). These pointers - * are often used to reduce the DNS packet size and avoid unnecessary duplications. The way to include pointers - * in the hostname string is to use the following format: 'some.domain.#{offset}' where '#{offset}' is the offset - * from the start of the DNS layer. For example: if the string 'yahoo.com' already appears in offset 12 in the - * packet and you want to set the DNS RR data as 'my.subdomain.yahoo.com' you may use the following string: - * 'my.subdomain.#12'. This will result in writing 'my.subdomain' and a pointer to offset 12 - */ - MxDnsResourceData(const uint16_t& preference, const std::string& mailExchange); - - ~MxDnsResourceData() {} - - /** - * Equality operator overload for this class that compares the MX data stored in each object - * @param[in] other The object to compare with - * @return True if MX data is the same in both objects, meaning both preference and MX hostname are the same, - * false otherwise - */ - bool operator==(const MxDnsResourceData& other) const; - - /** - * @return The MX data stored in this object - */ - MxData getMxData() const { return m_Data; } - - /** - * Set the MX data stored in this object - * @param[in] preference The MX preference value to store in this object - * @param[in] mailExchange The MX hostname value to store in this object - */ - void setMxData(uint16_t preference, std::string mailExchange); - - // implement abstract methods - - /** - * A string representation of the MX data stored in this object. The string format is as follows: - * 'pref: {preference_value}; mx: {mail_exchange_hostname_value}' - */ - std::string toString() const; - - bool toByteArr(uint8_t* arr, size_t& arrLength, IDnsResource* dnsResource) const; - - private: - MxData m_Data; - }; - - - /** - * @class GenericDnsResourceData - * A class that represents generic DNS RR data which cannot be represented in any of the other classes. It stores the - * DNS RR data as byte array - */ - class GenericDnsResourceData : public IDnsResourceData - { - private: - uint8_t* m_Data; - size_t m_DataLen; - - public: - - /** - * A c'tor for this class - * @param[in] dataPtr A byte array that contains the raw data (as it written in the DNS packet). The data will be - * copied from this byte array to the object - * @param[in] dataLen The byte array size - */ - GenericDnsResourceData(const uint8_t* dataPtr, size_t dataLen); - - /** - * A c'tor for this class - * @param[in] dataAsHexString A hex string that represents the DNS RR data - */ - explicit GenericDnsResourceData(const std::string& dataAsHexString); - - /** - * A copy c'tor for this class - * @param[in] other The instance to copy from - */ - GenericDnsResourceData(const GenericDnsResourceData& other); - - ~GenericDnsResourceData() { if (m_Data != NULL) delete [] m_Data; } - - GenericDnsResourceData& operator=(const GenericDnsResourceData& other); - - /** - * Equality operator overload for this class that compares the raw data stored in each object - * @param[in] other The object to compare with - * @return True if data is the same in both objects, meaning byte streams are equal, false otherwise - */ - bool operator==(const GenericDnsResourceData& other) const; - - // implement abstract methods - - std::string toString() const; - bool toByteArr(uint8_t* arr, size_t& arrLength, IDnsResource* dnsResource) const; - }; - -} +// forward declarations +class IDnsResource; + +/** + * @class IDnsResourceData + * A wrapper class for storing DNS RR (resource record) data. This is the base + * class which introduces several abstract methods for derived classes to + * implement for setting and retrieving the stored data. Each derived class will + * store different type of DNS RR data and implement these methods accordingly + * (for example: IPv4/IPv6 addresses, MX data, hostnames, raw byte data etc.) + */ +class IDnsResourceData { + protected: + // unimplemented private copy c'tor + IDnsResourceData(const IDnsResourceData& other); + IDnsResourceData() {} + + size_t decodeName(const char* encodedName, char* result, + IDnsResource* dnsResource) const; + void encodeName(const std::string& decodedName, char* result, + size_t& resultLen, IDnsResource* dnsResource) const; + + public: + /** + * A virtual d'tor, does nothing + */ + virtual ~IDnsResourceData() {} + + /** + * A templated method which takes a class that derives from IDnsResourceData + * as the template argument and checks whether this instance is of this type + * @return True if this instance is of the requested type, false otherwise + */ + template + bool isTypeOf() const { + return dynamic_cast(this) != NULL; + } + + /** + * A templated method which take a class that derives from IDnsResourceData as + * the template argument and tries to cast the current instance as that type + * @return A pointer to the current instance casted as the requested type or + * NULL if this instance isn't of this type + */ + template + IDnsResourceDataType* castAs() { + return dynamic_cast(this); + } + + /** + * @return A string that represents the current DNS RR data + */ + virtual std::string toString() const = 0; + + /** + * Convert the DNS RR data into a byte array + * @param[out] arr A pointer to a pre-allocated byte array where the result + * will be written to + * @param[out] arrLength A reference to a 2-byte number where the result array + * length will be written to + * @param[in] dnsResource A pointer to a DNS resource object where this DNS RR + * data will be stored + * @return True if the DNS RR data was successfully converted into a byte + * array and written to the given array or false if stored DNS RR data is + * invalid or if it could not be written to the given array + */ + virtual bool toByteArr(uint8_t* arr, size_t& arrLength, + IDnsResource* dnsResource) const = 0; +}; + +/** + * @class DnsResourceDataPtr + * A smart pointer class that holds pointers of type IDnsResourceData. This + * object is used in DnsResource#getData() + */ +class DnsResourceDataPtr : public PCPP_SMART_PTR(IDnsResourceData) { + public: + /** + * A c'tor to this class + * @param[in] ptr A pointer to IDnsResourceData + */ + explicit DnsResourceDataPtr(IDnsResourceData* ptr) + : PCPP_SMART_PTR(IDnsResourceData)(ptr) {} + + // Visual studio has always been stupid about returning something useful for + // __cplusplus Only recently was this fixed - and even then it requires a + // specific hack to the command line during build Its easier/more consistent to + // test _MSC_VER in VS + // https://docs.microsoft.com/en-us/cpp/build/reference/zc-cplusplus?view=vs-2017 + +#if __cplusplus <= 199711L && \ + _MSC_VER < 1800 // Maybe this can be 1600 for VS2010 + DnsResourceDataPtr(const DnsResourceDataPtr& other) + : PCPP_SMART_PTR(IDnsResourceData)((DnsResourceDataPtr&)other) {} +#endif + + /** + * A templated method which takes a class that derives from IDnsResourceData + * as the template argument and checks whether the pointer stored in this + * object is of this type + * @return True if the stored pointer is of the requested type, false + * otherwise + */ + template + bool isTypeOf() const { + return get()->isTypeOf(); + } + + /** + * A templated method which take a class that derives from IDnsResourceData as + * the template argument and tries to cast the pointer stored in this object + * as that type + * @return A pointer to the stored pointer casted as the requested type or + * NULL if it isn't of this type + */ + template + IDnsResourceDataType* castAs() { + return get()->castAs(); + } +}; + +/** + * @class StringDnsResourceData + * A class that represents DNS RR string data, mainly used in DNS RRs that store + * hostnames (like CNAME, DNAME, NS, etc.) + */ +class StringDnsResourceData : public IDnsResourceData { + private: + std::string m_Data; + + public: + /** + * A c'tor for this class + * @param[in] data The string data to store in this object. If this string + * represents a hostname it's possible to include a pointer to another string + * in the DNS layer (as explained here: + * http://www.zytrax.com/books/dns/ch15/#name). These pointers are often used + * to reduce the DNS packet size and avoid unnecessary duplications. The way + * to include pointers in a hostname string is to use the following format: + * 'some.domain.#{offset}' where '#{offset}' is the offset from the start of + * the DNS layer. For example: if the string 'yahoo.com' already appears in + * offset 12 in the packet and you want to set the DNS RR data as + * 'my.subdomain.yahoo.com' you may use the following string: + * 'my.subdomain.#12'. This will result in writing 'my.subdomain' and a + * pointer to offset 12 + */ + explicit StringDnsResourceData(const std::string& data) : m_Data(data) {} + + StringDnsResourceData(const uint8_t* dataPtr, size_t dataLen, + IDnsResource* dnsResource); + + ~StringDnsResourceData() {} + + /** + * Equality operator overload for this class that compares the strings stored + * in each object + * @param[in] other The object to compare with + * @return True if the string data is the same in both objects, false + * otherwise + */ + bool operator==(const StringDnsResourceData& other) const { + return m_Data == other.m_Data; + } + + // implement abstract methods + + std::string toString() const { return m_Data; } + bool toByteArr(uint8_t* arr, size_t& arrLength, + IDnsResource* dnsResource) const; +}; + +/** + * @class IPv4DnsResourceData + * A class that represents DNS RR IPv4 data, mainly used in DNS RRs of type + * ::DNS_TYPE_A + */ +class IPv4DnsResourceData : public IDnsResourceData { + private: + IPv4Address m_Data; + + public: + /** + * A c'tor for this class + * @param[in] dataPtr A byte array of size 4 that contains an IPv4 address + * (each byte represents 1 octet) + * @param[in] dataLen The byte array size, expected to be 4 + */ + IPv4DnsResourceData(const uint8_t* dataPtr, size_t dataLen); + + /** + * A c'tor for this class + * @param[in] addr The IPv4 address to store in this object + */ + explicit IPv4DnsResourceData(const IPv4Address& addr) : m_Data(addr) {} + + /** + * A c'tor for this class + * @param[in] addrAsString A string representation of an IPv4 address to store + * in this object + */ + explicit IPv4DnsResourceData(const std::string& addrAsString) + : m_Data(addrAsString) {} + + /** + * Equality operator overload for this class that compares the IPv4 addresses + * stored in each object + * @param[in] other The object to compare with + * @return True if IPv4 addresses are the same in both objects, false + * otherwise + */ + bool operator==(const IPv4DnsResourceData& other) const { + return m_Data == other.m_Data; + } + + /** + * @return The IPv4 address stored in this object + */ + IPv4Address getIpAddress() const { return m_Data; } + + // implement abstract methods + + std::string toString() const { return m_Data.toString(); } + bool toByteArr(uint8_t* arr, size_t& arrLength, + IDnsResource* dnsResource) const; +}; + +/** + * @class IPv6DnsResourceData + * A class that represents DNS RR IPv6 data, mainly used in DNS RRs of type + * ::DNS_TYPE_AAAA + */ +class IPv6DnsResourceData : public IDnsResourceData { + private: + IPv6Address m_Data; + + public: + /** + * A c'tor for this class + * @param[in] dataPtr A byte array of size 16 that contains an IPv6 address + * (each byte represents 1 octet) + * @param[in] dataLen The byte array size, expected to be 16 + */ + IPv6DnsResourceData(const uint8_t* dataPtr, size_t dataLen); + + /** + * A c'tor for this class + * @param[in] addr The IPv6 address to store in this object + */ + explicit IPv6DnsResourceData(const IPv6Address& addr) : m_Data(addr) {} + + /** + * A c'tor for this class + * @param[in] addrAsString A string representation of an IPv6 address to store + * in this object + */ + explicit IPv6DnsResourceData(const std::string& addrAsString) + : m_Data(addrAsString) {} + + /** + * Equality operator overload for this class that compares the IPv6 addresses + * stored in each object + * @param[in] other The object to compare with + * @return True if IPv6 addresses are the same in both objects, false + * otherwise + */ + bool operator==(const IPv6DnsResourceData& other) const { + return m_Data == other.m_Data; + } + + /** + * @return The IPv6 address stored in this object + */ + IPv6Address getIpAddress() const { return m_Data; } + + // implement abstract methods + + std::string toString() const { return m_Data.toString(); } + bool toByteArr(uint8_t* arr, size_t& arrLength, + IDnsResource* dnsResource) const; +}; + +/** + * @class MxDnsResourceData + * A class that represents DNS RR mail exchange (MX) data, used in DNS RRs of + * type ::DNS_TYPE_MX + */ +class MxDnsResourceData : public IDnsResourceData { + public: + /** + * A struct that represents mail exchange (MX) data + */ + struct MxData { + /** Preference value */ + uint16_t preference; + /** Mail exchange hostname */ + std::string mailExchange; + }; + + /** + * A c'tor for this class + * @param[in] dataPtr A byte array that contains the raw MX data (as written + * in the DNS packet) + * @param[in] dataLen The byte array size + * @param[in] dnsResource A pointer to a DNS resource object where this DNS RR + * data will be stored + */ + MxDnsResourceData(uint8_t* dataPtr, size_t dataLen, + IDnsResource* dnsResource); + + /** + * A c'tor for this class + * @param[in] preference The MX preference value to store in this object + * @param[in] mailExchange The MX hostname value to store in this object. It's + * possible to include a pointer to another string in the DNS layer (as + * explained here: http://www.zytrax.com/books/dns/ch15/#name). These pointers + * are often used to reduce the DNS packet size and avoid unnecessary + * duplications. The way to include pointers in the hostname string is to use + * the following format: 'some.domain.#{offset}' where '#{offset}' is the + * offset from the start of the DNS layer. For example: if the string + * 'yahoo.com' already appears in offset 12 in the packet and you want to set + * the DNS RR data as 'my.subdomain.yahoo.com' you may use the following + * string: 'my.subdomain.#12'. This will result in writing 'my.subdomain' and + * a pointer to offset 12 + */ + MxDnsResourceData(const uint16_t& preference, + const std::string& mailExchange); + + ~MxDnsResourceData() {} + + /** + * Equality operator overload for this class that compares the MX data stored + * in each object + * @param[in] other The object to compare with + * @return True if MX data is the same in both objects, meaning both + * preference and MX hostname are the same, false otherwise + */ + bool operator==(const MxDnsResourceData& other) const; + + /** + * @return The MX data stored in this object + */ + MxData getMxData() const { return m_Data; } + + /** + * Set the MX data stored in this object + * @param[in] preference The MX preference value to store in this object + * @param[in] mailExchange The MX hostname value to store in this object + */ + void setMxData(uint16_t preference, std::string mailExchange); + + // implement abstract methods + + /** + * A string representation of the MX data stored in this object. The string + * format is as follows: 'pref: {preference_value}; mx: + * {mail_exchange_hostname_value}' + */ + std::string toString() const; + + bool toByteArr(uint8_t* arr, size_t& arrLength, + IDnsResource* dnsResource) const; + + private: + MxData m_Data; +}; + +/** + * @class GenericDnsResourceData + * A class that represents generic DNS RR data which cannot be represented in + * any of the other classes. It stores the DNS RR data as byte array + */ +class GenericDnsResourceData : public IDnsResourceData { + private: + uint8_t* m_Data; + size_t m_DataLen; + + public: + /** + * A c'tor for this class + * @param[in] dataPtr A byte array that contains the raw data (as it written + * in the DNS packet). The data will be copied from this byte array to the + * object + * @param[in] dataLen The byte array size + */ + GenericDnsResourceData(const uint8_t* dataPtr, size_t dataLen); + + /** + * A c'tor for this class + * @param[in] dataAsHexString A hex string that represents the DNS RR data + */ + explicit GenericDnsResourceData(const std::string& dataAsHexString); + + /** + * A copy c'tor for this class + * @param[in] other The instance to copy from + */ + GenericDnsResourceData(const GenericDnsResourceData& other); + + ~GenericDnsResourceData() { + if (m_Data != NULL) + delete[] m_Data; + } + + GenericDnsResourceData& operator=(const GenericDnsResourceData& other); + + /** + * Equality operator overload for this class that compares the raw data stored + * in each object + * @param[in] other The object to compare with + * @return True if data is the same in both objects, meaning byte streams are + * equal, false otherwise + */ + bool operator==(const GenericDnsResourceData& other) const; + + // implement abstract methods + + std::string toString() const; + bool toByteArr(uint8_t* arr, size_t& arrLength, + IDnsResource* dnsResource) const; +}; + +} // namespace pcpp #endif // PACKETPP_DNS_RESOURCE_DATA diff --git a/Packet++/header/EthDot3Layer.h b/Packet++/header/EthDot3Layer.h index 1de207c229..071fa2674d 100644 --- a/Packet++/header/EthDot3Layer.h +++ b/Packet++/header/EthDot3Layer.h @@ -10,119 +10,135 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ +namespace pcpp { - /** - * @struct ether_dot3_header - * Represents an IEEE 802.3 Ethernet header - */ +/** + * @struct ether_dot3_header + * Represents an IEEE 802.3 Ethernet header + */ #pragma pack(push, 1) - struct ether_dot3_header - { - /** Destination MAC */ - uint8_t dstMac[6]; - /** Source MAC */ - uint8_t srcMac[6]; - /** EtherType */ - uint16_t length; - }; +struct ether_dot3_header { + /** Destination MAC */ + uint8_t dstMac[6]; + /** Source MAC */ + uint8_t srcMac[6]; + /** EtherType */ + uint16_t length; +}; #pragma pack(pop) - /** - * @class EthDot3Layer - * Represents an IEEE 802.3 Ethernet protocol layer - */ - class EthDot3Layer : public Layer - { - public: - - /** - * A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data (will be casted to ether_dot3_header) - * @param[in] dataLen Size of the data in bytes - * @param[in] packet A pointer to the Packet instance where layer will be stored in - */ - EthDot3Layer(uint8_t* data, size_t dataLen, Packet* packet) : Layer(data, dataLen, NULL, packet) { m_Protocol = EthernetDot3; } - - /** - * A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data (will be casted to ether_header) - * @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 - */ - EthDot3Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) { m_Protocol = EthernetDot3; } - - /** - * A constructor that creates a new IEEE 802.3 Ethernet header and allocates the data - * @param[in] sourceMac The source MAC address - * @param[in] destMac The destination MAC address - * @param[in] length The frame length - */ - EthDot3Layer(const MacAddress& sourceMac, const MacAddress& destMac, uint16_t length); - - ~EthDot3Layer() {} - - /** - * Get a pointer to the Ethernet header. Notice this points directly to the data, so every change will change the actual packet data - * @return A pointer to the ether_header - */ - ether_dot3_header* getEthHeader() const { return (ether_dot3_header*)m_Data; } - - /** - * Get the source MAC address - * @return The source MAC address - */ - MacAddress getSourceMac() const { return MacAddress(getEthHeader()->srcMac); } - - /** - * Set source MAC address - * @param sourceMac Source MAC to set - */ - void setSourceMac(const MacAddress& sourceMac) { sourceMac.copyTo(getEthHeader()->srcMac); } - - /** - * Get the destination MAC address - * @return The destination MAC address - */ - MacAddress getDestMac() const { return MacAddress(getEthHeader()->dstMac); } - - /** - * Set destination MAC address - * @param destMac Destination MAC to set - */ - void setDestMac(const MacAddress& destMac) { destMac.copyTo(getEthHeader()->dstMac); } - - // implement abstract methods - - /** - * Parses next layer - */ - void parseNextLayer(); - - /** - * @return Size of ether_dot3_header - */ - size_t getHeaderLen() const { return sizeof(ether_dot3_header); } - - /** - * Does nothing for this layer - */ - void computeCalculateFields() {} - - std::string toString() const; - - OsiModelLayer getOsiModelLayer() const { return OsiModelDataLinkLayer; } - - /** - * A static method that validates the input data - * @param[in] data The pointer to the beginning of a byte stream of an IEEE 802.3 Eth packet - * @param[in] dataLen The length of the byte stream - * @return True if the data is valid and can represent an IEEE 802.3 Eth packet - */ - static bool isDataValid(const uint8_t* data, size_t dataLen); - }; +/** + * @class EthDot3Layer + * Represents an IEEE 802.3 Ethernet protocol layer + */ +class EthDot3Layer : public Layer { + public: + /** + * A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data (will be casted to + * ether_dot3_header) + * @param[in] dataLen Size of the data in bytes + * @param[in] packet A pointer to the Packet instance where layer will be + * stored in + */ + EthDot3Layer(uint8_t* data, size_t dataLen, Packet* packet) + : Layer(data, dataLen, NULL, packet) { + m_Protocol = EthernetDot3; + } + + /** + * A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data (will be casted to ether_header) + * @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 + */ + EthDot3Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : Layer(data, dataLen, prevLayer, packet) { + m_Protocol = EthernetDot3; + } + + /** + * A constructor that creates a new IEEE 802.3 Ethernet header and allocates + * the data + * @param[in] sourceMac The source MAC address + * @param[in] destMac The destination MAC address + * @param[in] length The frame length + */ + EthDot3Layer(const MacAddress& sourceMac, const MacAddress& destMac, + uint16_t length); + + ~EthDot3Layer() {} + + /** + * Get a pointer to the Ethernet header. Notice this points directly to the + * data, so every change will change the actual packet data + * @return A pointer to the ether_header + */ + ether_dot3_header* getEthHeader() const { + return (ether_dot3_header*)m_Data; + } + + /** + * Get the source MAC address + * @return The source MAC address + */ + MacAddress getSourceMac() const { return MacAddress(getEthHeader()->srcMac); } + + /** + * Set source MAC address + * @param sourceMac Source MAC to set + */ + void setSourceMac(const MacAddress& sourceMac) { + sourceMac.copyTo(getEthHeader()->srcMac); + } + + /** + * Get the destination MAC address + * @return The destination MAC address + */ + MacAddress getDestMac() const { return MacAddress(getEthHeader()->dstMac); } + + /** + * Set destination MAC address + * @param destMac Destination MAC to set + */ + void setDestMac(const MacAddress& destMac) { + destMac.copyTo(getEthHeader()->dstMac); + } + + // implement abstract methods + + /** + * Parses next layer + */ + void parseNextLayer(); + + /** + * @return Size of ether_dot3_header + */ + size_t getHeaderLen() const { return sizeof(ether_dot3_header); } + + /** + * Does nothing for this layer + */ + void computeCalculateFields() {} + + std::string toString() const; + + OsiModelLayer getOsiModelLayer() const { return OsiModelDataLinkLayer; } + + /** + * A static method that validates the input data + * @param[in] data The pointer to the beginning of a byte stream of an IEEE + * 802.3 Eth packet + * @param[in] dataLen The length of the byte stream + * @return True if the data is valid and can represent an IEEE 802.3 Eth + * packet + */ + static bool isDataValid(const uint8_t* data, size_t dataLen); +}; } // namespace pcpp diff --git a/Packet++/header/EthLayer.h b/Packet++/header/EthLayer.h index a9c12331f9..1546f8eaa9 100644 --- a/Packet++/header/EthLayer.h +++ b/Packet++/header/EthLayer.h @@ -10,158 +10,174 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ +namespace pcpp { - /** - * @struct ether_header - * Represents an Ethernet II header - */ +/** + * @struct ether_header + * Represents an Ethernet II header + */ #pragma pack(push, 1) - struct ether_header - { - /** Destination MAC */ - uint8_t dstMac[6]; - /** Source MAC */ - uint8_t srcMac[6]; - /** EtherType */ - uint16_t etherType; - }; +struct ether_header { + /** Destination MAC */ + uint8_t dstMac[6]; + /** Source MAC */ + uint8_t srcMac[6]; + /** EtherType */ + uint16_t etherType; +}; #pragma pack(pop) - /* Ethernet protocol ID's */ - - /** IP */ -#define PCPP_ETHERTYPE_IP 0x0800 - /** Address resolution */ -#define PCPP_ETHERTYPE_ARP 0x0806 - /** Transparent Ethernet Bridging */ -#define PCPP_ETHERTYPE_ETHBRIDGE 0x6558 - /** Reverse ARP */ -#define PCPP_ETHERTYPE_REVARP 0x8035 - /** AppleTalk protocol */ -#define PCPP_ETHERTYPE_AT 0x809B - /** AppleTalk ARP */ -#define PCPP_ETHERTYPE_AARP 0x80F3 - /** IEEE 802.1Q VLAN tagging */ -#define PCPP_ETHERTYPE_VLAN 0x8100 - /** IPX */ -#define PCPP_ETHERTYPE_IPX 0x8137 - /** IP protocol version 6 */ -#define PCPP_ETHERTYPE_IPV6 0x86dd - /** used to test interfaces */ -#define PCPP_ETHERTYPE_LOOPBACK 0x9000 - /** PPPoE discovery */ -#define PCPP_ETHERTYPE_PPPOED 0x8863 - /** PPPoE session */ -#define PCPP_ETHERTYPE_PPPOES 0x8864 - /** MPLS */ -#define PCPP_ETHERTYPE_MPLS 0x8847 - /** Point-to-point protocol (PPP) */ -#define PCPP_ETHERTYPE_PPP 0x880B - /** RDMA over Converged Ethernet (RoCEv1) */ -#define PCPP_ETHERTYPE_ROCEV1 0x8915 - /** IEEE 802.1ad Provider Bridge, Q-in-Q */ -#define PCPP_ETHERTYPE_IEEE_802_1AD 0x88A8 - /** Wake on LAN */ -#define PCPP_ETHERTYPE_WAKE_ON_LAN 0x0842 - - - /** - * @class EthLayer - * Represents an Ethernet II protocol layer - */ - class EthLayer : public Layer - { - public: - /** - * A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data (will be casted to ether_header) - * @param[in] dataLen Size of the data in bytes - * @param[in] packet A pointer to the Packet instance where layer will be stored in - */ - EthLayer(uint8_t* data, size_t dataLen, Packet* packet) : Layer(data, dataLen, NULL, packet) { m_Protocol = Ethernet; } - - /** - * A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data (will be casted to ether_header) - * @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 - */ - EthLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) { m_Protocol = Ethernet; } - - /** - * A constructor that creates a new Ethernet header and allocates the data - * @param[in] sourceMac The source MAC address - * @param[in] destMac The destination MAC address - * @param[in] etherType The EtherType to be used. It's an optional parameter, a value of 0 will be set if not provided - */ - EthLayer(const MacAddress& sourceMac, const MacAddress& destMac, uint16_t etherType = 0); - - ~EthLayer() {} - - /** - * Get a pointer to the Ethernet header. Notice this points directly to the data, so every change will change the actual packet data - * @return A pointer to the ether_header - */ - inline ether_header* getEthHeader() const { return (ether_header*)m_Data; } - - /** - * Get the source MAC address - * @return The source MAC address - */ - inline MacAddress getSourceMac() const { return MacAddress(getEthHeader()->srcMac); } - - /** - * Set source MAC address - * @param sourceMac Source MAC to set - */ - inline void setSourceMac(const MacAddress& sourceMac) { sourceMac.copyTo(getEthHeader()->srcMac); } - - /** - * Get the destination MAC address - * @return The destination MAC address - */ - inline MacAddress getDestMac() const { return MacAddress(getEthHeader()->dstMac); } - - /** - * Set destination MAC address - * @param destMac Destination MAC to set - */ - inline void setDestMac(const MacAddress& destMac) { destMac.copyTo(getEthHeader()->dstMac); } - - // implement abstract methods - - /** - * Currently identifies the following next layers: IPv4Layer, IPv6Layer, ArpLayer, VlanLayer, PPPoESessionLayer, PPPoEDiscoveryLayer, - * MplsLayer. - * Otherwise sets PayloadLayer - */ - void parseNextLayer(); - - /** - * @return Size of ether_header - */ - size_t getHeaderLen() const { return sizeof(ether_header); } - - /** - * Calculate ether_header#etherType for known protocols: IPv4, IPv6, ARP, VLAN - */ - void computeCalculateFields(); - - std::string toString() const; - - OsiModelLayer getOsiModelLayer() const { return OsiModelDataLinkLayer; } - - /** - * A static method that validates the input data - * @param[in] data The pointer to the beginning of a byte stream of an Ethernet II packet - * @param[in] dataLen The length of the byte stream - * @return True if the data is valid and can represent an Ethernet II packet - */ - static bool isDataValid(const uint8_t* data, size_t dataLen); - }; +/* Ethernet protocol ID's */ + +/** IP */ +#define PCPP_ETHERTYPE_IP 0x0800 +/** Address resolution */ +#define PCPP_ETHERTYPE_ARP 0x0806 +/** Transparent Ethernet Bridging */ +#define PCPP_ETHERTYPE_ETHBRIDGE 0x6558 +/** Reverse ARP */ +#define PCPP_ETHERTYPE_REVARP 0x8035 +/** AppleTalk protocol */ +#define PCPP_ETHERTYPE_AT 0x809B +/** AppleTalk ARP */ +#define PCPP_ETHERTYPE_AARP 0x80F3 +/** IEEE 802.1Q VLAN tagging */ +#define PCPP_ETHERTYPE_VLAN 0x8100 +/** IPX */ +#define PCPP_ETHERTYPE_IPX 0x8137 +/** IP protocol version 6 */ +#define PCPP_ETHERTYPE_IPV6 0x86dd +/** used to test interfaces */ +#define PCPP_ETHERTYPE_LOOPBACK 0x9000 +/** PPPoE discovery */ +#define PCPP_ETHERTYPE_PPPOED 0x8863 +/** PPPoE session */ +#define PCPP_ETHERTYPE_PPPOES 0x8864 +/** MPLS */ +#define PCPP_ETHERTYPE_MPLS 0x8847 +/** Point-to-point protocol (PPP) */ +#define PCPP_ETHERTYPE_PPP 0x880B +/** RDMA over Converged Ethernet (RoCEv1) */ +#define PCPP_ETHERTYPE_ROCEV1 0x8915 +/** IEEE 802.1ad Provider Bridge, Q-in-Q */ +#define PCPP_ETHERTYPE_IEEE_802_1AD 0x88A8 +/** Wake on LAN */ +#define PCPP_ETHERTYPE_WAKE_ON_LAN 0x0842 + +/** + * @class EthLayer + * Represents an Ethernet II protocol layer + */ +class EthLayer : public Layer { + public: + /** + * A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data (will be casted to ether_header) + * @param[in] dataLen Size of the data in bytes + * @param[in] packet A pointer to the Packet instance where layer will be + * stored in + */ + EthLayer(uint8_t* data, size_t dataLen, Packet* packet) + : Layer(data, dataLen, NULL, packet) { + m_Protocol = Ethernet; + } + + /** + * A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data (will be casted to ether_header) + * @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 + */ + EthLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : Layer(data, dataLen, prevLayer, packet) { + m_Protocol = Ethernet; + } + + /** + * A constructor that creates a new Ethernet header and allocates the data + * @param[in] sourceMac The source MAC address + * @param[in] destMac The destination MAC address + * @param[in] etherType The EtherType to be used. It's an optional parameter, + * a value of 0 will be set if not provided + */ + EthLayer(const MacAddress& sourceMac, const MacAddress& destMac, + uint16_t etherType = 0); + + ~EthLayer() {} + + /** + * Get a pointer to the Ethernet header. Notice this points directly to the + * data, so every change will change the actual packet data + * @return A pointer to the ether_header + */ + inline ether_header* getEthHeader() const { return (ether_header*)m_Data; } + + /** + * Get the source MAC address + * @return The source MAC address + */ + inline MacAddress getSourceMac() const { + return MacAddress(getEthHeader()->srcMac); + } + + /** + * Set source MAC address + * @param sourceMac Source MAC to set + */ + inline void setSourceMac(const MacAddress& sourceMac) { + sourceMac.copyTo(getEthHeader()->srcMac); + } + + /** + * Get the destination MAC address + * @return The destination MAC address + */ + inline MacAddress getDestMac() const { + return MacAddress(getEthHeader()->dstMac); + } + + /** + * Set destination MAC address + * @param destMac Destination MAC to set + */ + inline void setDestMac(const MacAddress& destMac) { + destMac.copyTo(getEthHeader()->dstMac); + } + + // implement abstract methods + + /** + * Currently identifies the following next layers: IPv4Layer, IPv6Layer, + * ArpLayer, VlanLayer, PPPoESessionLayer, PPPoEDiscoveryLayer, MplsLayer. + * Otherwise sets PayloadLayer + */ + void parseNextLayer(); + + /** + * @return Size of ether_header + */ + size_t getHeaderLen() const { return sizeof(ether_header); } + + /** + * Calculate ether_header#etherType for known protocols: IPv4, IPv6, ARP, VLAN + */ + void computeCalculateFields(); + + std::string toString() const; + + OsiModelLayer getOsiModelLayer() const { return OsiModelDataLinkLayer; } + + /** + * A static method that validates the input data + * @param[in] data The pointer to the beginning of a byte stream of an + * Ethernet II packet + * @param[in] dataLen The length of the byte stream + * @return True if the data is valid and can represent an Ethernet II packet + */ + static bool isDataValid(const uint8_t* data, size_t dataLen); +}; } // namespace pcpp diff --git a/Packet++/header/FtpLayer.h b/Packet++/header/FtpLayer.h index ea26aac2ec..18c669228f 100644 --- a/Packet++/header/FtpLayer.h +++ b/Packet++/header/FtpLayer.h @@ -1,8 +1,8 @@ #ifndef PACKETPP_FTP_LAYER #define PACKETPP_FTP_LAYER -#include "SingleCommandTextProtocol.h" #include "PayloadLayer.h" +#include "SingleCommandTextProtocol.h" /// @file @@ -10,489 +10,509 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - - /** - * Class for general FTP message - */ - class FtpLayer : public SingleCommandTextProtocol - { - protected: - FtpLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) : SingleCommandTextProtocol(data, dataLen, prevLayer, packet) { m_Protocol = FTP; }; - FtpLayer(const std::string &command, const std::string &option) : SingleCommandTextProtocol(command, option) { m_Protocol = FTP; }; - - public: - - /** - * A static method that checks whether the port is considered as FTP control - * @param[in] port The port number to be checked - */ - static bool isFtpPort(uint16_t port) { return port == 21; } - - /** - * A static method that checks whether the port is considered as FTP data - * @param[in] port The port number to be checked - */ - static bool isFtpDataPort(uint16_t port) { return port == 20; } - - // overridden methods - - /// FTP is the always last so does nothing for this layer - void parseNextLayer() {} - - /** - * @return Get the size of the layer - */ - size_t getHeaderLen() const { return m_DataLen; } - - /// Does nothing for this layer - void computeCalculateFields() {} - - /** - * @return The OSI layer level of FTP (Application Layer). - */ - OsiModelLayer getOsiModelLayer() const { return OsiModelApplicationLayer; } - - }; - - /** - * Class for representing the request messages of FTP Layer - */ - class FtpRequestLayer : public FtpLayer - { - public: - - /** - * Enum for FTP command codes - */ - enum class FtpCommand : int - { - /// Unknown command - UNK, - /// Abort an active file transfer. - ABOR = ('A') | ('B' << 8) | ('O' << 16) | ('R' << 24), - /// Account information. - ACCT = ('A') | ('C' << 8) | ('C' << 16) | ('T' << 24), - /// Authentication/Security Data - ADAT = ('A') | ('D' << 8) | ('A' << 16) | ('T' << 24), - /// Allocate sufficient disk space to receive a file. - ALLO = ('A') | ('L' << 8) | ('L' << 16) | ('O' << 24), - /// Append (with create) - APPE = ('A') | ('P' << 8) | ('P' << 16) | ('E' << 24), - /// Authentication/Security Mechanism - AUTH = ('A') | ('U' << 8) | ('T' << 16) | ('H' << 24), - /// Get the available space - AVBL = ('A') | ('V' << 8) | ('B' << 16) | ('L' << 24), - /// Clear Command Channel - CCC = ('C') | ('C' << 8) | ('C' << 16), - /// Change to Parent Directory. - CDUP = ('C') | ('D' << 8) | ('U' << 16) | ('P' << 24), - /// Confidentiality Protection Command - CONF = ('C') | ('O' << 8) | ('N' << 16) | ('F' << 24), - /// Client / Server Identification - CSID = ('C') | ('S' << 8) | ('I' << 16) | ('D' << 24), - /// Change working directory. - CWD = ('C') | ('W' << 8) | ('D' << 16), - /// Delete file. - DELE = ('D') | ('E' << 8) | ('L' << 16) | ('E' << 24), - /// Get the directory size - DSIZ = ('D') | ('S' << 8) | ('I' << 16) | ('Z' << 24), - /// Privacy Protected Channel - ENC = ('E') | ('N' << 8) | ('C' << 16), - /// Specifies an extended address and port to which the server should connect. - EPRT = ('E') | ('P' << 8) | ('R' << 16) | ('T' << 24), - /// Enter extended passive mode. - EPSV = ('E') | ('P' << 8) | ('S' << 16) | ('V' << 24), - /// Get the feature list implemented by the server. - FEAT = ('F') | ('E' << 8) | ('A' << 16) | ('T' << 24), - /// Returns usage documentation on a command if specified, else a general help document is returned. - HELP = ('H') | ('E' << 8) | ('L' << 16) | ('P' << 24), - /// Identify desired virtual host on server, by name. - HOST = ('H') | ('O' << 8) | ('S' << 16) | ('T' << 24), - /// Language Negotiation - LANG = ('L') | ('A' << 8) | ('N' << 16) | ('G' << 24), - /// Returns information of a file or directory if specified, else information of the current working directory is returned. - LIST = ('L') | ('I' << 8) | ('S' << 16) | ('T' << 24), - /// Specifies a long address and port to which the server should connect. - LPRT = ('L') | ('P' << 8) | ('R' << 16) | ('T' << 24), - /// Enter long passive mode. - LPSV = ('L') | ('P' << 8) | ('S' << 16) | ('V' << 24), - /// Return the last-modified time of a specified file. - MDTM = ('M') | ('D' << 8) | ('T' << 16) | ('M' << 24), - /// Modify the creation time of a file. - MFCT = ('M') | ('F' << 8) | ('C' << 16) | ('T' << 24), - /// Modify fact (the last modification time, creation time, UNIX group/owner/mode of a file). - MFF = ('M') | ('F' << 8) | ('F' << 16), - /// Modify the last modification time of a file. - MFMT = ('M') | ('F' << 8) | ('M' << 16) | ('T' << 24), - /// Integrity Protected Command - MIC = ('M') | ('I' << 8) | ('C' << 16), - /// Make directory. - MKD = ('M') | ('K' << 8) | ('D' << 16), - /// Lists the contents of a directory in a standardized machine-readable format. - MLSD = ('M') | ('L' << 8) | ('S' << 16) | ('D' << 24), - /// Provides data about exactly the object named on its command line in a standardized machine-readable format. - MLST = ('M') | ('L' << 8) | ('S' << 16) | ('T' << 24), - /// Sets the transfer mode (Stream, Block, or Compressed). - MODE = ('M') | ('O' << 8) | ('D' << 16) | ('E' << 24), - /// Returns a list of file names in a specified directory. - NLST = ('N') | ('L' << 8) | ('S' << 16) | ('T' << 24), - /// No operation (dummy packet; used mostly on keepalives). - NOOP = ('N') | ('O' << 8) | ('O' << 16) | ('P' << 24), - /// Select options for a feature (for example OPTS UTF8 ON). - OPTS = ('O') | ('P' << 8) | ('T' << 16) | ('S' << 24), - /// Authentication password. - PASS = ('P') | ('A' << 8) | ('S' << 16) | ('S' << 24), - /// Enter passive mode. - PASV = ('P') | ('A' << 8) | ('S' << 16) | ('V' << 24), - /// Protection Buffer Size - PBSZ = ('P') | ('B' << 8) | ('S' << 16) | ('Z' << 24), - /// Specifies an address and port to which the server should connect. - PORT = ('P') | ('O' << 8) | ('R' << 16) | ('T' << 24), - /// Data Channel Protection Level. - PROT = ('P') | ('R' << 8) | ('O' << 16) | ('T' << 24), - /// Print working directory. Returns the current directory of the host. - PWD = ('P') | ('W' << 8) | ('D' << 16), - /// Disconnect. - QUIT = ('Q') | ('U' << 8) | ('I' << 16) | ('T' << 24), - /// Re initializes the connection. - REIN = ('R') | ('E' << 8) | ('I' << 16) | ('N' << 24), - /// Restart transfer from the specified point. - REST = ('R') | ('E' << 8) | ('S' << 16) | ('T' << 24), - /// Retrieve a copy of the file - RETR = ('R') | ('E' << 8) | ('T' << 16) | ('R' << 24), - /// Remove a directory. - RMD = ('R') | ('M' << 8) | ('D' << 16), - /// Remove a directory tree - RMDA = ('R') | ('M' << 8) | ('D' << 16) | ('A' << 24), - /// Rename from. - RNFR = ('R') | ('N' << 8) | ('F' << 16) | ('R' << 24), - /// Rename to. - RNTO = ('R') | ('N' << 8) | ('T' << 16) | ('O' << 24), - /// Sends site specific commands to remote server (like SITE IDLE 60 or SITE UMASK 002). Inspect SITE HELP output for complete list of supported commands. - SITE = ('S') | ('I' << 8) | ('T' << 16) | ('E' << 24), - /// Return the size of a file. - SIZE = ('S') | ('I' << 8) | ('Z' << 16) | ('E' << 24), - /// Mount file structure. - SMNT = ('S') | ('M' << 8) | ('N' << 16) | ('T' << 24), - /// Use single port passive mode (only one TCP port number for both control connections and passive-mode data connections) - SPSV = ('S') | ('P' << 8) | ('S' << 16) | ('V' << 24), - /// Returns information on the server status, including the status of the current connection - STAT = ('S') | ('T' << 8) | ('A' << 16) | ('T' << 24), - /// Accept the data and to store the data as a file at the server site - STOR = ('S') | ('T' << 8) | ('O' << 16) | ('R' << 24), - /// Store file uniquely. - STOU = ('S') | ('T' << 8) | ('O' << 16) | ('U' << 24), - /// Set file transfer structure. - STRU = ('S') | ('T' << 8) | ('R' << 16) | ('U' << 24), - /// Return system type. - SYST = ('S') | ('Y' << 8) | ('S' << 16) | ('T' << 24), - /// Get a thumbnail of a remote image file - THMB = ('T') | ('H' << 8) | ('M' << 16) | ('B' << 24), - /// Sets the transfer mode (ASCII/Binary). - TYPE = ('T') | ('Y' << 8) | ('P' << 16) | ('E' << 24), - /// Authentication username. - USER = ('U') | ('S' << 8) | ('E' << 16) | ('R' << 24), - /// Change to the parent of the current working directory - XCUP = ('X') | ('C' << 8) | ('U' << 16) | ('P' << 24), - /// Make a directory - XMKD = ('X') | ('M' << 8) | ('K' << 16) | ('D' << 24), - /// Print the current working directory - XPWD = ('X') | ('P' << 8) | ('W' << 16) | ('D' << 24), - /// - XRCP = ('X') | ('R' << 8) | ('C' << 16) | ('P' << 24), - /// Remove the directory - XRMD = ('X') | ('R' << 8) | ('M' << 16) | ('D' << 24), - /// - XRSQ = ('X') | ('R' << 8) | ('S' << 16) | ('Q' << 24), - /// Send, mail if cannot - XSEM = ('X') | ('S' << 8) | ('E' << 16) | ('M' << 24), - /// Send to terminal - XSEN = ('X') | ('S' << 8) | ('E' << 16) | ('N' << 24) - }; - - /** 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 - */ - FtpRequestLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) : FtpLayer(data, dataLen, prevLayer, packet) {}; - - /** - * A constructor that creates layer with provided input values - * @param[in] command FTP command - * @param[in] option Argument of the command - */ - explicit FtpRequestLayer(const FtpCommand &command, const std::string &option = "") : FtpLayer(getCommandAsString(command), option) {}; - - /** - * Set the command of request message - * @param[in] code Value to set command - * @return True if the operation is successful, false otherwise - */ - bool setCommand(FtpCommand code); - - /** - * Get the command of request message - * @return FtpCommand Value of the command - */ - FtpCommand getCommand() const; - - /** - * Get the command of request message as string - * @return std::string Value of the command as string - */ - std::string getCommandString() const; - - /** - * Set the command argument of request message - * @param[in] value Value to set command argument - * @return True if the operation is successful, false otherwise - */ - bool setCommandOption(const std::string &value); - - /** - * Get the command argument of request message - * @param[in] removeEscapeCharacters Whether non-alphanumerical characters should be removed or not - * @return std::string Value of command argument - */ - std::string getCommandOption(bool removeEscapeCharacters = true) const; - - /** - * Convert the command info to readable string - * @param[in] code Command code to convert - * @return std::string Returns the command info as readable string - */ - static std::string getCommandInfo(FtpCommand code); - - /** - * Convert the command to readable string - * @param[in] code Command code to convert - * @return std::string Returns the command as readable string - */ - static std::string getCommandAsString(FtpCommand code); - - // overridden methods - - /** - * @return Returns the protocol info as readable string - */ - std::string toString() const; - }; - - /** - * Class for representing the response messages of FTP Layer - */ - class FtpResponseLayer : public FtpLayer - { - public: - - /** - * Enum for FTP response codes - */ - enum class FtpStatusCode : int - { - /// Unknown status code - UNKNOWN, - /// Restart marker reply - RESTART_MARKER = 110, - /// Service ready in nnn minutes - SERVICE_READY_IN_MIN = 120, - /// Data connection already open; transfer starting - DATA_ALREADY_OPEN_START_TRANSFER = 125, - /// File status okay; about to open data connection - FILE_OK = 150, - /// Command okay - COMMAND_OK = 200, - /// Command not implemented, superfluous at this site - COMMAND_NOT_IMPLEMENTED_SUPERFLUOUS = 202, - /// System status, or system help reply - SYSTEM_STATUS = 211, - /// Directory status - DIR_STATUS = 212, - /// File status - FILE_STATUS = 213, - /// Help message - HELP_MESSAGE = 214, - /// NAME system type - NAME_SYSTEM_TYPE = 215, - /// Service ready for new user - SERVICE_READY_FOR_USER = 220, - /// Service closing control connection - SERVICE_CLOSING_CONTROL = 221, - /// Data connection open; no transfer in progress - DATA_OPEN_NO_TRANSFER = 225, - /// Closing data connection - CLOSING_DATA = 226, - /// Entering Passive Mode - ENTERING_PASSIVE = 227, - /// Entering Extended Passive Mode - ENTERING_EXTENDED_PASSIVE = 229, - /// User logged in, proceed - USER_LOG_IN_PROCEED = 230, - /// User logged in, authorized by security data exchange - USER_LOG_IN_AUTHORIZED = 232, - /// Security data exchange complete - SEC_DATA_EXCHANGE_COMPLETE = 234, - /// Security data exchange completed successfully - SEC_DATA_EXCHANGE_COMPLETE_SUCCESS = 235, - /// Requested file action okay, completed - REQ_FILE_OK_COMPLETE = 250, - /// PATHNAME created - PATHNAME_CREATED = 257, - /// User name okay, need password - USER_OK_NEED_PASSWORD = 331, - /// Need account for login - NEED_ACCOUNT = 332, - /// Requested security mechanism is ok - REQ_SEC_MECHANISM_OK = 334, - /// Security data is acceptable, more is required - SEC_IS_ACCEPTABLE = 335, - /// Username okay, need password. Challenge is ... - USER_OK_NEED_PASS_CHALLENGE = 336, - /// Requested file action pending further information - FILE_PENDING_ACTION = 350, - /// Service not available, closing control connection - SERVICE_NOT_AVAILABLE = 421, - /// Can't open data connection - CANT_OPEN_DATA_CONNECTION = 425, - /// Connection closed; transfer aborted - CONNECTION_CLOSED = 426, - /// Need some unavailable resource to process security - NEED_UNAVAILABLE_RESOURCE_TO_SEC = 431, - /// Requested file action not taken - REQ_FILE_ACTION_NOT_TAKEN = 450, - /// Requested action aborted: local error in processing - REQ_ACTION_ABORTED = 451, - /// Requested action not taken. Insufficient storage space in system - REQ_ACTION_NOT_TAKEN = 452, - /// Syntax error, command unrecognized - SYNTAX_ERROR_COMMAND_UNRECOGNIZED = 500, - /// Syntax error in parameters or arguments - SYNTAX_ERROR_PARAMETER_OR_ARGUMENT = 501, - /// Command not implemented - COMMAND_NOT_IMPLEMENTED = 502, - /// Bad sequence of commands - BAD_SEQUENCE_COMMANDS = 503, - /// Command not implemented for that parameter - COMMAND_NOT_IMPLEMENTED_FOR_PARAMETER = 504, - /// Network protocol not supported - NETWORK_PROTOCOL_NOT_SUPPORTED = 522, - /// Not logged in - NOT_LOGGED_IN = 530, - /// Need account for storing files - NEED_ACCOUNT_FOR_STORE_FILE = 532, - /// Command protection level denied for policy reasons - COMMAND_PROTECTION_DENIED = 533, - /// Request denied for policy reasons - REQUEST_DENIED = 534, - /// Failed security check (hash, sequence, etc) - FAILED_SEC_CHECK = 535, - /// Requested PROT level not supported by mechanism - REQ_PROT_LEVEL_NOT_SUPPORTED = 536, - /// Command protection level not supported by security mechanism - COMMAND_PROTECTION_LEVEL_NOT_SUPPORTED = 537, - /// Requested action not taken: File unavailable - FILE_UNAVAILABLE = 550, - /// Requested action aborted: page type unknown - PAGE_TYPE_UNKNOWN = 551, - /// Requested file action aborted: Exceeded storage allocation - EXCEED_STORAGE_ALLOCATION = 552, - /// Requested action not taken: File name not allowed - FILENAME_NOT_ALLOWED = 553, - /// Integrity protected reply - INTEGRITY_PROTECTED = 631, - /// Confidentiality and integrity protected reply - CONFIDENTIALITY_AND_INTEGRITY_PROTECTED = 632, - /// Confidentiality protected reply - CONFIDENTIALITY_PROTECTED = 633 - }; - - /** 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 - */ - FtpResponseLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) : FtpLayer(data, dataLen, prevLayer, packet) {}; - - /** - * A constructor that creates layer with provided input values - * @param[in] code Status code - * @param[in] option Argument of the status code - */ - explicit FtpResponseLayer(const FtpStatusCode &code, const std::string &option = "") : FtpLayer(std::to_string(int(code)), option) {}; - - /** - * Set the status code of response message - * @param[in] code Value to set status code - * @return True if the operation is successful, false otherwise - */ - bool setStatusCode(FtpStatusCode code); - - /** - * Get the status code of response message - * @return FtpStatusCode Value of the status code - */ - FtpStatusCode getStatusCode() const; - - /** - * Get the status code of response message as string - * @return std::string Value of the status code as string - */ - std::string getStatusCodeString() const; - - /** - * Set the argument of response message - * @param[in] value Value to set argument - * @return True if the operation is successful, false otherwise - */ - bool setStatusOption(const std::string &value); - - /** - * Get the argument of response message - * @param[in] removeEscapeCharacters Whether non-alphanumerical characters should be removed or not - * @return std::string Value of argument - */ - std::string getStatusOption(bool removeEscapeCharacters = true) const; - - /** - * Convert the status code to readable string - * @param[in] code Status code to convert - * @return std::string Returns the status info as readable string - */ - static std::string getStatusCodeAsString(FtpStatusCode code); - - // overridden methods - - /** - * @return Returns the protocol info as readable string - */ - std::string toString() const; - }; - - /** - * Class for representing the data of FTP Layer - */ - class FtpDataLayer : public PayloadLayer - { - public: - - /** 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 - */ - FtpDataLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) : PayloadLayer(data, dataLen, prevLayer, packet) { m_Protocol = FTP; }; - - /** - * @return Returns the protocol info as readable string - */ - std::string toString() const; - }; +namespace pcpp { + +/** + * Class for general FTP message + */ +class FtpLayer : public SingleCommandTextProtocol { + protected: + FtpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : SingleCommandTextProtocol(data, dataLen, prevLayer, packet) { + m_Protocol = FTP; + }; + FtpLayer(const std::string& command, const std::string& option) + : SingleCommandTextProtocol(command, option) { + m_Protocol = FTP; + }; + + public: + /** + * A static method that checks whether the port is considered as FTP control + * @param[in] port The port number to be checked + */ + static bool isFtpPort(uint16_t port) { return port == 21; } + + /** + * A static method that checks whether the port is considered as FTP data + * @param[in] port The port number to be checked + */ + static bool isFtpDataPort(uint16_t port) { return port == 20; } + + // overridden methods + + /// FTP is the always last so does nothing for this layer + void parseNextLayer() {} + + /** + * @return Get the size of the layer + */ + size_t getHeaderLen() const { return m_DataLen; } + + /// Does nothing for this layer + void computeCalculateFields() {} + + /** + * @return The OSI layer level of FTP (Application Layer). + */ + OsiModelLayer getOsiModelLayer() const { return OsiModelApplicationLayer; } +}; + +/** + * Class for representing the request messages of FTP Layer + */ +class FtpRequestLayer : public FtpLayer { + public: + /** + * Enum for FTP command codes + */ + enum class FtpCommand : int { + /// Unknown command + UNK, + /// Abort an active file transfer. + ABOR = ('A') | ('B' << 8) | ('O' << 16) | ('R' << 24), + /// Account information. + ACCT = ('A') | ('C' << 8) | ('C' << 16) | ('T' << 24), + /// Authentication/Security Data + ADAT = ('A') | ('D' << 8) | ('A' << 16) | ('T' << 24), + /// Allocate sufficient disk space to receive a file. + ALLO = ('A') | ('L' << 8) | ('L' << 16) | ('O' << 24), + /// Append (with create) + APPE = ('A') | ('P' << 8) | ('P' << 16) | ('E' << 24), + /// Authentication/Security Mechanism + AUTH = ('A') | ('U' << 8) | ('T' << 16) | ('H' << 24), + /// Get the available space + AVBL = ('A') | ('V' << 8) | ('B' << 16) | ('L' << 24), + /// Clear Command Channel + CCC = ('C') | ('C' << 8) | ('C' << 16), + /// Change to Parent Directory. + CDUP = ('C') | ('D' << 8) | ('U' << 16) | ('P' << 24), + /// Confidentiality Protection Command + CONF = ('C') | ('O' << 8) | ('N' << 16) | ('F' << 24), + /// Client / Server Identification + CSID = ('C') | ('S' << 8) | ('I' << 16) | ('D' << 24), + /// Change working directory. + CWD = ('C') | ('W' << 8) | ('D' << 16), + /// Delete file. + DELE = ('D') | ('E' << 8) | ('L' << 16) | ('E' << 24), + /// Get the directory size + DSIZ = ('D') | ('S' << 8) | ('I' << 16) | ('Z' << 24), + /// Privacy Protected Channel + ENC = ('E') | ('N' << 8) | ('C' << 16), + /// Specifies an extended address and port to which the server should + /// connect. + EPRT = ('E') | ('P' << 8) | ('R' << 16) | ('T' << 24), + /// Enter extended passive mode. + EPSV = ('E') | ('P' << 8) | ('S' << 16) | ('V' << 24), + /// Get the feature list implemented by the server. + FEAT = ('F') | ('E' << 8) | ('A' << 16) | ('T' << 24), + /// Returns usage documentation on a command if specified, else a general + /// help document is returned. + HELP = ('H') | ('E' << 8) | ('L' << 16) | ('P' << 24), + /// Identify desired virtual host on server, by name. + HOST = ('H') | ('O' << 8) | ('S' << 16) | ('T' << 24), + /// Language Negotiation + LANG = ('L') | ('A' << 8) | ('N' << 16) | ('G' << 24), + /// Returns information of a file or directory if specified, else + /// information of the current working directory is returned. + LIST = ('L') | ('I' << 8) | ('S' << 16) | ('T' << 24), + /// Specifies a long address and port to which the server should connect. + LPRT = ('L') | ('P' << 8) | ('R' << 16) | ('T' << 24), + /// Enter long passive mode. + LPSV = ('L') | ('P' << 8) | ('S' << 16) | ('V' << 24), + /// Return the last-modified time of a specified file. + MDTM = ('M') | ('D' << 8) | ('T' << 16) | ('M' << 24), + /// Modify the creation time of a file. + MFCT = ('M') | ('F' << 8) | ('C' << 16) | ('T' << 24), + /// Modify fact (the last modification time, creation time, UNIX + /// group/owner/mode of a file). + MFF = ('M') | ('F' << 8) | ('F' << 16), + /// Modify the last modification time of a file. + MFMT = ('M') | ('F' << 8) | ('M' << 16) | ('T' << 24), + /// Integrity Protected Command + MIC = ('M') | ('I' << 8) | ('C' << 16), + /// Make directory. + MKD = ('M') | ('K' << 8) | ('D' << 16), + /// Lists the contents of a directory in a standardized machine-readable + /// format. + MLSD = ('M') | ('L' << 8) | ('S' << 16) | ('D' << 24), + /// Provides data about exactly the object named on its command line in a + /// standardized machine-readable format. + MLST = ('M') | ('L' << 8) | ('S' << 16) | ('T' << 24), + /// Sets the transfer mode (Stream, Block, or Compressed). + MODE = ('M') | ('O' << 8) | ('D' << 16) | ('E' << 24), + /// Returns a list of file names in a specified directory. + NLST = ('N') | ('L' << 8) | ('S' << 16) | ('T' << 24), + /// No operation (dummy packet; used mostly on keepalives). + NOOP = ('N') | ('O' << 8) | ('O' << 16) | ('P' << 24), + /// Select options for a feature (for example OPTS UTF8 ON). + OPTS = ('O') | ('P' << 8) | ('T' << 16) | ('S' << 24), + /// Authentication password. + PASS = ('P') | ('A' << 8) | ('S' << 16) | ('S' << 24), + /// Enter passive mode. + PASV = ('P') | ('A' << 8) | ('S' << 16) | ('V' << 24), + /// Protection Buffer Size + PBSZ = ('P') | ('B' << 8) | ('S' << 16) | ('Z' << 24), + /// Specifies an address and port to which the server should connect. + PORT = ('P') | ('O' << 8) | ('R' << 16) | ('T' << 24), + /// Data Channel Protection Level. + PROT = ('P') | ('R' << 8) | ('O' << 16) | ('T' << 24), + /// Print working directory. Returns the current directory of the host. + PWD = ('P') | ('W' << 8) | ('D' << 16), + /// Disconnect. + QUIT = ('Q') | ('U' << 8) | ('I' << 16) | ('T' << 24), + /// Re initializes the connection. + REIN = ('R') | ('E' << 8) | ('I' << 16) | ('N' << 24), + /// Restart transfer from the specified point. + REST = ('R') | ('E' << 8) | ('S' << 16) | ('T' << 24), + /// Retrieve a copy of the file + RETR = ('R') | ('E' << 8) | ('T' << 16) | ('R' << 24), + /// Remove a directory. + RMD = ('R') | ('M' << 8) | ('D' << 16), + /// Remove a directory tree + RMDA = ('R') | ('M' << 8) | ('D' << 16) | ('A' << 24), + /// Rename from. + RNFR = ('R') | ('N' << 8) | ('F' << 16) | ('R' << 24), + /// Rename to. + RNTO = ('R') | ('N' << 8) | ('T' << 16) | ('O' << 24), + /// Sends site specific commands to remote server (like SITE IDLE 60 or SITE + /// UMASK 002). Inspect SITE HELP output for complete list of supported + /// commands. + SITE = ('S') | ('I' << 8) | ('T' << 16) | ('E' << 24), + /// Return the size of a file. + SIZE = ('S') | ('I' << 8) | ('Z' << 16) | ('E' << 24), + /// Mount file structure. + SMNT = ('S') | ('M' << 8) | ('N' << 16) | ('T' << 24), + /// Use single port passive mode (only one TCP port number for both control + /// connections and passive-mode data connections) + SPSV = ('S') | ('P' << 8) | ('S' << 16) | ('V' << 24), + /// Returns information on the server status, including the status of the + /// current connection + STAT = ('S') | ('T' << 8) | ('A' << 16) | ('T' << 24), + /// Accept the data and to store the data as a file at the server site + STOR = ('S') | ('T' << 8) | ('O' << 16) | ('R' << 24), + /// Store file uniquely. + STOU = ('S') | ('T' << 8) | ('O' << 16) | ('U' << 24), + /// Set file transfer structure. + STRU = ('S') | ('T' << 8) | ('R' << 16) | ('U' << 24), + /// Return system type. + SYST = ('S') | ('Y' << 8) | ('S' << 16) | ('T' << 24), + /// Get a thumbnail of a remote image file + THMB = ('T') | ('H' << 8) | ('M' << 16) | ('B' << 24), + /// Sets the transfer mode (ASCII/Binary). + TYPE = ('T') | ('Y' << 8) | ('P' << 16) | ('E' << 24), + /// Authentication username. + USER = ('U') | ('S' << 8) | ('E' << 16) | ('R' << 24), + /// Change to the parent of the current working directory + XCUP = ('X') | ('C' << 8) | ('U' << 16) | ('P' << 24), + /// Make a directory + XMKD = ('X') | ('M' << 8) | ('K' << 16) | ('D' << 24), + /// Print the current working directory + XPWD = ('X') | ('P' << 8) | ('W' << 16) | ('D' << 24), + /// + XRCP = ('X') | ('R' << 8) | ('C' << 16) | ('P' << 24), + /// Remove the directory + XRMD = ('X') | ('R' << 8) | ('M' << 16) | ('D' << 24), + /// + XRSQ = ('X') | ('R' << 8) | ('S' << 16) | ('Q' << 24), + /// Send, mail if cannot + XSEM = ('X') | ('S' << 8) | ('E' << 16) | ('M' << 24), + /// Send to terminal + XSEN = ('X') | ('S' << 8) | ('E' << 16) | ('N' << 24) + }; + + /** 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 + */ + FtpRequestLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet) + : FtpLayer(data, dataLen, prevLayer, packet){}; + + /** + * A constructor that creates layer with provided input values + * @param[in] command FTP command + * @param[in] option Argument of the command + */ + explicit FtpRequestLayer(const FtpCommand& command, + const std::string& option = "") + : FtpLayer(getCommandAsString(command), option){}; + + /** + * Set the command of request message + * @param[in] code Value to set command + * @return True if the operation is successful, false otherwise + */ + bool setCommand(FtpCommand code); + + /** + * Get the command of request message + * @return FtpCommand Value of the command + */ + FtpCommand getCommand() const; + + /** + * Get the command of request message as string + * @return std::string Value of the command as string + */ + std::string getCommandString() const; + + /** + * Set the command argument of request message + * @param[in] value Value to set command argument + * @return True if the operation is successful, false otherwise + */ + bool setCommandOption(const std::string& value); + + /** + * Get the command argument of request message + * @param[in] removeEscapeCharacters Whether non-alphanumerical characters + * should be removed or not + * @return std::string Value of command argument + */ + std::string getCommandOption(bool removeEscapeCharacters = true) const; + + /** + * Convert the command info to readable string + * @param[in] code Command code to convert + * @return std::string Returns the command info as readable string + */ + static std::string getCommandInfo(FtpCommand code); + + /** + * Convert the command to readable string + * @param[in] code Command code to convert + * @return std::string Returns the command as readable string + */ + static std::string getCommandAsString(FtpCommand code); + + // overridden methods + + /** + * @return Returns the protocol info as readable string + */ + std::string toString() const; +}; + +/** + * Class for representing the response messages of FTP Layer + */ +class FtpResponseLayer : public FtpLayer { + public: + /** + * Enum for FTP response codes + */ + enum class FtpStatusCode : int { + /// Unknown status code + UNKNOWN, + /// Restart marker reply + RESTART_MARKER = 110, + /// Service ready in nnn minutes + SERVICE_READY_IN_MIN = 120, + /// Data connection already open; transfer starting + DATA_ALREADY_OPEN_START_TRANSFER = 125, + /// File status okay; about to open data connection + FILE_OK = 150, + /// Command okay + COMMAND_OK = 200, + /// Command not implemented, superfluous at this site + COMMAND_NOT_IMPLEMENTED_SUPERFLUOUS = 202, + /// System status, or system help reply + SYSTEM_STATUS = 211, + /// Directory status + DIR_STATUS = 212, + /// File status + FILE_STATUS = 213, + /// Help message + HELP_MESSAGE = 214, + /// NAME system type + NAME_SYSTEM_TYPE = 215, + /// Service ready for new user + SERVICE_READY_FOR_USER = 220, + /// Service closing control connection + SERVICE_CLOSING_CONTROL = 221, + /// Data connection open; no transfer in progress + DATA_OPEN_NO_TRANSFER = 225, + /// Closing data connection + CLOSING_DATA = 226, + /// Entering Passive Mode + ENTERING_PASSIVE = 227, + /// Entering Extended Passive Mode + ENTERING_EXTENDED_PASSIVE = 229, + /// User logged in, proceed + USER_LOG_IN_PROCEED = 230, + /// User logged in, authorized by security data exchange + USER_LOG_IN_AUTHORIZED = 232, + /// Security data exchange complete + SEC_DATA_EXCHANGE_COMPLETE = 234, + /// Security data exchange completed successfully + SEC_DATA_EXCHANGE_COMPLETE_SUCCESS = 235, + /// Requested file action okay, completed + REQ_FILE_OK_COMPLETE = 250, + /// PATHNAME created + PATHNAME_CREATED = 257, + /// User name okay, need password + USER_OK_NEED_PASSWORD = 331, + /// Need account for login + NEED_ACCOUNT = 332, + /// Requested security mechanism is ok + REQ_SEC_MECHANISM_OK = 334, + /// Security data is acceptable, more is required + SEC_IS_ACCEPTABLE = 335, + /// Username okay, need password. Challenge is ... + USER_OK_NEED_PASS_CHALLENGE = 336, + /// Requested file action pending further information + FILE_PENDING_ACTION = 350, + /// Service not available, closing control connection + SERVICE_NOT_AVAILABLE = 421, + /// Can't open data connection + CANT_OPEN_DATA_CONNECTION = 425, + /// Connection closed; transfer aborted + CONNECTION_CLOSED = 426, + /// Need some unavailable resource to process security + NEED_UNAVAILABLE_RESOURCE_TO_SEC = 431, + /// Requested file action not taken + REQ_FILE_ACTION_NOT_TAKEN = 450, + /// Requested action aborted: local error in processing + REQ_ACTION_ABORTED = 451, + /// Requested action not taken. Insufficient storage space in system + REQ_ACTION_NOT_TAKEN = 452, + /// Syntax error, command unrecognized + SYNTAX_ERROR_COMMAND_UNRECOGNIZED = 500, + /// Syntax error in parameters or arguments + SYNTAX_ERROR_PARAMETER_OR_ARGUMENT = 501, + /// Command not implemented + COMMAND_NOT_IMPLEMENTED = 502, + /// Bad sequence of commands + BAD_SEQUENCE_COMMANDS = 503, + /// Command not implemented for that parameter + COMMAND_NOT_IMPLEMENTED_FOR_PARAMETER = 504, + /// Network protocol not supported + NETWORK_PROTOCOL_NOT_SUPPORTED = 522, + /// Not logged in + NOT_LOGGED_IN = 530, + /// Need account for storing files + NEED_ACCOUNT_FOR_STORE_FILE = 532, + /// Command protection level denied for policy reasons + COMMAND_PROTECTION_DENIED = 533, + /// Request denied for policy reasons + REQUEST_DENIED = 534, + /// Failed security check (hash, sequence, etc) + FAILED_SEC_CHECK = 535, + /// Requested PROT level not supported by mechanism + REQ_PROT_LEVEL_NOT_SUPPORTED = 536, + /// Command protection level not supported by security mechanism + COMMAND_PROTECTION_LEVEL_NOT_SUPPORTED = 537, + /// Requested action not taken: File unavailable + FILE_UNAVAILABLE = 550, + /// Requested action aborted: page type unknown + PAGE_TYPE_UNKNOWN = 551, + /// Requested file action aborted: Exceeded storage allocation + EXCEED_STORAGE_ALLOCATION = 552, + /// Requested action not taken: File name not allowed + FILENAME_NOT_ALLOWED = 553, + /// Integrity protected reply + INTEGRITY_PROTECTED = 631, + /// Confidentiality and integrity protected reply + CONFIDENTIALITY_AND_INTEGRITY_PROTECTED = 632, + /// Confidentiality protected reply + CONFIDENTIALITY_PROTECTED = 633 + }; + + /** 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 + */ + FtpResponseLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet) + : FtpLayer(data, dataLen, prevLayer, packet){}; + + /** + * A constructor that creates layer with provided input values + * @param[in] code Status code + * @param[in] option Argument of the status code + */ + explicit FtpResponseLayer(const FtpStatusCode& code, + const std::string& option = "") + : FtpLayer(std::to_string(int(code)), option){}; + + /** + * Set the status code of response message + * @param[in] code Value to set status code + * @return True if the operation is successful, false otherwise + */ + bool setStatusCode(FtpStatusCode code); + + /** + * Get the status code of response message + * @return FtpStatusCode Value of the status code + */ + FtpStatusCode getStatusCode() const; + + /** + * Get the status code of response message as string + * @return std::string Value of the status code as string + */ + std::string getStatusCodeString() const; + + /** + * Set the argument of response message + * @param[in] value Value to set argument + * @return True if the operation is successful, false otherwise + */ + bool setStatusOption(const std::string& value); + + /** + * Get the argument of response message + * @param[in] removeEscapeCharacters Whether non-alphanumerical characters + * should be removed or not + * @return std::string Value of argument + */ + std::string getStatusOption(bool removeEscapeCharacters = true) const; + + /** + * Convert the status code to readable string + * @param[in] code Status code to convert + * @return std::string Returns the status info as readable string + */ + static std::string getStatusCodeAsString(FtpStatusCode code); + + // overridden methods + + /** + * @return Returns the protocol info as readable string + */ + std::string toString() const; +}; + +/** + * Class for representing the data of FTP Layer + */ +class FtpDataLayer : public PayloadLayer { + public: + /** 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 + */ + FtpDataLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : PayloadLayer(data, dataLen, prevLayer, packet) { + m_Protocol = FTP; + }; + + /** + * @return Returns the protocol info as readable string + */ + std::string toString() const; +}; } // namespace pcpp #endif /* PACKETPP_FTP_LAYER */ diff --git a/Packet++/header/GreLayer.h b/Packet++/header/GreLayer.h index 1fb262e6da..f19da75556 100644 --- a/Packet++/header/GreLayer.h +++ b/Packet++/header/GreLayer.h @@ -5,441 +5,477 @@ /// @file - /** * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ +namespace pcpp { - /** - * @struct gre_basic_header - * Represents GRE basic protocol header (common for GREv0 and GREv1) - */ +/** + * @struct gre_basic_header + * Represents GRE basic protocol header (common for GREv0 and GREv1) + */ #pragma pack(push, 1) - struct gre_basic_header - { +struct gre_basic_header { #if (BYTE_ORDER == LITTLE_ENDIAN) - /** Number of additional encapsulations which are permitted. 0 is the default value */ - uint8_t recursionControl:3, - /** Strict source routing bit (GRE v0 only) */ - strictSourceRouteBit:1, - /** Set if sequence number exists */ - sequenceNumBit:1, - /** Set if key exists */ - keyBit:1, - /** Set if routing exists (GRE v0 only) */ - routingBit:1, - /** Set if checksum exists (GRE v0 only) */ - checksumBit:1; + /** Number of additional encapsulations which are permitted. 0 is the default + * value */ + uint8_t recursionControl : 3, + /** Strict source routing bit (GRE v0 only) */ + strictSourceRouteBit : 1, + /** Set if sequence number exists */ + sequenceNumBit : 1, + /** Set if key exists */ + keyBit : 1, + /** Set if routing exists (GRE v0 only) */ + routingBit : 1, + /** Set if checksum exists (GRE v0 only) */ + checksumBit : 1; #else - /** Set if checksum exists (GRE v0 only) */ - uint8_t checksumBit:1, - /** Set if routing exists (GRE v0 only) */ - routingBit:1, - /** Set if key exists */ - keyBit:1, - /** Set if sequence number exists */ - sequenceNumBit:1, - /** Strict source routing bit (GRE v0 only) */ - strictSourceRouteBit:1, - /** Number of additional encapsulations which are permitted. 0 is the default value */ - recursionControl:3; + /** Set if checksum exists (GRE v0 only) */ + uint8_t checksumBit : 1, + /** Set if routing exists (GRE v0 only) */ + routingBit : 1, + /** Set if key exists */ + keyBit : 1, + /** Set if sequence number exists */ + sequenceNumBit : 1, + /** Strict source routing bit (GRE v0 only) */ + strictSourceRouteBit : 1, + /** Number of additional encapsulations which are permitted. 0 is the + default value */ + recursionControl : 3; #endif #if (BYTE_ORDER == LITTLE_ENDIAN) - /** GRE version - can be 0 or 1 */ - uint8_t version:3, - /** Reserved */ - flags:4, - /** Set if acknowledgment number is set (GRE v1 only) */ - ackSequenceNumBit:1; + /** GRE version - can be 0 or 1 */ + uint8_t version : 3, + /** Reserved */ + flags : 4, + /** Set if acknowledgment number is set (GRE v1 only) */ + ackSequenceNumBit : 1; #else - /** Set if acknowledgment number is set (GRE v1 only) */ - uint8_t ackSequenceNumBit:1, - /** Reserved */ - flags:4, - /** GRE version - can be 0 or 1 */ - version:3; + /** Set if acknowledgment number is set (GRE v1 only) */ + uint8_t ackSequenceNumBit : 1, + /** Reserved */ + flags : 4, + /** GRE version - can be 0 or 1 */ + version : 3; #endif - /** Protocol type of the next layer */ - uint16_t protocol; - }; + /** Protocol type of the next layer */ + uint16_t protocol; +}; #pragma pack(pop) - - /** - * @struct gre1_header - * Represents GREv1 protocol header - */ +/** + * @struct gre1_header + * Represents GREv1 protocol header + */ #pragma pack(push, 1) - struct gre1_header : gre_basic_header - { - /** Size of the payload not including the GRE header */ - uint16_t payloadLength; - /** Contains the Peer's Call ID for the session to which this packet belongs */ - uint16_t callID; - }; +struct gre1_header : gre_basic_header { + /** Size of the payload not including the GRE header */ + uint16_t payloadLength; + /** Contains the Peer's Call ID for the session to which this packet belongs + */ + uint16_t callID; +}; #pragma pack(pop) - - /** - * @struct ppp_pptp_header - * Represents PPP layer that comes after GREv1 as part of PPTP protocol - */ +/** + * @struct ppp_pptp_header + * Represents PPP layer that comes after GREv1 as part of PPTP protocol + */ #pragma pack(push, 1) - struct ppp_pptp_header - { - /** Broadcast address */ - uint8_t address; - /** Control byte */ - uint8_t control; - /** Protocol type of the next layer (see PPP_* macros at PPPoELayer.h) */ - uint16_t protocol; - }; +struct ppp_pptp_header { + /** Broadcast address */ + uint8_t address; + /** Control byte */ + uint8_t control; + /** Protocol type of the next layer (see PPP_* macros at PPPoELayer.h) */ + uint16_t protocol; +}; #pragma pack(pop) +/** + * @class GreLayer + * Abstract base class for GRE layers (GREv0Layer and GREv1Layer). Cannot be + * instantiated and contains common logic for derived classes + */ +class GreLayer : public Layer { + public: + virtual ~GreLayer() {} + + /** + * A static method that determines the GRE version of GRE layer raw data by + * looking at the gre_basic_header#version field + * @param[in] greData GRE layer raw data + * @param[in] greDataLen Size of raw data + * @return ::GREv0 or ::GREv1 values if raw data is GREv0 or GREv1 + * (accordingly) or ::UnknownProtocol otherwise + */ + static ProtocolType getGREVersion(uint8_t* greData, size_t greDataLen); + + /** + * Get sequence number value if field exists in layer + * @param[out] seqNumber The returned sequence number value if exists in + * layer. Else remain unchanged + * @return True if sequence number field exists in layer. In this case + * seqNumber will be filled with the value. Or false if sequence number field + * doesn't exist in layer + */ + bool getSequenceNumber(uint32_t& seqNumber) const; + + /** + * Set sequence number value. If field already exists + * (gre_basic_header#sequenceNumBit is set) then only the new value is set. If + * field doesn't exist it will be added to the layer, + * gre_basic_header#sequenceNumBit will be set and the new value will be set + * @param[in] seqNumber The sequence number value to set + * @return True if managed to set the value successfully, or false otherwise + * (if couldn't extend the layer) + */ + bool setSequenceNumber(uint32_t seqNumber); + + /** + * Unset sequence number and remove it from the layer + * @return True if managed to unset successfully or false (and error log) if + * sequence number wasn't set in the first place or if didn't manage to remove + * it from the layer + */ + bool unsetSequenceNumber(); + + // implement abstract methods + + /** + * Currently identifies the following next layers: + * IPv4Layer, IPv6Layer, VlanLayer, MplsLayer, PPP_PPTPLayer, EthLayer, + * EthDot3Layer Otherwise sets PayloadLayer + */ + void parseNextLayer(); + + /** + * @return Size of GRE header (may change if optional fields are added or + * removed) + */ + size_t getHeaderLen() const; + + OsiModelLayer getOsiModelLayer() const { return OsiModelNetworkLayer; } + + protected: + GreLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : Layer(data, dataLen, prevLayer, packet) {} + + GreLayer() {} + + enum GreField { + GreChecksumOrRouting = 0, + GreKey = 1, + GreSeq = 2, + GreAck = 3 + }; + + uint8_t* getFieldValue(GreField field, + bool returnOffsetEvenIfFieldMissing) const; + + void computeCalculateFieldsInner(); +}; - /** - * @class GreLayer - * Abstract base class for GRE layers (GREv0Layer and GREv1Layer). Cannot be instantiated and contains common logic for derived classes - */ - class GreLayer : public Layer - { - public: - - virtual ~GreLayer() {} - - /** - * A static method that determines the GRE version of GRE layer raw data by looking at the gre_basic_header#version - * field - * @param[in] greData GRE layer raw data - * @param[in] greDataLen Size of raw data - * @return ::GREv0 or ::GREv1 values if raw data is GREv0 or GREv1 (accordingly) or ::UnknownProtocol otherwise - */ - static ProtocolType getGREVersion(uint8_t* greData, size_t greDataLen); - - /** - * Get sequence number value if field exists in layer - * @param[out] seqNumber The returned sequence number value if exists in layer. Else remain unchanged - * @return True if sequence number field exists in layer. In this case seqNumber will be filled with the value. - * Or false if sequence number field doesn't exist in layer - */ - bool getSequenceNumber(uint32_t& seqNumber) const; - - /** - * Set sequence number value. If field already exists (gre_basic_header#sequenceNumBit is set) then only the new - * value is set. If field doesn't exist it will be added to the layer, gre_basic_header#sequenceNumBit will be set - * and the new value will be set - * @param[in] seqNumber The sequence number value to set - * @return True if managed to set the value successfully, or false otherwise (if couldn't extend the layer) - */ - bool setSequenceNumber(uint32_t seqNumber); - - /** - * Unset sequence number and remove it from the layer - * @return True if managed to unset successfully or false (and error log) if sequence number wasn't set in the first - * place or if didn't manage to remove it from the layer - */ - bool unsetSequenceNumber(); - - - // implement abstract methods - - /** - * Currently identifies the following next layers: - * IPv4Layer, IPv6Layer, VlanLayer, MplsLayer, PPP_PPTPLayer, EthLayer, EthDot3Layer - * Otherwise sets PayloadLayer - */ - void parseNextLayer(); - - /** - * @return Size of GRE header (may change if optional fields are added or removed) - */ - size_t getHeaderLen() const; - - OsiModelLayer getOsiModelLayer() const { return OsiModelNetworkLayer; } - - protected: - GreLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) { } - - GreLayer() {} - - enum GreField - { - GreChecksumOrRouting = 0, - GreKey = 1, - GreSeq = 2, - GreAck = 3 - }; - - uint8_t* getFieldValue(GreField field, bool returnOffsetEvenIfFieldMissing) const; - - void computeCalculateFieldsInner(); - }; - - - /** - * @class GREv0Layer - * Represents a GRE version 0 protocol. Limitation: currently this layer doesn't support GRE routing information parsing - * and editing. So if a GREv0 packet includes routing information it won't be parse correctly. I didn't add it because - * of lack of time, but if you need it please tell me and I'll add it - */ - class GREv0Layer : public GreLayer - { - public: - - /** 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 - */ - GREv0Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : GreLayer(data, dataLen, prevLayer, packet) { m_Protocol = GREv0; } - - /** - * A constructor that creates a new GREv0 header and allocates the data - */ - GREv0Layer(); - - virtual ~GREv0Layer() {} - - /** - * Get a pointer to the basic GRE header containing only non-optional fields. Notice this points directly to the data, - * so every change will change the actual packet data. Also please notice that changing the set bits - * (gre_basic_header#strictSourceRouteBit, gre_basic_header#sequenceNumBit, gre_basic_header#keyBit, gre_basic_header#routingBit, - * gre_basic_header#checksumBit, gre_basic_header#ackSequenceNumBit) without using the proper set or unset methods (such - * as setChecksum(), unsetChecksum(), etc.) may result to wrong calculation of header length and really weird bugs. - * Please avoid doing so - * @return A pointer to the gre_basic_header - */ - gre_basic_header* getGreHeader() const { return (gre_basic_header*)m_Data; } - - /** - * Get checksum value if field exists in layer - * @param[out] checksum The returned checksum value if exists in layer. Else remain unchanged - * @return True if checksum field exists in layer. In this case checksum parameter will be filled with the value. - * Or false if checksum field doesn't exist in layer - */ - bool getChecksum(uint16_t& checksum); - - /** - * Set checksum value. If checksum or offset fields already exist (gre_basic_header#checksumBit or gre_basic_header#routingBit are set) - * then only the new value is set. If both fields don't exist a new 4-byte value will be added to the layer, - * gre_basic_header#checksumBit will be set (gre_basic_header#routingBit will remain unset), the new checksum value - * will be set and offset value will be set to 0. The reason both fields are added is that GREv0 protocol states - * both of them or none of them should exist on packet (even if only one of the bits are set) - * @param[in] checksum The checksum value to set - * @return True if managed to set the value/s successfully, or false otherwise (if couldn't extend the layer) - */ - bool setChecksum(uint16_t checksum); - - /** - * Unset checksum and possibly remove it from the layer. It will be removed from the layer only if gre_basic_header#routingBit - * is not set as well. Otherwise checksum field will remain on packet with value of 0 - * @return True if managed to unset successfully or false (and error log) if checksum wasn't set in the first - * place or if didn't manage to remove it from the layer - */ - bool unsetChecksum(); - - /** - * Get offset value if field exists in layer. Notice there is no setOffset() method as GRE routing information isn't - * supported yet (see comment on class description) - * @param[out] offset The returned offset value if exists in layer. Else remain unchanged - * @return True if offset field exists in layer. In this case offset parameter will be filled with the value. - * Or false if offset field doesn't exist in layer - */ - bool getOffset(uint16_t& offset) const; - - /** - * Get key value if field exists in layer - * @param[out] key The returned key value if exists in layer. Else remain unchanged - * @return True if key field exists in layer. In this case key parameter will be filled with the value. - * Or false if key field doesn't exist in layer - */ - bool getKey(uint32_t& key) const; - - /** - * Set key value. If field already exists (gre_basic_header#keyBit is set) then only the new value is set. - * If field doesn't exist it will be added to the layer, gre_basic_header#keyBit will be set - * and the new value will be set - * @param[in] key The key value to set - * @return True if managed to set the value successfully, or false otherwise (if couldn't extend the layer) - */ - bool setKey(uint32_t key); - - /** - * Unset key and remove it from the layer - * @return True if managed to unset successfully or false (and error log) if key wasn't set in the first - * place or if didn't manage to remove it from the layer - */ - bool unsetKey(); - - /** - * A static method that validates the input data - * @param[in] data The pointer to the beginning of a byte stream of an GREv0 layer - * @param[in] dataLen The length of the byte stream - * @return True if the data is valid and can represent an GREv0 layer - */ - static inline bool isDataValid(const uint8_t* data, size_t dataLen) - { - return data && dataLen >= sizeof(gre_basic_header); - } - - // implement abstract methods - - - /** - * Calculate the following fields: - * - gre_basic_header#protocol - * - GRE checksum field (if exists in packet) - */ - void computeCalculateFields(); - - std::string toString() const; - - }; - - - /** - * @class GREv1Layer - * Represents a GRE version 1 protocol - */ - class GREv1Layer : public GreLayer - { - public: - - /** 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 - */ - GREv1Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : GreLayer(data, dataLen, prevLayer, packet) { m_Protocol = GREv1; } - - /** - * A constructor that creates a new GREv1 header and allocates the data - * @param[in] callID The call ID to set - */ - explicit GREv1Layer(uint16_t callID); - - virtual ~GREv1Layer() {} - - /** - * Get a pointer to the basic GREv1 header containing all non-optional fields. Notice this points directly to the data, so every change will change the actual - * packet data. Also please notice that changing the set bits (gre_basic_header#strictSourceRouteBit, gre_basic_header#sequenceNumBit, gre_basic_header#keyBit, - * gre_basic_header#routingBit, gre_basic_header#checksumBit, gre_basic_header#ackSequenceNumBit) without using the proper set or unset methods - * (such as setAcknowledgmentNum(), unsetSequenceNumber(), etc.) may result to wrong calculation of header length or illegal GREv1 packet and - * to some really weird bugs. Please avoid doing so - * @return A pointer to the gre1_header - */ - gre1_header* getGreHeader() const { return (gre1_header*)m_Data; } - - /** - * Get acknowledgment (ack) number value if field exists in layer - * @param[out] ackNum The returned ack number value if exists in layer. Else remain unchanged - * @return True if ack number field exists in layer. In this case ackNum will be filled with the value. - * Or false if ack number field doesn't exist in layer - */ - bool getAcknowledgmentNum(uint32_t& ackNum) const; - - /** - * Set acknowledgment (ack) number value. If field already exists (gre_basic_header#ackSequenceNumBit is set) - * then only the new value is set. If field doesn't exist it will be added to the layer, - * gre_basic_header#ackSequenceNumBit will be set and the new value will be set - * @param[in] ackNum The ack number value to set - * @return True if managed to set the value successfully, or false otherwise (if couldn't extend the layer) - */ - bool setAcknowledgmentNum(uint32_t ackNum); - - /** - * Unset acknowledgment (ack) number and remove it from the layer - * @return True if managed to unset successfully or false (and error log) if ack number wasn't set in the first - * place or if didn't manage to remove it from the layer - */ - bool unsetAcknowledgmentNum(); - - /** - * A static method that validates the input data - * @param[in] data The pointer to the beginning of a byte stream of an GREv1 layer - * @param[in] dataLen The length of the byte stream - * @return True if the data is valid and can represent an GREv1 layer - */ - static inline bool isDataValid(const uint8_t* data, size_t dataLen) - { - return data && dataLen >= sizeof(gre1_header); - } - - // implement abstract methods - - /** - * Calculate the following fields: - * - gre1_header#payloadLength - * - gre_basic_header#protocol - */ - void computeCalculateFields(); - - std::string toString() const; - - }; - - - /** - * @class PPP_PPTPLayer - * Represent a PPP (point-to-point) protocol header that comes after GREv1 header, as part of PPTP - Point-to-Point Tunneling Protocol - */ - class PPP_PPTPLayer : public Layer - { - public: - /** - * A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data (will be casted to @ref ppp_pptp_header) - * @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 - */ - PPP_PPTPLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) { m_Protocol = PPP_PPTP; } - - /** - * A constructor that allocates a new PPP-PPTP header - * @param[in] address Address field - * @param[in] control Control field - */ - PPP_PPTPLayer(uint8_t address, uint8_t control); - - ~PPP_PPTPLayer() {} - - /** - * Get a pointer to the PPP-PPTP header. Notice this points directly to the data, so every change will change the actual packet data - * @return A pointer to the @ref ppp_pptp_header - */ - ppp_pptp_header* getPPP_PPTPHeader() const { return (ppp_pptp_header*)m_Data; } - - - // implement abstract methods - - /** - * Currently identifies the following next layers: IPv4Layer, IPv6Layer. Otherwise sets PayloadLayer - */ - void parseNextLayer(); - - /** - * @return The size of @ref ppp_pptp_header - */ - size_t getHeaderLen() const { return sizeof(ppp_pptp_header); } - - /** - * Calculate the following fields: - * - ppp_pptp_header#protocol - */ - void computeCalculateFields(); - - std::string toString() const { return "PPP for PPTP Layer"; } - - OsiModelLayer getOsiModelLayer() const { return OsiModelSesionLayer; } - - }; +/** + * @class GREv0Layer + * Represents a GRE version 0 protocol. Limitation: currently this layer doesn't + * support GRE routing information parsing and editing. So if a GREv0 packet + * includes routing information it won't be parse correctly. I didn't add it + * because of lack of time, but if you need it please tell me and I'll add it + */ +class GREv0Layer : public GreLayer { + public: + /** 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 + */ + GREv0Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : GreLayer(data, dataLen, prevLayer, packet) { + m_Protocol = GREv0; + } + + /** + * A constructor that creates a new GREv0 header and allocates the data + */ + GREv0Layer(); + + virtual ~GREv0Layer() {} + + /** + * Get a pointer to the basic GRE header containing only non-optional fields. + * Notice this points directly to the data, so every change will change the + * actual packet data. Also please notice that changing the set bits + * (gre_basic_header#strictSourceRouteBit, gre_basic_header#sequenceNumBit, + * gre_basic_header#keyBit, gre_basic_header#routingBit, + * gre_basic_header#checksumBit, gre_basic_header#ackSequenceNumBit) without + * using the proper set or unset methods (such as setChecksum(), + * unsetChecksum(), etc.) may result to wrong calculation of header length and + * really weird bugs. Please avoid doing so + * @return A pointer to the gre_basic_header + */ + gre_basic_header* getGreHeader() const { return (gre_basic_header*)m_Data; } + + /** + * Get checksum value if field exists in layer + * @param[out] checksum The returned checksum value if exists in layer. Else + * remain unchanged + * @return True if checksum field exists in layer. In this case checksum + * parameter will be filled with the value. Or false if checksum field doesn't + * exist in layer + */ + bool getChecksum(uint16_t& checksum); + + /** + * Set checksum value. If checksum or offset fields already exist + * (gre_basic_header#checksumBit or gre_basic_header#routingBit are set) then + * only the new value is set. If both fields don't exist a new 4-byte value + * will be added to the layer, gre_basic_header#checksumBit will be set + * (gre_basic_header#routingBit will remain unset), the new checksum value + * will be set and offset value will be set to 0. The reason both fields are + * added is that GREv0 protocol states both of them or none of them should + * exist on packet (even if only one of the bits are set) + * @param[in] checksum The checksum value to set + * @return True if managed to set the value/s successfully, or false otherwise + * (if couldn't extend the layer) + */ + bool setChecksum(uint16_t checksum); + + /** + * Unset checksum and possibly remove it from the layer. It will be removed + * from the layer only if gre_basic_header#routingBit is not set as well. + * Otherwise checksum field will remain on packet with value of 0 + * @return True if managed to unset successfully or false (and error log) if + * checksum wasn't set in the first place or if didn't manage to remove it + * from the layer + */ + bool unsetChecksum(); + + /** + * Get offset value if field exists in layer. Notice there is no setOffset() + * method as GRE routing information isn't supported yet (see comment on class + * description) + * @param[out] offset The returned offset value if exists in layer. Else + * remain unchanged + * @return True if offset field exists in layer. In this case offset parameter + * will be filled with the value. Or false if offset field doesn't exist in + * layer + */ + bool getOffset(uint16_t& offset) const; + + /** + * Get key value if field exists in layer + * @param[out] key The returned key value if exists in layer. Else remain + * unchanged + * @return True if key field exists in layer. In this case key parameter will + * be filled with the value. Or false if key field doesn't exist in layer + */ + bool getKey(uint32_t& key) const; + + /** + * Set key value. If field already exists (gre_basic_header#keyBit is set) + * then only the new value is set. If field doesn't exist it will be added to + * the layer, gre_basic_header#keyBit will be set and the new value will be + * set + * @param[in] key The key value to set + * @return True if managed to set the value successfully, or false otherwise + * (if couldn't extend the layer) + */ + bool setKey(uint32_t key); + + /** + * Unset key and remove it from the layer + * @return True if managed to unset successfully or false (and error log) if + * key wasn't set in the first place or if didn't manage to remove it from the + * layer + */ + bool unsetKey(); + + /** + * A static method that validates the input data + * @param[in] data The pointer to the beginning of a byte stream of an GREv0 + * layer + * @param[in] dataLen The length of the byte stream + * @return True if the data is valid and can represent an GREv0 layer + */ + static inline bool isDataValid(const uint8_t* data, size_t dataLen) { + return data && dataLen >= sizeof(gre_basic_header); + } + + // implement abstract methods + + /** + * Calculate the following fields: + * - gre_basic_header#protocol + * - GRE checksum field (if exists in packet) + */ + void computeCalculateFields(); + + std::string toString() const; +}; + +/** + * @class GREv1Layer + * Represents a GRE version 1 protocol + */ +class GREv1Layer : public GreLayer { + public: + /** 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 + */ + GREv1Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : GreLayer(data, dataLen, prevLayer, packet) { + m_Protocol = GREv1; + } + + /** + * A constructor that creates a new GREv1 header and allocates the data + * @param[in] callID The call ID to set + */ + explicit GREv1Layer(uint16_t callID); + + virtual ~GREv1Layer() {} + + /** + * Get a pointer to the basic GREv1 header containing all non-optional fields. + * Notice this points directly to the data, so every change will change the + * actual packet data. Also please notice that changing the set bits + * (gre_basic_header#strictSourceRouteBit, gre_basic_header#sequenceNumBit, + * gre_basic_header#keyBit, gre_basic_header#routingBit, + * gre_basic_header#checksumBit, gre_basic_header#ackSequenceNumBit) without + * using the proper set or unset methods (such as setAcknowledgmentNum(), + * unsetSequenceNumber(), etc.) may result to wrong calculation of header + * length or illegal GREv1 packet and to some really weird bugs. Please avoid + * doing so + * @return A pointer to the gre1_header + */ + gre1_header* getGreHeader() const { return (gre1_header*)m_Data; } + + /** + * Get acknowledgment (ack) number value if field exists in layer + * @param[out] ackNum The returned ack number value if exists in layer. Else + * remain unchanged + * @return True if ack number field exists in layer. In this case ackNum will + * be filled with the value. Or false if ack number field doesn't exist in + * layer + */ + bool getAcknowledgmentNum(uint32_t& ackNum) const; + + /** + * Set acknowledgment (ack) number value. If field already exists + * (gre_basic_header#ackSequenceNumBit is set) then only the new value is set. + * If field doesn't exist it will be added to the layer, + * gre_basic_header#ackSequenceNumBit will be set and the new value will be + * set + * @param[in] ackNum The ack number value to set + * @return True if managed to set the value successfully, or false otherwise + * (if couldn't extend the layer) + */ + bool setAcknowledgmentNum(uint32_t ackNum); + + /** + * Unset acknowledgment (ack) number and remove it from the layer + * @return True if managed to unset successfully or false (and error log) if + * ack number wasn't set in the first place or if didn't manage to remove it + * from the layer + */ + bool unsetAcknowledgmentNum(); + + /** + * A static method that validates the input data + * @param[in] data The pointer to the beginning of a byte stream of an GREv1 + * layer + * @param[in] dataLen The length of the byte stream + * @return True if the data is valid and can represent an GREv1 layer + */ + static inline bool isDataValid(const uint8_t* data, size_t dataLen) { + return data && dataLen >= sizeof(gre1_header); + } + + // implement abstract methods + + /** + * Calculate the following fields: + * - gre1_header#payloadLength + * - gre_basic_header#protocol + */ + void computeCalculateFields(); + + std::string toString() const; +}; + +/** + * @class PPP_PPTPLayer + * Represent a PPP (point-to-point) protocol header that comes after GREv1 + * header, as part of PPTP - Point-to-Point Tunneling Protocol + */ +class PPP_PPTPLayer : public Layer { + public: + /** + * A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data (will be casted to @ref + * ppp_pptp_header) + * @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 + */ + PPP_PPTPLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : Layer(data, dataLen, prevLayer, packet) { + m_Protocol = PPP_PPTP; + } + + /** + * A constructor that allocates a new PPP-PPTP header + * @param[in] address Address field + * @param[in] control Control field + */ + PPP_PPTPLayer(uint8_t address, uint8_t control); + + ~PPP_PPTPLayer() {} + + /** + * Get a pointer to the PPP-PPTP header. Notice this points directly to the + * data, so every change will change the actual packet data + * @return A pointer to the @ref ppp_pptp_header + */ + ppp_pptp_header* getPPP_PPTPHeader() const { + return (ppp_pptp_header*)m_Data; + } + + // implement abstract methods + + /** + * Currently identifies the following next layers: IPv4Layer, IPv6Layer. + * Otherwise sets PayloadLayer + */ + void parseNextLayer(); + + /** + * @return The size of @ref ppp_pptp_header + */ + size_t getHeaderLen() const { return sizeof(ppp_pptp_header); } + + /** + * Calculate the following fields: + * - ppp_pptp_header#protocol + */ + void computeCalculateFields(); + + std::string toString() const { return "PPP for PPTP Layer"; } + + OsiModelLayer getOsiModelLayer() const { return OsiModelSesionLayer; } +}; } // namespace pcpp diff --git a/Packet++/header/GtpLayer.h b/Packet++/header/GtpLayer.h index 39f1fc188c..6cf20eefee 100644 --- a/Packet++/header/GtpLayer.h +++ b/Packet++/header/GtpLayer.h @@ -5,458 +5,498 @@ /// @file - /** * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ +namespace pcpp { #pragma pack(push, 1) - /** - * @struct gtpv1_header - * GTP v1 common message header - */ - struct gtpv1_header - { +/** + * @struct gtpv1_header + * GTP v1 common message header + */ +struct gtpv1_header { #if (BYTE_ORDER == LITTLE_ENDIAN) - /** A 1-bit value that states whether there is a N-PDU number optional field */ - uint8_t npduNumberFlag:1, - /** A 1-bit value that states whether there is a Sequence Number optional field */ - sequenceNumberFlag:1, - /** A 1-bit value that states whether there is an extension header optional field */ - extensionHeaderFlag:1, - /** Reserved bit */ - reserved:1, - /** A 1-bit value that differentiates GTP (value 1) from GTP' (value 0) */ - protocolType:1, - /** GTP version */ - version:3; + /** A 1-bit value that states whether there is a N-PDU number optional field + */ + uint8_t npduNumberFlag : 1, + /** A 1-bit value that states whether there is a Sequence Number optional + field */ + sequenceNumberFlag : 1, + /** A 1-bit value that states whether there is an extension header + optional field */ + extensionHeaderFlag : 1, + /** Reserved bit */ + reserved : 1, + /** A 1-bit value that differentiates GTP (value 1) from GTP' (value 0) */ + protocolType : 1, + /** GTP version */ + version : 3; #else - /** GTP version */ - uint8_t version:3, - /** A 1-bit value that differentiates GTP (value 1) from GTP' (value 0) */ - protocolType:1, - /** Reserved bit */ - reserved:1, - /** A 1-bit value that states whether there is an extension header optional field */ - extensionHeaderFlag:1, - /** A 1-bit value that states whether there is a Sequence Number optional field */ - sequenceNumberFlag:1, - /** A 1-bit value that states whether there is a N-PDU number optional field */ - npduNumberFlag:1; + /** GTP version */ + uint8_t version : 3, + /** A 1-bit value that differentiates GTP (value 1) from GTP' (value 0) */ + protocolType : 1, + /** Reserved bit */ + reserved : 1, + /** A 1-bit value that states whether there is an extension header + optional field */ + extensionHeaderFlag : 1, + /** A 1-bit value that states whether there is a Sequence Number optional + field */ + sequenceNumberFlag : 1, + /** A 1-bit value that states whether there is a N-PDU number optional + field */ + npduNumberFlag : 1; #endif - /** An 8-bit field that indicates the type of GTP message */ - uint8_t messageType; + /** An 8-bit field that indicates the type of GTP message */ + uint8_t messageType; - /** A 16-bit field that indicates the length of the payload in bytes (rest of the packet following the mandatory 8-byte GTP header). Includes the optional fields */ - uint16_t messageLength; + /** A 16-bit field that indicates the length of the payload in bytes (rest of + * the packet following the mandatory 8-byte GTP header). Includes the + * optional fields */ + uint16_t messageLength; - /** Tunnel endpoint identifier - A 32-bit(4-octet) field used to multiplex different connections in the same GTP tunnel */ - uint32_t teid; - }; + /** Tunnel endpoint identifier - A 32-bit(4-octet) field used to multiplex + * different connections in the same GTP tunnel */ + uint32_t teid; +}; #pragma pack(pop) - /** - * An enum representing the possible GTP v1 message types. - * All of the message types except for #GtpV1_GPDU are considered GTP-C messages. #GtpV1_GPDU is considered a GTP-U message - */ - enum GtpV1MessageType - { - /** GTPv1 Message Type Unknown */ - GtpV1_MessageTypeUnknown = 0, - /** Echo Request */ - GtpV1_EchoRequest = 1, - /** Echo Response */ - GtpV1_EchoResponse = 2, - /** Version Not Supported */ - GtpV1_VersionNotSupported = 3, - /** Node Alive Request */ - GtpV1_NodeAliveRequest = 4, - /** Node Alive Response */ - GtpV1_NodeAliveResponse = 5, - /** Redirection Request */ - GtpV1_RedirectionRequest = 6, - /** Create PDP Context Request */ - GtpV1_CreatePDPContextRequest = 7, - /** Create PDP Context Response */ - GtpV1_CreatePDPContextResponse = 16, - /** Update PDP Context Request */ - GtpV1_UpdatePDPContextRequest = 17, - /** Update PDP Context Response */ - GtpV1_UpdatePDPContextResponse = 18, - /** Delete PDP Context Request */ - GtpV1_DeletePDPContextRequest = 19, - /** Delete PDP Context Response */ - GtpV1_DeletePDPContextResponse = 20, - /** Initiate PDP Context Activation Request */ - GtpV1_InitiatePDPContextActivationRequest = 22, - /** Initiate PDP Context Activation Response */ - GtpV1_InitiatePDPContextActivationResponse = 23, - /** Error Indication */ - GtpV1_ErrorIndication = 26, - /** PDU Notification Request */ - GtpV1_PDUNotificationRequest = 27, - /** PDU Notification Response */ - GtpV1_PDUNotificationResponse = 28, - /** PDU Notification Reject Request */ - GtpV1_PDUNotificationRejectRequest = 29, - /** PDU Notification Reject Response */ - GtpV1_PDUNotificationRejectResponse = 30, - /** Supported Extensions Header Notification */ - GtpV1_SupportedExtensionsHeaderNotification = 31, - /** Send Routing for GPRS Request */ - GtpV1_SendRoutingforGPRSRequest = 32, - /** Send Routing for GPRS Response */ - GtpV1_SendRoutingforGPRSResponse = 33, - /** Failure Report Request */ - GtpV1_FailureReportRequest = 34, - /** Failure Report Response */ - GtpV1_FailureReportResponse = 35, - /** Note MS Present Request */ - GtpV1_NoteMSPresentRequest = 36, - /** Note MS Present Response */ - GtpV1_NoteMSPresentResponse = 37, - /** Identification Request */ - GtpV1_IdentificationRequest = 38, - /** Identification Response */ - GtpV1_IdentificationResponse = 39, - /** SGSN Context Request */ - GtpV1_SGSNContextRequest = 50, - /** SGSN Context Response */ - GtpV1_SGSNContextResponse = 51, - /** SGSN Context Acknowledge */ - GtpV1_SGSNContextAcknowledge = 52, - /** Forward Relocation Request */ - GtpV1_ForwardRelocationRequest = 53, - /** Forward Relocation Response */ - GtpV1_ForwardRelocationResponse = 54, - /** Forward Relocation Complete */ - GtpV1_ForwardRelocationComplete = 55, - /** Relocation Cancel Request */ - GtpV1_RelocationCancelRequest = 56, - /** Relocation Cancel Response */ - GtpV1_RelocationCancelResponse = 57, - /** Forward SRNS Context */ - GtpV1_ForwardSRNSContext = 58, - /** Forward Relocation Complete Acknowledge */ - GtpV1_ForwardRelocationCompleteAcknowledge = 59, - /** Forward SRNS Context Acknowledge */ - GtpV1_ForwardSRNSContextAcknowledge = 60, - /** UE Registration Request */ - GtpV1_UERegistrationRequest = 61, - /** UE Registration Response */ - GtpV1_UERegistrationResponse = 62, - /** RAN Information Relay */ - GtpV1_RANInformationRelay = 70, - /** MBMS Notification Request */ - GtpV1_MBMSNotificationRequest = 96, - /** MBMS Notification Response */ - GtpV1_MBMSNotificationResponse = 97, - /** MBMS Notification Reject Request */ - GtpV1_MBMSNotificationRejectRequest = 98, - /** MBMS Notification Reject Response */ - GtpV1_MBMSNotificationRejectResponse = 99, - /** Create MBMS Notification Request */ - GtpV1_CreateMBMSNotificationRequest = 100, - /** Create MBMS Notification Response */ - GtpV1_CreateMBMSNotificationResponse = 101, - /** Update MBMS Notification Request */ - GtpV1_UpdateMBMSNotificationRequest = 102, - /** Update MBMS Notification Response */ - GtpV1_UpdateMBMSNotificationResponse = 103, - /** Delete MBMS Notification Request */ - GtpV1_DeleteMBMSNotificationRequest = 104, - /** Delete MBMS Notification Response */ - GtpV1_DeleteMBMSNotificationResponse = 105, - /** MBMS Registration Request */ - GtpV1_MBMSRegistrationRequest = 112, - /** MBMS Registration Response */ - GtpV1_MBMSRegistrationResponse = 113, - /** MBMS De-Registration Request */ - GtpV1_MBMSDeRegistrationRequest = 114, - /** MBMS De-Registration Response */ - GtpV1_MBMSDeRegistrationResponse = 115, - /** MBMS Session Start Request */ - GtpV1_MBMSSessionStartRequest = 116, - /** MBMS Session Start Response */ - GtpV1_MBMSSessionStartResponse = 117, - /** MBMS Session Stop Request */ - GtpV1_MBMSSessionStopRequest = 118, - /** MBMS Session Stop Response */ - GtpV1_MBMSSessionStopResponse = 119, - /** MBMS Session Update Request */ - GtpV1_MBMSSessionUpdateRequest = 120, - /** MBMS Session Update Response */ - GtpV1_MBMSSessionUpdateResponse = 121, - /** MS Info Change Request */ - GtpV1_MSInfoChangeRequest = 128, - /** MS Info Change Response */ - GtpV1_MSInfoChangeResponse = 129, - /** Data Record Transfer Request */ - GtpV1_DataRecordTransferRequest = 240, - /** Data Record Transfer Response */ - GtpV1_DataRecordTransferResponse = 241, - /** End Marker */ - GtpV1_EndMarker = 254, - /** G-PDU */ - GtpV1_GPDU = 255 - }; - - - /** - * @class GtpV1Layer - * A class representing the [GTP v1](https://en.wikipedia.org/wiki/GPRS_Tunnelling_Protocol) protocol. - */ - class GtpV1Layer : public Layer - { - private: - struct gtpv1_header_extra - { - uint16_t sequenceNumber; - uint8_t npduNumber; - uint8_t nextExtensionHeader; - }; - - gtpv1_header_extra* getHeaderExtra() const; - - void init(GtpV1MessageType messageType, uint32_t teid, bool setSeqNum, uint16_t seqNum, bool setNpduNum, uint8_t npduNum); - - public: - - /** - * @class GtpExtension - * A class that represents [GTP header extensions](https://en.wikipedia.org/wiki/GPRS_Tunnelling_Protocol) - */ - class GtpExtension - { - friend class GtpV1Layer; - - private: - uint8_t* m_Data; - size_t m_DataLen; - uint8_t m_ExtType; - - GtpExtension(uint8_t* data, size_t dataLen, uint8_t type); - - void setNextHeaderType(uint8_t nextHeaderType); - - static GtpExtension createGtpExtension(uint8_t* data, size_t dataLen, uint8_t extType, uint16_t content); - - public: - - /** - * An empty c'tor that creates an empty object, meaning one that isNull() returns "true") - */ - GtpExtension(); - - /** - * A copy c'tor for this class - * @param[in] other The GTP extension to copy from - */ - GtpExtension(const GtpExtension& other); - - /** - * An assignment operator for this class - * @param[in] other The extension to assign from - * @return A reference to the assignee - */ - GtpExtension& operator=(const GtpExtension& other); - - /** - * @return Instances of this class may be initialized as empty, meaning they don't contain any data. In - * these cases this method returns true - */ - bool isNull() const; - - /** - * @return The extension type. If the object is empty a value of zero is returned - */ - uint8_t getExtensionType() const; - - /** - * @return The total length of the extension including the length and next extension type fields. - * If the object is empty a value of zero is returned - */ - size_t getTotalLength() const; - - /** - * @return The length of the extension's content, excluding the extension length and next extension type fields. - * If the object is empty a value of zero is returned - */ - size_t getContentLength() const; - - /** - * @return A byte array that includes the extension's content. The length of this array can be determined by - * getContentLength(). If the object is empty a null value is returned - */ - uint8_t* getContent() const; - - /** - * @return The extension type of the next header. If there are no more header extensions or if this object is empty - * a value of zero is returned - */ - uint8_t getNextExtensionHeaderType() const; - - /** - * @return An instance of this class representing the next extension header, if exists in the message. If there are - * no more header extensions or if this object is empty an empty instance of GtpExtension is returned, meaning - * one that GtpExtension#isNull() returns "true" - */ - GtpExtension getNextExtension() const; - }; // GtpExtension - - virtual ~GtpV1Layer() {} - - /** 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 - */ - GtpV1Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) { m_Protocol = GTPv1; } - - /** - * A constructor that creates a new GTPv1 layer and sets the message type and the TEID value - * @param[in] messageType The GTPv1 message type to be set in the newly created layer - * @param[in] teid The TEID value to be set in the newly created layer - */ - GtpV1Layer(GtpV1MessageType messageType, uint32_t teid); - - /** - * A constructor that creates a new GTPv1 layer and sets various parameters - * @param[in] messageType The GTPv1 message type to be set in the newly created layer - * @param[in] teid The TEID value to be set in the newly created layer - * @param[in] setSeqNum A flag indicating whether to set a sequence number. If set to "false" then the parameter "seqNum" will be ignored - * @param[in] seqNum The sequence number to be set in the newly created later. If "setSeqNum" is set to false this parameter will be ignored - * @param[in] setNpduNum A flag indicating whether to set the N-PDU number. If set to "false" then the parameter "npduNum" will be ignored - * @param[in] npduNum The N-PDU number to be set in the newly created later. If "setNpduNum" is set to false this parameter will be ignored - */ - GtpV1Layer(GtpV1MessageType messageType, uint32_t teid, bool setSeqNum, uint16_t seqNum, bool setNpduNum, uint8_t npduNum); - - /** - * A static method that takes a byte array and detects whether it is a GTP v1 message - * @param[in] data A byte array - * @param[in] dataSize The byte array size (in bytes) - * @return True if the data is identified as GTP v1 message (GTP-C or GTP-U) - */ - static bool isGTPv1(const uint8_t* data, size_t dataSize); - - /** - * @return The GTP v1 common header structure. Notice this points directly to the data, so every change will change the actual packet data - */ - gtpv1_header* getHeader() const { return (gtpv1_header*)m_Data; } - - /** - * Get the sequence number if exists on the message (sequence number is an optional field in GTP messages) - * @param[out] seqNumber Set with the sequence number value if exists in the layer. Otherwise remains unchanged - * @return True if the sequence number field exists in layer, in which case seqNumber is set with the value. - * Or false otherwise - */ - bool getSequenceNumber(uint16_t& seqNumber) const; - - /** - * Set a sequence number - * @param[in] seqNumber The sequence number to set - * @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); - - /** - * Get the N-PDU number if exists on the message (N-PDU number is an optional field in GTP messages) - * @param[out] npduNum Set with the N-PDU number value if exists in the layer. Otherwise remains unchanged - * @return True if the N-PDU number field exists in layer, in which case npduNum is set with the value. - * Or false otherwise - */ - bool getNpduNumber(uint8_t& npduNum) const; - - /** - * Set an N-PDU number - * @param[in] npduNum The N-PDU number to set - * @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); - - /** - * Get the type of the next header extension if exists on the message (extensions are optional in GTP messages) - * @param[out] nextExtType Set with the next header extension type if exists in layer. Otherwise remains unchanged - * @return True if the message contains header extensions, in which case nextExtType is set to the next - * header extension type. If there are no header extensions false is returned and nextExtType remains unchanged - */ - bool getNextExtensionHeaderType(uint8_t& nextExtType) const; - - /** - * @return An object that represents the next extension header, if exists in the message. If there are no extensions - * an empty object is returned, meaning an object which GtpExtension#isNull() returns "true" - */ - GtpExtension getNextExtension() const; - - /** - * Add a GTPv1 header extension. It is assumed that the extension is 4 bytes in length and its content is 2 bytes in length. - * If you need a different content size please reach out to me. This method takes care of extending the layer to make room for - * the new extension and also sets the relevant flags and fields - * @param[in] extensionType The type of the new extension - * @param[in] extensionContent A 2-byte long content - * @return An object representing the newly added extension. If there was an error adding the extension a null object will be - * returned (meaning GtpExtension#isNull() will return "true") and a corresponding error message will be written to log - */ - GtpExtension addExtension(uint8_t extensionType, uint16_t extensionContent); - - /** - * @return The message type of this GTP packet - */ - GtpV1MessageType getMessageType() const; - - /** - * @return A string representation of the packet's message type - */ - std::string getMessageTypeAsString() const; - - /** - * @return True if this is a GTP-U message, false otherwise - */ - bool isGTPUMessage() const; - - /** - * @return True if this is a GTP-C message, false otherwise - */ - bool isGTPCMessage() const; - - /** - * 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 - */ - static bool isGTPv1Port(uint16_t port) { return port == 2152 /* GTP-U */ || port == 2123 /* GTP-C */; } - - - // implement abstract methods - - /** - * Identifies the following next layers for GTP-U packets: IPv4Layer, IPv6Layer. Otherwise sets PayloadLayer - */ - void parseNextLayer(); - - /** - * @return The size of the GTP header. For GTP-C packets the size is determined by the value of - * gtpv1_header#messageLength and for GTP-U the size only includes the GTP header itself (meaning - * the size of gtpv1_header plus the size of the optional fields such as sequence number, N-PDU - * or extensions if exist) - */ - size_t getHeaderLen() const; - - /** - * Calculate the following fields: - * - gtpv1_header#messageLength - */ - void computeCalculateFields(); - - std::string toString() const; - - OsiModelLayer getOsiModelLayer() const { return OsiModelTransportLayer; } - }; -} - -#endif //PACKETPP_GTP_LAYER +/** + * An enum representing the possible GTP v1 message types. + * All of the message types except for #GtpV1_GPDU are considered GTP-C + * messages. #GtpV1_GPDU is considered a GTP-U message + */ +enum GtpV1MessageType { + /** GTPv1 Message Type Unknown */ + GtpV1_MessageTypeUnknown = 0, + /** Echo Request */ + GtpV1_EchoRequest = 1, + /** Echo Response */ + GtpV1_EchoResponse = 2, + /** Version Not Supported */ + GtpV1_VersionNotSupported = 3, + /** Node Alive Request */ + GtpV1_NodeAliveRequest = 4, + /** Node Alive Response */ + GtpV1_NodeAliveResponse = 5, + /** Redirection Request */ + GtpV1_RedirectionRequest = 6, + /** Create PDP Context Request */ + GtpV1_CreatePDPContextRequest = 7, + /** Create PDP Context Response */ + GtpV1_CreatePDPContextResponse = 16, + /** Update PDP Context Request */ + GtpV1_UpdatePDPContextRequest = 17, + /** Update PDP Context Response */ + GtpV1_UpdatePDPContextResponse = 18, + /** Delete PDP Context Request */ + GtpV1_DeletePDPContextRequest = 19, + /** Delete PDP Context Response */ + GtpV1_DeletePDPContextResponse = 20, + /** Initiate PDP Context Activation Request */ + GtpV1_InitiatePDPContextActivationRequest = 22, + /** Initiate PDP Context Activation Response */ + GtpV1_InitiatePDPContextActivationResponse = 23, + /** Error Indication */ + GtpV1_ErrorIndication = 26, + /** PDU Notification Request */ + GtpV1_PDUNotificationRequest = 27, + /** PDU Notification Response */ + GtpV1_PDUNotificationResponse = 28, + /** PDU Notification Reject Request */ + GtpV1_PDUNotificationRejectRequest = 29, + /** PDU Notification Reject Response */ + GtpV1_PDUNotificationRejectResponse = 30, + /** Supported Extensions Header Notification */ + GtpV1_SupportedExtensionsHeaderNotification = 31, + /** Send Routing for GPRS Request */ + GtpV1_SendRoutingforGPRSRequest = 32, + /** Send Routing for GPRS Response */ + GtpV1_SendRoutingforGPRSResponse = 33, + /** Failure Report Request */ + GtpV1_FailureReportRequest = 34, + /** Failure Report Response */ + GtpV1_FailureReportResponse = 35, + /** Note MS Present Request */ + GtpV1_NoteMSPresentRequest = 36, + /** Note MS Present Response */ + GtpV1_NoteMSPresentResponse = 37, + /** Identification Request */ + GtpV1_IdentificationRequest = 38, + /** Identification Response */ + GtpV1_IdentificationResponse = 39, + /** SGSN Context Request */ + GtpV1_SGSNContextRequest = 50, + /** SGSN Context Response */ + GtpV1_SGSNContextResponse = 51, + /** SGSN Context Acknowledge */ + GtpV1_SGSNContextAcknowledge = 52, + /** Forward Relocation Request */ + GtpV1_ForwardRelocationRequest = 53, + /** Forward Relocation Response */ + GtpV1_ForwardRelocationResponse = 54, + /** Forward Relocation Complete */ + GtpV1_ForwardRelocationComplete = 55, + /** Relocation Cancel Request */ + GtpV1_RelocationCancelRequest = 56, + /** Relocation Cancel Response */ + GtpV1_RelocationCancelResponse = 57, + /** Forward SRNS Context */ + GtpV1_ForwardSRNSContext = 58, + /** Forward Relocation Complete Acknowledge */ + GtpV1_ForwardRelocationCompleteAcknowledge = 59, + /** Forward SRNS Context Acknowledge */ + GtpV1_ForwardSRNSContextAcknowledge = 60, + /** UE Registration Request */ + GtpV1_UERegistrationRequest = 61, + /** UE Registration Response */ + GtpV1_UERegistrationResponse = 62, + /** RAN Information Relay */ + GtpV1_RANInformationRelay = 70, + /** MBMS Notification Request */ + GtpV1_MBMSNotificationRequest = 96, + /** MBMS Notification Response */ + GtpV1_MBMSNotificationResponse = 97, + /** MBMS Notification Reject Request */ + GtpV1_MBMSNotificationRejectRequest = 98, + /** MBMS Notification Reject Response */ + GtpV1_MBMSNotificationRejectResponse = 99, + /** Create MBMS Notification Request */ + GtpV1_CreateMBMSNotificationRequest = 100, + /** Create MBMS Notification Response */ + GtpV1_CreateMBMSNotificationResponse = 101, + /** Update MBMS Notification Request */ + GtpV1_UpdateMBMSNotificationRequest = 102, + /** Update MBMS Notification Response */ + GtpV1_UpdateMBMSNotificationResponse = 103, + /** Delete MBMS Notification Request */ + GtpV1_DeleteMBMSNotificationRequest = 104, + /** Delete MBMS Notification Response */ + GtpV1_DeleteMBMSNotificationResponse = 105, + /** MBMS Registration Request */ + GtpV1_MBMSRegistrationRequest = 112, + /** MBMS Registration Response */ + GtpV1_MBMSRegistrationResponse = 113, + /** MBMS De-Registration Request */ + GtpV1_MBMSDeRegistrationRequest = 114, + /** MBMS De-Registration Response */ + GtpV1_MBMSDeRegistrationResponse = 115, + /** MBMS Session Start Request */ + GtpV1_MBMSSessionStartRequest = 116, + /** MBMS Session Start Response */ + GtpV1_MBMSSessionStartResponse = 117, + /** MBMS Session Stop Request */ + GtpV1_MBMSSessionStopRequest = 118, + /** MBMS Session Stop Response */ + GtpV1_MBMSSessionStopResponse = 119, + /** MBMS Session Update Request */ + GtpV1_MBMSSessionUpdateRequest = 120, + /** MBMS Session Update Response */ + GtpV1_MBMSSessionUpdateResponse = 121, + /** MS Info Change Request */ + GtpV1_MSInfoChangeRequest = 128, + /** MS Info Change Response */ + GtpV1_MSInfoChangeResponse = 129, + /** Data Record Transfer Request */ + GtpV1_DataRecordTransferRequest = 240, + /** Data Record Transfer Response */ + GtpV1_DataRecordTransferResponse = 241, + /** End Marker */ + GtpV1_EndMarker = 254, + /** G-PDU */ + GtpV1_GPDU = 255 +}; + +/** + * @class GtpV1Layer + * A class representing the [GTP + * v1](https://en.wikipedia.org/wiki/GPRS_Tunnelling_Protocol) protocol. + */ +class GtpV1Layer : public Layer { + private: + struct gtpv1_header_extra { + uint16_t sequenceNumber; + uint8_t npduNumber; + uint8_t nextExtensionHeader; + }; + + gtpv1_header_extra* getHeaderExtra() const; + + void init(GtpV1MessageType messageType, uint32_t teid, bool setSeqNum, + uint16_t seqNum, bool setNpduNum, uint8_t npduNum); + + public: + /** + * @class GtpExtension + * A class that represents [GTP header + * extensions](https://en.wikipedia.org/wiki/GPRS_Tunnelling_Protocol) + */ + class GtpExtension { + friend class GtpV1Layer; + + private: + uint8_t* m_Data; + size_t m_DataLen; + uint8_t m_ExtType; + + GtpExtension(uint8_t* data, size_t dataLen, uint8_t type); + + void setNextHeaderType(uint8_t nextHeaderType); + + static GtpExtension createGtpExtension(uint8_t* data, size_t dataLen, + uint8_t extType, uint16_t content); + + public: + /** + * An empty c'tor that creates an empty object, meaning one that isNull() + * returns "true") + */ + GtpExtension(); + + /** + * A copy c'tor for this class + * @param[in] other The GTP extension to copy from + */ + GtpExtension(const GtpExtension& other); + + /** + * An assignment operator for this class + * @param[in] other The extension to assign from + * @return A reference to the assignee + */ + GtpExtension& operator=(const GtpExtension& other); + + /** + * @return Instances of this class may be initialized as empty, meaning they + * don't contain any data. In these cases this method returns true + */ + bool isNull() const; + + /** + * @return The extension type. If the object is empty a value of zero is + * returned + */ + uint8_t getExtensionType() const; + + /** + * @return The total length of the extension including the length and next + * extension type fields. If the object is empty a value of zero is returned + */ + size_t getTotalLength() const; + + /** + * @return The length of the extension's content, excluding the extension + * length and next extension type fields. If the object is empty a value of + * zero is returned + */ + size_t getContentLength() const; + + /** + * @return A byte array that includes the extension's content. The length of + * this array can be determined by getContentLength(). If the object is + * empty a null value is returned + */ + uint8_t* getContent() const; + + /** + * @return The extension type of the next header. If there are no more + * header extensions or if this object is empty a value of zero is returned + */ + uint8_t getNextExtensionHeaderType() const; + + /** + * @return An instance of this class representing the next extension header, + * if exists in the message. If there are no more header extensions or if + * this object is empty an empty instance of GtpExtension is returned, + * meaning one that GtpExtension#isNull() returns "true" + */ + GtpExtension getNextExtension() const; + }; // GtpExtension + + virtual ~GtpV1Layer() {} + + /** 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 + */ + GtpV1Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : Layer(data, dataLen, prevLayer, packet) { + m_Protocol = GTPv1; + } + + /** + * A constructor that creates a new GTPv1 layer and sets the message type and + * the TEID value + * @param[in] messageType The GTPv1 message type to be set in the newly + * created layer + * @param[in] teid The TEID value to be set in the newly created layer + */ + GtpV1Layer(GtpV1MessageType messageType, uint32_t teid); + + /** + * A constructor that creates a new GTPv1 layer and sets various parameters + * @param[in] messageType The GTPv1 message type to be set in the newly + * created layer + * @param[in] teid The TEID value to be set in the newly created layer + * @param[in] setSeqNum A flag indicating whether to set a sequence number. If + * set to "false" then the parameter "seqNum" will be ignored + * @param[in] seqNum The sequence number to be set in the newly created later. + * If "setSeqNum" is set to false this parameter will be ignored + * @param[in] setNpduNum A flag indicating whether to set the N-PDU number. If + * set to "false" then the parameter "npduNum" will be ignored + * @param[in] npduNum The N-PDU number to be set in the newly created later. + * If "setNpduNum" is set to false this parameter will be ignored + */ + GtpV1Layer(GtpV1MessageType messageType, uint32_t teid, bool setSeqNum, + uint16_t seqNum, bool setNpduNum, uint8_t npduNum); + + /** + * A static method that takes a byte array and detects whether it is a GTP v1 + * message + * @param[in] data A byte array + * @param[in] dataSize The byte array size (in bytes) + * @return True if the data is identified as GTP v1 message (GTP-C or GTP-U) + */ + static bool isGTPv1(const uint8_t* data, size_t dataSize); + + /** + * @return The GTP v1 common header structure. Notice this points directly to + * the data, so every change will change the actual packet data + */ + gtpv1_header* getHeader() const { return (gtpv1_header*)m_Data; } + + /** + * Get the sequence number if exists on the message (sequence number is an + * optional field in GTP messages) + * @param[out] seqNumber Set with the sequence number value if exists in the + * layer. Otherwise remains unchanged + * @return True if the sequence number field exists in layer, in which case + * seqNumber is set with the value. Or false otherwise + */ + bool getSequenceNumber(uint16_t& seqNumber) const; + + /** + * Set a sequence number + * @param[in] seqNumber The sequence number to set + * @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); + + /** + * Get the N-PDU number if exists on the message (N-PDU number is an optional + * field in GTP messages) + * @param[out] npduNum Set with the N-PDU number value if exists in the layer. + * Otherwise remains unchanged + * @return True if the N-PDU number field exists in layer, in which case + * npduNum is set with the value. Or false otherwise + */ + bool getNpduNumber(uint8_t& npduNum) const; + + /** + * Set an N-PDU number + * @param[in] npduNum The N-PDU number to set + * @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); + + /** + * Get the type of the next header extension if exists on the message + * (extensions are optional in GTP messages) + * @param[out] nextExtType Set with the next header extension type if exists + * in layer. Otherwise remains unchanged + * @return True if the message contains header extensions, in which case + * nextExtType is set to the next header extension type. If there are no + * header extensions false is returned and nextExtType remains unchanged + */ + bool getNextExtensionHeaderType(uint8_t& nextExtType) const; + + /** + * @return An object that represents the next extension header, if exists in + * the message. If there are no extensions an empty object is returned, + * meaning an object which GtpExtension#isNull() returns "true" + */ + GtpExtension getNextExtension() const; + + /** + * Add a GTPv1 header extension. It is assumed that the extension is 4 bytes + * in length and its content is 2 bytes in length. If you need a different + * content size please reach out to me. This method takes care of extending + * the layer to make room for the new extension and also sets the relevant + * flags and fields + * @param[in] extensionType The type of the new extension + * @param[in] extensionContent A 2-byte long content + * @return An object representing the newly added extension. If there was an + * error adding the extension a null object will be returned (meaning + * GtpExtension#isNull() will return "true") and a corresponding error message + * will be written to log + */ + GtpExtension addExtension(uint8_t extensionType, uint16_t extensionContent); + + /** + * @return The message type of this GTP packet + */ + GtpV1MessageType getMessageType() const; + + /** + * @return A string representation of the packet's message type + */ + std::string getMessageTypeAsString() const; + + /** + * @return True if this is a GTP-U message, false otherwise + */ + bool isGTPUMessage() const; + + /** + * @return True if this is a GTP-C message, false otherwise + */ + bool isGTPCMessage() const; + + /** + * 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 + */ + static bool isGTPv1Port(uint16_t port) { + return port == 2152 /* GTP-U */ || port == 2123 /* GTP-C */; + } + + // implement abstract methods + + /** + * Identifies the following next layers for GTP-U packets: IPv4Layer, + * IPv6Layer. Otherwise sets PayloadLayer + */ + void parseNextLayer(); + + /** + * @return The size of the GTP header. For GTP-C packets the size is + * determined by the value of gtpv1_header#messageLength and for GTP-U the + * size only includes the GTP header itself (meaning the size of gtpv1_header + * plus the size of the optional fields such as sequence number, N-PDU or + * extensions if exist) + */ + size_t getHeaderLen() const; + + /** + * Calculate the following fields: + * - gtpv1_header#messageLength + */ + void computeCalculateFields(); + + std::string toString() const; + + OsiModelLayer getOsiModelLayer() const { return OsiModelTransportLayer; } +}; +} // namespace pcpp + +#endif // PACKETPP_GTP_LAYER diff --git a/Packet++/header/HttpLayer.h b/Packet++/header/HttpLayer.h index 9a68422e94..95f56f78c7 100644 --- a/Packet++/header/HttpLayer.h +++ b/Packet++/header/HttpLayer.h @@ -2,8 +2,8 @@ #define PACKETPP_HTTP_LAYER #include "TextBasedProtocol.h" -#include #include +#include #ifndef PCPP_DEPRECATED #if defined(__GNUC__) || defined(__clang__) @@ -11,7 +11,8 @@ #elif defined(_MSC_VER) #define PCPP_DEPRECATED __declspec(deprecated) #else -#pragma message("WARNING: DEPRECATED feature is not implemented for this compiler") +#pragma message( \ + "WARNING: DEPRECATED feature is not implemented for this compiler") #define PCPP_DEPRECATED #endif #endif @@ -22,807 +23,857 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - - /** - * An enum for HTTP version - */ - enum HttpVersion - { - /** HTTP/0.9 */ - ZeroDotNine, - /** HTTP/1.0 */ - OneDotZero, - /** HTTP/1.1 */ - OneDotOne, - /** Unknown HTTP version */ - HttpVersionUnknown - }; - - // some popular HTTP fields - - /** Host field */ -#define PCPP_HTTP_HOST_FIELD "Host" - /** Connection field */ -#define PCPP_HTTP_CONNECTION_FIELD "Connection" - /** User-Agent field */ -#define PCPP_HTTP_USER_AGENT_FIELD "User-Agent" - /** Referer field */ -#define PCPP_HTTP_REFERER_FIELD "Referer" - /** Accept field */ -#define PCPP_HTTP_ACCEPT_FIELD "Accept" - /** Accept-Encoding field */ -#define PCPP_HTTP_ACCEPT_ENCODING_FIELD "Accept-Encoding" - /** Accept-Language field */ -#define PCPP_HTTP_ACCEPT_LANGUAGE_FIELD "Accept-Language" - /** Cookie field */ -#define PCPP_HTTP_COOKIE_FIELD "Cookie" - /** Content-Length field */ -#define PCPP_HTTP_CONTENT_LENGTH_FIELD "Content-Length" - /** Content-Encoding field */ -#define PCPP_HTTP_CONTENT_ENCODING_FIELD "Content-Encoding" - /** Content-Type field */ -#define PCPP_HTTP_CONTENT_TYPE_FIELD "Content-Type" - /** Transfer-Encoding field */ -#define PCPP_HTTP_TRANSFER_ENCODING_FIELD "Transfer-Encoding" - /** Server field */ -#define PCPP_HTTP_SERVER_FIELD "Server" - - - // -------- classes to be defined later ----------------- - - - class HttpRequestFirstLine; - class HttpResponseFirstLine; - - - // -------- Class HttpMessage ----------------- - - - /** - * @class HttpMessage - * Represents a general HTTP message. It's an abstract class and cannot be instantiated. It's inherited by HttpRequestLayer and HttpResponseLayer - */ - class HttpMessage : public TextBasedProtocolMessage - { - public: - - virtual ~HttpMessage() {} - - /** - * A static method that checks whether the port is considered as HTTP - * @param[in] port The port number to be checked - * @return True if the port matches those associated with the HTTP protocol - */ - static bool isHttpPort(uint16_t port) { return port == 80 || port == 8080; } - - // overridden methods - - virtual HeaderField* addField(const std::string& fieldName, const std::string& fieldValue); - virtual HeaderField* addField(const HeaderField& newField); - virtual HeaderField* insertField(HeaderField* prevField, const std::string& fieldName, const std::string& fieldValue); - virtual HeaderField* insertField(HeaderField* prevField, const HeaderField& newField); - - OsiModelLayer getOsiModelLayer() const { return OsiModelApplicationLayer; } - - protected: - HttpMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : TextBasedProtocolMessage(data, dataLen, prevLayer, packet) {} - HttpMessage() : TextBasedProtocolMessage() {} - HttpMessage(const HttpMessage& other) : TextBasedProtocolMessage(other) {} - HttpMessage& operator=(const HttpMessage& other) { TextBasedProtocolMessage::operator=(other); return *this; } - - // implementation of abstract methods - char getHeaderFieldNameValueSeparator() const { return ':'; } - bool spacesAllowedBetweenHeaderFieldNameAndValue() const { return true; } - }; - - - // -------- Class HttpRequestLayer ----------------- - - /** - * @class HttpRequestLayer - * Represents an HTTP request header and inherits all basic functionality of HttpMessage and TextBasedProtocolMessage. - * The functionality that is added for this class is the HTTP first line concept. An HTTP request has the following first line: - * GET /bla/blabla.asp HTTP/1.1 - * Since it's not an "ordinary" HTTP field, it requires a special treatment and gets a class of it's own: HttpRequestFirstLine. - * Unlike most L2-4 protocols, an HTTP request header can spread over more than 1 packet. PcapPlusPlus currently doesn't support a header - * that is spread over more than 1 packet so in such cases: 1) only the first packet will be parsed as HttpRequestLayer (the other packets - * won't be recognized as HttpRequestLayer) and 2) the HTTP header for the first packet won't be complete (as it continues in the following - * packets), this why PcapPlusPlus can indicate that HTTP request header is complete or not(doesn't end with "\r\n\r\n" or "\n\n") using - * HttpMessage#isHeaderComplete() - */ - class HttpRequestLayer : public HttpMessage - { - friend class HttpRequestFirstLine; - public: - /** - * HTTP request methods - */ - enum HttpMethod - { - /** GET */ - HttpGET, - /** HEAD */ - HttpHEAD, - /** POST */ - HttpPOST, - /** PUT */ - HttpPUT, - /** DELETE */ - HttpDELETE, - /** TRACE */ - HttpTRACE, - /** OPTIONS */ - HttpOPTIONS, - /** CONNECT */ - HttpCONNECT, - /** PATCH */ - HttpPATCH, - /** Unknown HTTP method */ - HttpMethodUnknown - }; - - /** 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 - */ - HttpRequestLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); - - /** - * A constructor that allocates a new HTTP request header with only the first line filled. Object will be created without further fields. - * The user can then add fields using addField() methods - * @param[in] method The HTTP method used in this HTTP request - * @param[in] uri The URI of the first line - * @param[in] version HTTP version to be used in this request - */ - HttpRequestLayer(HttpMethod method, const std::string& uri, HttpVersion version); - - virtual ~HttpRequestLayer(); - - /** - * A copy constructor for this layer. This copy constructor inherits base copy constructor HttpMessage#HttpMessage() and add the functionality - * of copying the first line as well - * @param[in] other The instance to copy from - */ - HttpRequestLayer(const HttpRequestLayer& other); - - /** - * An assignment operator overload for this layer. This method inherits base assignment operator HttpMessage#operator=() and add the functionality - * of copying the first line as well - * @param[in] other The instance to copy from - * @return A reference to the assignee - */ - HttpRequestLayer& operator=(const HttpRequestLayer& other); - - /** - * @return A pointer to the first line instance for this message - */ - HttpRequestFirstLine* getFirstLine() const { return m_FirstLine; } - - /** - * The URL is hostname+uri. So given the following URL, for example: "www.cnn.com/main.html", the hostname is "www.cnn.com" and the URI - * is "/.main.html". URI and hostname are split to 2 different places inside the HTTP request packet: URI is in the first line and hostname - * is in "HOST" field. - * This methods concatenates the hostname and URI to the full URL - * @return The URL of the HTTP request message - */ - std::string getUrl() const; - - // implement Layer's abstract methods - std::string toString() const; - - private: - HttpRequestFirstLine* m_FirstLine; - }; - - // -------- Class HttpResponseStatusCode ----------------- - - /** - * @struct HttpResponseStatusCode - * @brief The enum wrapper class of HTTP response status codes - */ - class HttpResponseStatusCode - { - public: - /** - * @brief Define enum types and the corresponding int values - */ - enum Value: int - { - /** 100 Continue*/ - Http100Continue = 100, - /** 101 Switching Protocols*/ - Http101SwitchingProtocols = 101, - /** 102 Processing */ - Http102Processing = 102, - /** 103 Early Hints */ - Http103EarlyHints = 103, - /** 104-199 Unassigned */ - - /** 200 OK */ - Http200OK= 200, - /** 201 Created */ - Http201Created = 201, - /** 202 Accepted */ - Http202Accepted = 202, - /** 203 Non-Authoritative Information */ - Http203NonAuthoritativeInformation = 203, - /** 204 No Content*/ - Http204NoContent = 204, - /** 205 Reset Content*/ - Http205ResetContent = 205, - /** 206 Partial Content */ - Http206PartialContent = 206, - /** 207 Multi-Status */ - Http207MultiStatus = 207, - /** 208 Already Reported */ - Http208AlreadyReported = 208, - /** 209-225 Unassigned */ - /** 226 IM Used */ - Http226IMUsed = 226, - /** 227-299 Unassigned */ - - /** 300 Multiple Choices */ - Http300MultipleChoices = 300, - /** 301 Moved Permanently */ - Http301MovedPermanently = 301, - /** 302 (various messages) */ - Http302 = 302, - /** 303 See Other */ - Http303SeeOther = 303, - /** 304 Not Modified */ - Http304NotModified = 304, - /** 305 Use Proxy */ - Http305UseProxy = 305, - /** 306 Switch Proxy */ - Http306SwitchProxy = 306, - /** 307 Temporary Redirect */ - Http307TemporaryRedirect = 307, - /** 308 Permanent Redirect, */ - Http308PermanentRedirect = 308, - /** 309-399 Unassigned */ - - /** 400 Bad Request */ - Http400BadRequest = 400, - /** 401 Unauthorized */ - Http401Unauthorized = 401, - /** 402 Payment Required */ - Http402PaymentRequired = 402, - /** 403 Forbidden */ - Http403Forbidden = 403, - /** 404 Not Found */ - Http404NotFound = 404, - /** 405 Method Not Allowed */ - Http405MethodNotAllowed = 405, - /** 406 Not Acceptable */ - Http406NotAcceptable = 406, - /** 407 Proxy Authentication Required */ - Http407ProxyAuthenticationRequired = 407, - /** 408 Request Timeout */ - Http408RequestTimeout = 408, - /** 409 Conflict */ - Http409Conflict = 409, - /** 410 Gone */ - Http410Gone = 410, - /** 411 Length Required */ - Http411LengthRequired = 411, - /** 412 Precondition Failed */ - Http412PreconditionFailed = 412, - /** 413 RequestEntity Too Large */ - Http413RequestEntityTooLarge = 413, - /** 414 Request-URI Too Long */ - Http414RequestURITooLong = 414, - /** 415 Unsupported Media Type */ - Http415UnsupportedMediaType = 415, - /** 416 Requested Range Not Satisfiable */ - Http416RequestedRangeNotSatisfiable = 416, - /** 417 Expectation Failed */ - Http417ExpectationFailed = 417, - /** 418 I'm a teapot */ - Http418ImATeapot = 418, - /** 419 Authentication Timeout */ - Http419AuthenticationTimeout = 419, - /** 420 (various messages) */ - Http420 = 420, - /** 421 Misdirected Request */ - Http421MisdirectedRequest = 421, - /** 422 Unprocessable Entity */ - Http422UnprocessableEntity = 422, - /** 423 Locked */ - Http423Locked = 423, - /** 424 Failed Dependency */ - Http424FailedDependency = 424, - /** 425 Too Early */ - Http425TooEarly = 425, - /** 426 Upgrade Required */ - Http426UpgradeRequired = 426, - /** 427 Unassigned */ - /** 428 Precondition Required */ - Http428PreconditionRequired = 428, - /** 429 Too Many Requests */ - Http429TooManyRequests = 429, - /** 430 Unassigned */ - /** 431 Request Header Fields Too Large */ - Http431RequestHeaderFieldsTooLarge = 431, - /** 432-439 unassigned */ - /** 440 Login Timeout */ - Http440LoginTimeout = 440, - /** 441-443 unassigned */ - /** 444 No Response */ - Http444NoResponse = 444, - /** 445-448 unassigned */ - /** 449 Retry With */ - Http449RetryWith = 449, - /** 450 Blocked by Windows Parental Controls */ - Http450BlockedByWindowsParentalControls = 450, - /** 451 (various messages) */ - Http451 = 451, - /** 452-493 unassigned */ - /** 494 Request Header Too Large */ - Http494RequestHeaderTooLarge = 494, - /** 495 Cert Error */ - Http495CertError = 495, - /** 496 No Cert */ - Http496NoCert = 496, - /** 497 HTTP to HTTPS */ - Http497HTTPtoHTTPS = 497, - /** 498 Token expired/invalid */ - Http498TokenExpiredInvalid = 498, - /** 499 (various messages) */ - Http499 = 499, - - /** 500 Internal Server Error */ - Http500InternalServerError = 500, - /** 501 Not Implemented */ - Http501NotImplemented = 501, - /** 502 Bad Gateway */ - Http502BadGateway = 502, - /** 503 Service Unavailable */ - Http503ServiceUnavailable = 503, - /** 504 Gateway Timeout */ - Http504GatewayTimeout = 504, - /** 505 HTTP Version Not Supported */ - Http505HTTPVersionNotSupported = 505, - /** 506 Variant Also Negotiates */ - Http506VariantAlsoNegotiates = 506, - /** 507 Insufficient Storage */ - Http507InsufficientStorage = 507, - /** 508 Loop Detected */ - Http508LoopDetected = 508, - /** 509 Bandwidth Limit Exceeded */ - Http509BandwidthLimitExceeded = 509, - /** 510 Not Extended */ - Http510NotExtended = 510, - /** 511 Network Authentication Required */ - Http511NetworkAuthenticationRequired = 511, - /** 512-519 unassigned */ - /** 520 Origin Error */ - Http520OriginError = 520, - /** 521 Web server is down */ - Http521WebServerIsDown = 521, - /** 522 Connection timed out */ - Http522ConnectionTimedOut = 522, - /** 523 Proxy Declined Request */ - Http523ProxyDeclinedRequest = 523, - /** 524 A timeout occurred */ - Http524aTimeoutOccurred = 524, - /** 525-597 unassigned */ - /** 598 Network read timeout error */ - Http598NetworkReadTimeoutError = 598, - /** 599 Network connect timeout error */ - Http599NetworkConnectTimeoutError = 599, - - /** Unknown status code */ - HttpStatus1xxCodeUnknown = 900001, // 1xx: Informational - Request received, continuing process - HttpStatus2xxCodeUnknown = 900002, // 2xx: Success - The action was successfully received, understood, and accepted - HttpStatus3xxCodeUnknown = 900003, // 3xx: Redirection - Further action must be taken in order to complete the request - HttpStatus4xxCodeUnknown = 900004, // 4xx: Client Error - The request contains bad syntax or cannot be fulfilled - HttpStatus5xxCodeUnknown = 900005, // 5xx: Server Error - The server failed to fulfill an apparently valid request - HttpStatusCodeUnknown = 999999, // other arbitrary number - }; - - HttpResponseStatusCode() = default; - - // cppcheck-suppress noExplicitConstructor - /** - * @brief Construct HttpResponseStatusCode from Value enum - * @param[in] statusCode the status code enum - */ - HttpResponseStatusCode(Value statusCode) : m_Value(statusCode) { } - - /** - * @brief Construct HttpResponseStatusCode from the code number and the customized message - * @param[in] statusCodeNumber the status code in number, e.g. 200, 404 - * @param[in] statusMessage the status message, optional, leave empty to use a default message - */ - explicit HttpResponseStatusCode(const int &statusCodeNumber, const std::string& statusMessage = ""); - - /** - * @brief Construct HttpResponseStatusCode from Value enum and the customized message - * @param[in] statusCode the status code enum - * @param[in] statusMessage the customized status message, optional - */ - explicit HttpResponseStatusCode(const Value& statusCode, const std::string& statusMessage); - - // Allow switch and comparisons. - operator Value() const { return m_Value; } - // Prevent usage: if(httpResponseStatusCode) - explicit operator bool() const = delete; - - /** - * @brief get status code number as string - */ - std::string toString() const - { - return std::to_string(m_Value); - } - - /** - * @brief get status code number as int - */ - int toInt() const - { - return static_cast(m_Value); - } - - /** - * @brief get status code message, e.g. "OK", "Not Found" - */ - std::string getMessage() const; - /** - * @return If this HttpResponseStatusCode a valid code - * @note Any unknown or error code has an extreme large enum value - */ - bool isUnsupportedCode() const - { - return m_Value > 599; - } - - private: - Value m_Value = HttpStatusCodeUnknown; - std::string m_CustomizedMessage; - }; - - // -------- Class HttpResponseLayer ----------------- - - /** - * @class HttpResponseLayer - * Represents an HTTP response header and inherits all basic functionality of HttpMessage and TextBasedProtocolMessage. - * The functionality that is added for this class is the HTTP first line concept. An HTTP response has the following first line: - * 200 OK HTTP/1.1 - * Since it's not an "ordinary" HTTP field, it requires a special treatment and gets a class of it's own: HttpResponseFirstLine. - * Unlike most L2-4 protocols, an HTTP response header can spread over more than 1 packet. PcapPlusPlus currently doesn't support a header - * that is spread over more than 1 packet so in such cases: 1) only the first packet will be parsed as HttpResponseLayer (the other packets - * won't be recognized as HttpResponseLayer) and 2) the HTTP header for the first packet won't be complete (as it continues in the following - * packets), this why PcapPlusPlus can indicate that HTTP response header is complete or not (doesn't end with "\r\n\r\n" or "\n\n") using - * HttpMessage#isHeaderComplete() - */ - class HttpResponseLayer : public HttpMessage - { - friend class HttpResponseFirstLine; - public: - // backward compatibility - using HttpResponseStatusCode = pcpp::HttpResponseStatusCode; - - /** 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 - */ - HttpResponseLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); - - /** - * A constructor that allocates a new HTTP response header with only the first line filled. Object will be created without further fields. - * The user can then add fields using addField() methods - * @param[in] version HTTP version to be used - * @param[in] statusCode Status code to be used - * @param[in] statusCodeString Most status codes have their default string, e.g 200 is usually "OK", 404 is usually "Not Found", etc. - * But the user can set a non-default status code string and it will be written in the header first line. Empty string ("") means using the - * default status code string - * @deprecated Use other constructors instead. - */ - PCPP_DEPRECATED explicit HttpResponseLayer(HttpVersion version, const HttpResponseStatusCode& statusCode, const std::string& statusCodeString); - - /** - * A constructor that allocates a new HTTP response header with only the first line filled. Object will be created without further fields. - * The user can then add fields using addField() methods - * @param[in] version HTTP version to be used - * @param[in] statusCode Status code to be used - */ - explicit HttpResponseLayer(HttpVersion version, const HttpResponseStatusCode& statusCode); - - virtual ~HttpResponseLayer(); - - /** - * A copy constructor for this layer. This copy constructor inherits base copy constructor HttpMessage#HttpMessage() and adds the functionality - * of copying the first line as well - * @param[in] other The instance to copy from - */ - HttpResponseLayer(const HttpResponseLayer& other); - - /** - * An assignment operator overload for this layer. This method inherits base assignment operator HttpMessage#operator=() and adds the functionality - * of copying the first line as well - * @param[in] other The instance to copy from - * @return A reference to the assignee - */ - HttpResponseLayer& operator=(const HttpResponseLayer& other); - - /** - * @return A pointer to the first line instance for this message - */ - HttpResponseFirstLine* getFirstLine() const { return m_FirstLine; } - - /** - * The length of the body of many HTTP response messages is determined by a HTTP header field called "Content-Length". This method sets - * The content-length field value. The method supports several cases: - * - If the "Content-Length" field exists - the method will only replace the existing value with the new value - * - If the "Content-Length" field doesn't exist - the method will create this field and put the value in it. Here are also 2 cases: - * - If prevFieldName is specified - the new "Content-Length" field will be created after it - * - If prevFieldName isn't specified or doesn't exist - the new "Content-Length" field will be created as the last field before - * end-of-header field - * - * @param[in] contentLength The content length value to set - * @param[in] prevFieldName Optional field, if specified and "Content-Length" field doesn't exist, it will be created after it - * @return A pointer to the "Content-Length" field, or NULL if creation failed for some reason - */ - HeaderField* setContentLength(int contentLength, const std::string &prevFieldName = ""); - - /** - * The length of the body of many HTTP response messages is determined by a HTTP header field called "Content-Length". This method - * parses this field, extracts its value and return it. If this field doesn't exist the method will return 0 - * @return HTTP response body length determined by "Content-Length" field - */ - int getContentLength() const; - - // implement Layer's abstract methods - - std::string toString() const; - - private: - HttpResponseFirstLine* m_FirstLine; - - }; - - - - - - // -------- Class HttpRequestFirstLine ----------------- - - /** - * @class HttpRequestFirstLine - * Represents an HTTP request header first line. The first line includes 3 parameters: HTTP method (e.g GET, POST, etc.), - * URI (e.g /main/index.html) and HTTP version (e.g HTTP/1.1). All these parameters are included in this class, and the user - * can retrieve or set them. - * This class cannot be instantiated by users, it's created inside HttpRequestLayer and user can get a pointer to an instance of it. All "get" - * methods of this class will retrieve the actual data of the HTTP request and the "set" methods will change the packet data. - * Since HTTP is a textual protocol, most fields aren't of fixed size and this also applies to the first line parameters. So most "set" methods - * of this class need in most cases to shorten or extend the data in HttpRequestLayer. These methods will return a false value if this - * action failed - */ - class HttpRequestFirstLine - { - friend class HttpRequestLayer; - public: - /** - * @return The HTTP method - */ - HttpRequestLayer::HttpMethod getMethod() const { return m_Method; } - - /** - * Set the HTTP method - * @param[in] newMethod The method to set - * @return False if newMethod is HttpRequestLayer#HttpMethodUnknown or if shortening/extending the HttpRequestLayer data failed. True otherwise - */ - bool setMethod(HttpRequestLayer::HttpMethod newMethod); - - /** - * @return A copied version of the URI (notice changing the return value won't change the actual data of the packet) - */ - std::string getUri() const; - - /** - * Set the URI - * @param[in] newUri The URI to set - * @return False if shortening/extending the HttpRequestLayer data failed. True otherwise - */ - bool setUri(std::string newUri); - - /** - * @return The HTTP version - */ - HttpVersion getVersion() const { return m_Version; } - - /** - * Set the HTTP version. This method doesn't return a value since all supported HTTP versions are of the same size - * (HTTP/0.9, HTTP/1.0, HTTP/1.1) - * @param[in] newVersion The HTTP version to set - */ - void setVersion(HttpVersion newVersion); - - /** - * A static method for parsing the HTTP method out of raw data - * @param[in] data The raw data - * @param[in] dataLen The raw data length - * @return The parsed HTTP method - */ - static HttpRequestLayer::HttpMethod parseMethod(const char* data, size_t dataLen); - - /** - * @return The size in bytes of the HTTP first line - */ - int getSize() const { return m_FirstLineEndOffset; } - - /** - * As explained in HttpRequestLayer, an HTTP header can spread over more than 1 packet, so when looking at a single packet - * the header can be partial. Same goes for the first line - it can spread over more than 1 packet. This method returns an indication - * whether the first line is partial - * @return False if the first line is partial, true if it's complete - */ - bool isComplete() const { return m_IsComplete; } - - /** - * @class HttpRequestFirstLineException - * This exception can be thrown while constructing HttpRequestFirstLine (the constructor is private, so the construction happens - * only in HttpRequestLayer). This kind of exception will be thrown if trying to construct with HTTP method of - * HttpRequestLayer#HttpMethodUnknown or with undefined HTTP version ::HttpVersionUnknown - */ - class HttpRequestFirstLineException : public std::exception - { - public: - ~HttpRequestFirstLineException() throw() {} - void setMessage(const std::string &message) { m_Message = message; } - virtual const char* what() const throw() - { - return m_Message.c_str(); - } - private: - std::string m_Message; - }; - private: - HttpRequestFirstLine(HttpRequestLayer* httpRequest); - HttpRequestFirstLine(HttpRequestLayer* httpRequest, HttpRequestLayer::HttpMethod method, HttpVersion version, const std::string& uri = "/"); - - void parseVersion(); - - HttpRequestLayer* m_HttpRequest; - HttpRequestLayer::HttpMethod m_Method; - HttpVersion m_Version; - int m_VersionOffset; - int m_UriOffset; - int m_FirstLineEndOffset; - bool m_IsComplete; - HttpRequestFirstLineException m_Exception; - }; - - - - - - // -------- Class HttpResponseFirstLine ----------------- - - /** - * @class HttpResponseFirstLine - * Represents an HTTP response header first line. The first line includes 2 parameters: status code (e.g 200 OK, 404 Not Found, etc.), - * and HTTP version (e.g HTTP/1.1). These 2 parameters are included in this class, and the user can retrieve or set them. - * This class cannot be instantiated by users, it's created inside HttpResponseLayer and user can get a pointer to an instance of it. The "get" - * methods of this class will retrieve the actual data of the HTTP response and the "set" methods will change the packet data. - * Since HTTP is a textual protocol, most fields aren't of fixed size and this also applies to the first line parameters. So most "set" methods - * of this class need in most cases to shorten or extend the data in HttpResponseLayer. These methods will return a false value if this - * action failed - */ - class HttpResponseFirstLine - { - friend class HttpResponseLayer; - public: - /** - * @return The status code as HttpResponseStatusCode enum - */ - HttpResponseStatusCode getStatusCode() const { return m_StatusCode; } - - /** - * @return The status code number as integer (e.g 200, 404, etc.) - */ - int getStatusCodeAsInt() const; - - /** - * @return The status code message (e.g "OK", "Not Found", etc.) - */ - std::string getStatusCodeString() const; - - /** - * Set the status code - * @param[in] newStatusCode The new status code to set - * @param[in] statusCodeString An optional parameter: set a non-default status code message (e.g "Bla Bla" instead of "Not Found"). If - * this parameter isn't supplied or supplied as empty string (""), the default message for the status code will be set - * @return True if setting the status code was completed successfully, false otherwise - * @deprecated Use the other overload instead. - */ - PCPP_DEPRECATED bool setStatusCode(const HttpResponseStatusCode& newStatusCode, const std::string& statusCodeString); - - /** - * Set the status code - * @param[in] newStatusCode The new status code to set - * @return True if setting the status code was completed successfully, false otherwise - */ - bool setStatusCode(const HttpResponseStatusCode& newStatusCode); - - /** - * @return The HTTP version - */ - HttpVersion getVersion() const { return m_Version; } - - /** - * Set the HTTP version. This method doesn't return a value since all supported HTTP versions are of the same size - * (HTTP/0.9, HTTP/1.0, HTTP/1.1) - * @param[in] newVersion The HTTP version to set - */ - void setVersion(HttpVersion newVersion); - - /** - * A static method for parsing the HTTP status code out of raw data - * @param[in] data The raw data - * @param[in] dataLen The raw data length - * @return The parsed HTTP status code as enum - */ - static HttpResponseStatusCode parseStatusCode(const char* data, size_t dataLen); - - /** - * A static method for parsing the HTTP version out of raw first line data (e.g "HTTP/x.y") - * @param[in] data The raw data - * @param[in] dataLen The raw data length - * @return The parsed HTTP status code as enum - */ - static HttpVersion parseVersion(const char* data, size_t dataLen); - - /** - * @return The size in bytes of the HTTP first line - */ - int getSize() const { return m_FirstLineEndOffset; } - - /** - * As explained in HttpResponseLayer, an HTTP header can spread over more than 1 packet, so when looking at a single packet - * the header can be partial. Same goes for the first line - it can spread over more than 1 packet. This method returns an indication - * whether the first line is partial - * @return False if the first line is partial, true if it's complete - */ - bool isComplete() const { return m_IsComplete; } - - /** - * @class HttpResponseFirstLineException - * This exception can be thrown while constructing HttpResponseFirstLine (the constructor is private, so the construction happens - * only in HttpResponseLayer). This kind of exception will be thrown if trying to construct with a HTTP status code that is not in - * HttpResponseStatusCode or with undefined HTTP version ::HttpVersionUnknown - */ - class HttpResponseFirstLineException : public std::exception - { - public: - ~HttpResponseFirstLineException() throw() {} - void setMessage(const std::string &message) { m_Message = message; } - virtual const char* what() const throw() - { - return m_Message.c_str(); - } - private: - std::string m_Message; - }; - - private: - HttpResponseFirstLine(HttpResponseLayer* httpResponse); - HttpResponseFirstLine(HttpResponseLayer* httpResponse, HttpVersion version, const HttpResponseStatusCode& statusCode); - - HttpResponseLayer* m_HttpResponse; - HttpVersion m_Version; - HttpResponseStatusCode m_StatusCode; - int m_FirstLineEndOffset; - bool m_IsComplete; - HttpResponseFirstLineException m_Exception; - }; +namespace pcpp { + +/** + * An enum for HTTP version + */ +enum HttpVersion { + /** HTTP/0.9 */ + ZeroDotNine, + /** HTTP/1.0 */ + OneDotZero, + /** HTTP/1.1 */ + OneDotOne, + /** Unknown HTTP version */ + HttpVersionUnknown +}; + +// some popular HTTP fields + +/** Host field */ +#define PCPP_HTTP_HOST_FIELD "Host" +/** Connection field */ +#define PCPP_HTTP_CONNECTION_FIELD "Connection" +/** User-Agent field */ +#define PCPP_HTTP_USER_AGENT_FIELD "User-Agent" +/** Referer field */ +#define PCPP_HTTP_REFERER_FIELD "Referer" +/** Accept field */ +#define PCPP_HTTP_ACCEPT_FIELD "Accept" +/** Accept-Encoding field */ +#define PCPP_HTTP_ACCEPT_ENCODING_FIELD "Accept-Encoding" +/** Accept-Language field */ +#define PCPP_HTTP_ACCEPT_LANGUAGE_FIELD "Accept-Language" +/** Cookie field */ +#define PCPP_HTTP_COOKIE_FIELD "Cookie" +/** Content-Length field */ +#define PCPP_HTTP_CONTENT_LENGTH_FIELD "Content-Length" +/** Content-Encoding field */ +#define PCPP_HTTP_CONTENT_ENCODING_FIELD "Content-Encoding" +/** Content-Type field */ +#define PCPP_HTTP_CONTENT_TYPE_FIELD "Content-Type" +/** Transfer-Encoding field */ +#define PCPP_HTTP_TRANSFER_ENCODING_FIELD "Transfer-Encoding" +/** Server field */ +#define PCPP_HTTP_SERVER_FIELD "Server" + +// -------- classes to be defined later ----------------- + +class HttpRequestFirstLine; +class HttpResponseFirstLine; + +// -------- Class HttpMessage ----------------- + +/** + * @class HttpMessage + * Represents a general HTTP message. It's an abstract class and cannot be + * instantiated. It's inherited by HttpRequestLayer and HttpResponseLayer + */ +class HttpMessage : public TextBasedProtocolMessage { + public: + virtual ~HttpMessage() {} + + /** + * A static method that checks whether the port is considered as HTTP + * @param[in] port The port number to be checked + * @return True if the port matches those associated with the HTTP protocol + */ + static bool isHttpPort(uint16_t port) { return port == 80 || port == 8080; } + + // overridden methods + + virtual HeaderField* addField(const std::string& fieldName, + const std::string& fieldValue); + virtual HeaderField* addField(const HeaderField& newField); + virtual HeaderField* insertField(HeaderField* prevField, + const std::string& fieldName, + const std::string& fieldValue); + virtual HeaderField* insertField(HeaderField* prevField, + const HeaderField& newField); + + OsiModelLayer getOsiModelLayer() const { return OsiModelApplicationLayer; } + + protected: + HttpMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : TextBasedProtocolMessage(data, dataLen, prevLayer, packet) {} + HttpMessage() : TextBasedProtocolMessage() {} + HttpMessage(const HttpMessage& other) : TextBasedProtocolMessage(other) {} + HttpMessage& operator=(const HttpMessage& other) { + TextBasedProtocolMessage::operator=(other); + return *this; + } + + // implementation of abstract methods + char getHeaderFieldNameValueSeparator() const { return ':'; } + bool spacesAllowedBetweenHeaderFieldNameAndValue() const { return true; } +}; + +// -------- Class HttpRequestLayer ----------------- + +/** + * @class HttpRequestLayer + * Represents an HTTP request header and inherits all basic functionality of + * HttpMessage and TextBasedProtocolMessage. The functionality that is added for + * this class is the HTTP first line concept. An HTTP request has the following + * first line: GET /bla/blabla.asp HTTP/1.1 Since it's not an "ordinary" + * HTTP field, it requires a special treatment and gets a class of it's own: + * HttpRequestFirstLine. Unlike most L2-4 protocols, an HTTP request header can + * spread over more than 1 packet. PcapPlusPlus currently doesn't support a + * header that is spread over more than 1 packet so in such cases: 1) only the + * first packet will be parsed as HttpRequestLayer (the other packets won't be + * recognized as HttpRequestLayer) and 2) the HTTP header for the first packet + * won't be complete (as it continues in the following packets), this why + * PcapPlusPlus can indicate that HTTP request header is complete or not(doesn't + * end with "\r\n\r\n" or "\n\n") using HttpMessage#isHeaderComplete() + */ +class HttpRequestLayer : public HttpMessage { + friend class HttpRequestFirstLine; + + public: + /** + * HTTP request methods + */ + enum HttpMethod { + /** GET */ + HttpGET, + /** HEAD */ + HttpHEAD, + /** POST */ + HttpPOST, + /** PUT */ + HttpPUT, + /** DELETE */ + HttpDELETE, + /** TRACE */ + HttpTRACE, + /** OPTIONS */ + HttpOPTIONS, + /** CONNECT */ + HttpCONNECT, + /** PATCH */ + HttpPATCH, + /** Unknown HTTP method */ + HttpMethodUnknown + }; + + /** 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 + */ + HttpRequestLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet); + + /** + * A constructor that allocates a new HTTP request header with only the first + * line filled. Object will be created without further fields. The user can + * then add fields using addField() methods + * @param[in] method The HTTP method used in this HTTP request + * @param[in] uri The URI of the first line + * @param[in] version HTTP version to be used in this request + */ + HttpRequestLayer(HttpMethod method, const std::string& uri, + HttpVersion version); + + virtual ~HttpRequestLayer(); + + /** + * A copy constructor for this layer. This copy constructor inherits base copy + * constructor HttpMessage#HttpMessage() and add the functionality of copying + * the first line as well + * @param[in] other The instance to copy from + */ + HttpRequestLayer(const HttpRequestLayer& other); + + /** + * An assignment operator overload for this layer. This method inherits base + * assignment operator HttpMessage#operator=() and add the functionality of + * copying the first line as well + * @param[in] other The instance to copy from + * @return A reference to the assignee + */ + HttpRequestLayer& operator=(const HttpRequestLayer& other); + + /** + * @return A pointer to the first line instance for this message + */ + HttpRequestFirstLine* getFirstLine() const { return m_FirstLine; } + + /** + * The URL is hostname+uri. So given the following URL, for example: + * "www.cnn.com/main.html", the hostname is "www.cnn.com" and the URI is + * "/.main.html". URI and hostname are split to 2 different places inside the + * HTTP request packet: URI is in the first line and hostname is in "HOST" + * field. This methods concatenates the hostname and URI to the full URL + * @return The URL of the HTTP request message + */ + std::string getUrl() const; + + // implement Layer's abstract methods + std::string toString() const; + + private: + HttpRequestFirstLine* m_FirstLine; +}; + +// -------- Class HttpResponseStatusCode ----------------- + +/** + * @struct HttpResponseStatusCode + * @brief The enum wrapper class of HTTP response status codes + */ +class HttpResponseStatusCode { + public: + /** + * @brief Define enum types and the corresponding int values + */ + enum Value : int { + /** 100 Continue*/ + Http100Continue = 100, + /** 101 Switching Protocols*/ + Http101SwitchingProtocols = 101, + /** 102 Processing */ + Http102Processing = 102, + /** 103 Early Hints */ + Http103EarlyHints = 103, + /** 104-199 Unassigned */ + + /** 200 OK */ + Http200OK = 200, + /** 201 Created */ + Http201Created = 201, + /** 202 Accepted */ + Http202Accepted = 202, + /** 203 Non-Authoritative Information */ + Http203NonAuthoritativeInformation = 203, + /** 204 No Content*/ + Http204NoContent = 204, + /** 205 Reset Content*/ + Http205ResetContent = 205, + /** 206 Partial Content */ + Http206PartialContent = 206, + /** 207 Multi-Status */ + Http207MultiStatus = 207, + /** 208 Already Reported */ + Http208AlreadyReported = 208, + /** 209-225 Unassigned */ + /** 226 IM Used */ + Http226IMUsed = 226, + /** 227-299 Unassigned */ + + /** 300 Multiple Choices */ + Http300MultipleChoices = 300, + /** 301 Moved Permanently */ + Http301MovedPermanently = 301, + /** 302 (various messages) */ + Http302 = 302, + /** 303 See Other */ + Http303SeeOther = 303, + /** 304 Not Modified */ + Http304NotModified = 304, + /** 305 Use Proxy */ + Http305UseProxy = 305, + /** 306 Switch Proxy */ + Http306SwitchProxy = 306, + /** 307 Temporary Redirect */ + Http307TemporaryRedirect = 307, + /** 308 Permanent Redirect, */ + Http308PermanentRedirect = 308, + /** 309-399 Unassigned */ + + /** 400 Bad Request */ + Http400BadRequest = 400, + /** 401 Unauthorized */ + Http401Unauthorized = 401, + /** 402 Payment Required */ + Http402PaymentRequired = 402, + /** 403 Forbidden */ + Http403Forbidden = 403, + /** 404 Not Found */ + Http404NotFound = 404, + /** 405 Method Not Allowed */ + Http405MethodNotAllowed = 405, + /** 406 Not Acceptable */ + Http406NotAcceptable = 406, + /** 407 Proxy Authentication Required */ + Http407ProxyAuthenticationRequired = 407, + /** 408 Request Timeout */ + Http408RequestTimeout = 408, + /** 409 Conflict */ + Http409Conflict = 409, + /** 410 Gone */ + Http410Gone = 410, + /** 411 Length Required */ + Http411LengthRequired = 411, + /** 412 Precondition Failed */ + Http412PreconditionFailed = 412, + /** 413 RequestEntity Too Large */ + Http413RequestEntityTooLarge = 413, + /** 414 Request-URI Too Long */ + Http414RequestURITooLong = 414, + /** 415 Unsupported Media Type */ + Http415UnsupportedMediaType = 415, + /** 416 Requested Range Not Satisfiable */ + Http416RequestedRangeNotSatisfiable = 416, + /** 417 Expectation Failed */ + Http417ExpectationFailed = 417, + /** 418 I'm a teapot */ + Http418ImATeapot = 418, + /** 419 Authentication Timeout */ + Http419AuthenticationTimeout = 419, + /** 420 (various messages) */ + Http420 = 420, + /** 421 Misdirected Request */ + Http421MisdirectedRequest = 421, + /** 422 Unprocessable Entity */ + Http422UnprocessableEntity = 422, + /** 423 Locked */ + Http423Locked = 423, + /** 424 Failed Dependency */ + Http424FailedDependency = 424, + /** 425 Too Early */ + Http425TooEarly = 425, + /** 426 Upgrade Required */ + Http426UpgradeRequired = 426, + /** 427 Unassigned */ + /** 428 Precondition Required */ + Http428PreconditionRequired = 428, + /** 429 Too Many Requests */ + Http429TooManyRequests = 429, + /** 430 Unassigned */ + /** 431 Request Header Fields Too Large */ + Http431RequestHeaderFieldsTooLarge = 431, + /** 432-439 unassigned */ + /** 440 Login Timeout */ + Http440LoginTimeout = 440, + /** 441-443 unassigned */ + /** 444 No Response */ + Http444NoResponse = 444, + /** 445-448 unassigned */ + /** 449 Retry With */ + Http449RetryWith = 449, + /** 450 Blocked by Windows Parental Controls */ + Http450BlockedByWindowsParentalControls = 450, + /** 451 (various messages) */ + Http451 = 451, + /** 452-493 unassigned */ + /** 494 Request Header Too Large */ + Http494RequestHeaderTooLarge = 494, + /** 495 Cert Error */ + Http495CertError = 495, + /** 496 No Cert */ + Http496NoCert = 496, + /** 497 HTTP to HTTPS */ + Http497HTTPtoHTTPS = 497, + /** 498 Token expired/invalid */ + Http498TokenExpiredInvalid = 498, + /** 499 (various messages) */ + Http499 = 499, + + /** 500 Internal Server Error */ + Http500InternalServerError = 500, + /** 501 Not Implemented */ + Http501NotImplemented = 501, + /** 502 Bad Gateway */ + Http502BadGateway = 502, + /** 503 Service Unavailable */ + Http503ServiceUnavailable = 503, + /** 504 Gateway Timeout */ + Http504GatewayTimeout = 504, + /** 505 HTTP Version Not Supported */ + Http505HTTPVersionNotSupported = 505, + /** 506 Variant Also Negotiates */ + Http506VariantAlsoNegotiates = 506, + /** 507 Insufficient Storage */ + Http507InsufficientStorage = 507, + /** 508 Loop Detected */ + Http508LoopDetected = 508, + /** 509 Bandwidth Limit Exceeded */ + Http509BandwidthLimitExceeded = 509, + /** 510 Not Extended */ + Http510NotExtended = 510, + /** 511 Network Authentication Required */ + Http511NetworkAuthenticationRequired = 511, + /** 512-519 unassigned */ + /** 520 Origin Error */ + Http520OriginError = 520, + /** 521 Web server is down */ + Http521WebServerIsDown = 521, + /** 522 Connection timed out */ + Http522ConnectionTimedOut = 522, + /** 523 Proxy Declined Request */ + Http523ProxyDeclinedRequest = 523, + /** 524 A timeout occurred */ + Http524aTimeoutOccurred = 524, + /** 525-597 unassigned */ + /** 598 Network read timeout error */ + Http598NetworkReadTimeoutError = 598, + /** 599 Network connect timeout error */ + Http599NetworkConnectTimeoutError = 599, + + /** Unknown status code */ + HttpStatus1xxCodeUnknown = + 900001, // 1xx: Informational - Request received, continuing process + HttpStatus2xxCodeUnknown = + 900002, // 2xx: Success - The action was successfully received, + // understood, and accepted + HttpStatus3xxCodeUnknown = + 900003, // 3xx: Redirection - Further action must be taken in order to + // complete the request + HttpStatus4xxCodeUnknown = + 900004, // 4xx: Client Error - The request contains bad syntax or cannot + // be fulfilled + HttpStatus5xxCodeUnknown = 900005, // 5xx: Server Error - The server failed + // to fulfill an apparently valid request + HttpStatusCodeUnknown = 999999, // other arbitrary number + }; + + HttpResponseStatusCode() = default; + + // cppcheck-suppress noExplicitConstructor + /** + * @brief Construct HttpResponseStatusCode from Value enum + * @param[in] statusCode the status code enum + */ + HttpResponseStatusCode(Value statusCode) : m_Value(statusCode) {} + + /** + * @brief Construct HttpResponseStatusCode from the code number and the + * customized message + * @param[in] statusCodeNumber the status code in number, e.g. 200, 404 + * @param[in] statusMessage the status message, optional, leave empty to use a + * default message + */ + explicit HttpResponseStatusCode(const int& statusCodeNumber, + const std::string& statusMessage = ""); + + /** + * @brief Construct HttpResponseStatusCode from Value enum and the customized + * message + * @param[in] statusCode the status code enum + * @param[in] statusMessage the customized status message, optional + */ + explicit HttpResponseStatusCode(const Value& statusCode, + const std::string& statusMessage); + + // Allow switch and comparisons. + operator Value() const { return m_Value; } + // Prevent usage: if(httpResponseStatusCode) + explicit operator bool() const = delete; + + /** + * @brief get status code number as string + */ + std::string toString() const { return std::to_string(m_Value); } + + /** + * @brief get status code number as int + */ + int toInt() const { return static_cast(m_Value); } + + /** + * @brief get status code message, e.g. "OK", "Not Found" + */ + std::string getMessage() const; + /** + * @return If this HttpResponseStatusCode a valid code + * @note Any unknown or error code has an extreme large enum value + */ + bool isUnsupportedCode() const { return m_Value > 599; } + + private: + Value m_Value = HttpStatusCodeUnknown; + std::string m_CustomizedMessage; +}; + +// -------- Class HttpResponseLayer ----------------- + +/** + * @class HttpResponseLayer + * Represents an HTTP response header and inherits all basic functionality of + * HttpMessage and TextBasedProtocolMessage. The functionality that is added for + * this class is the HTTP first line concept. An HTTP response has the following + * first line: 200 OK HTTP/1.1 Since it's not an "ordinary" HTTP field, + * it requires a special treatment and gets a class of it's own: + * HttpResponseFirstLine. Unlike most L2-4 protocols, an HTTP response header + * can spread over more than 1 packet. PcapPlusPlus currently doesn't support a + * header that is spread over more than 1 packet so in such cases: 1) only the + * first packet will be parsed as HttpResponseLayer (the other packets won't be + * recognized as HttpResponseLayer) and 2) the HTTP header for the first packet + * won't be complete (as it continues in the following packets), this why + * PcapPlusPlus can indicate that HTTP response header is complete or not + * (doesn't end with "\r\n\r\n" or "\n\n") using HttpMessage#isHeaderComplete() + */ +class HttpResponseLayer : public HttpMessage { + friend class HttpResponseFirstLine; + + public: + // backward compatibility + using HttpResponseStatusCode = pcpp::HttpResponseStatusCode; + + /** 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 + */ + HttpResponseLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet); + + /** + * A constructor that allocates a new HTTP response header with only the first + * line filled. Object will be created without further fields. The user can + * then add fields using addField() methods + * @param[in] version HTTP version to be used + * @param[in] statusCode Status code to be used + * @param[in] statusCodeString Most status codes have their default string, + * e.g 200 is usually "OK", 404 is usually "Not Found", etc. But the user can + * set a non-default status code string and it will be written in the header + * first line. Empty string ("") means using the default status code string + * @deprecated Use other constructors instead. + */ + PCPP_DEPRECATED explicit HttpResponseLayer( + HttpVersion version, const HttpResponseStatusCode& statusCode, + const std::string& statusCodeString); + + /** + * A constructor that allocates a new HTTP response header with only the first + * line filled. Object will be created without further fields. The user can + * then add fields using addField() methods + * @param[in] version HTTP version to be used + * @param[in] statusCode Status code to be used + */ + explicit HttpResponseLayer(HttpVersion version, + const HttpResponseStatusCode& statusCode); + + virtual ~HttpResponseLayer(); + + /** + * A copy constructor for this layer. This copy constructor inherits base copy + * constructor HttpMessage#HttpMessage() and adds the functionality of copying + * the first line as well + * @param[in] other The instance to copy from + */ + HttpResponseLayer(const HttpResponseLayer& other); + + /** + * An assignment operator overload for this layer. This method inherits base + * assignment operator HttpMessage#operator=() and adds the functionality of + * copying the first line as well + * @param[in] other The instance to copy from + * @return A reference to the assignee + */ + HttpResponseLayer& operator=(const HttpResponseLayer& other); + + /** + * @return A pointer to the first line instance for this message + */ + HttpResponseFirstLine* getFirstLine() const { return m_FirstLine; } + + /** + * The length of the body of many HTTP response messages is determined by a + * HTTP header field called "Content-Length". This method sets The + * content-length field value. The method supports several cases: + * - If the "Content-Length" field exists - the method will only replace the + * existing value with the new value + * - If the "Content-Length" field doesn't exist - the method will create this + * field and put the value in it. Here are also 2 cases: + * - If prevFieldName is specified - the new "Content-Length" field will be + * created after it + * - If prevFieldName isn't specified or doesn't exist - the new + * "Content-Length" field will be created as the last field before + * end-of-header field + * + * @param[in] contentLength The content length value to set + * @param[in] prevFieldName Optional field, if specified and "Content-Length" + * field doesn't exist, it will be created after it + * @return A pointer to the "Content-Length" field, or NULL if creation failed + * for some reason + */ + HeaderField* setContentLength(int contentLength, + const std::string& prevFieldName = ""); + + /** + * The length of the body of many HTTP response messages is determined by a + * HTTP header field called "Content-Length". This method parses this field, + * extracts its value and return it. If this field doesn't exist the method + * will return 0 + * @return HTTP response body length determined by "Content-Length" field + */ + int getContentLength() const; + + // implement Layer's abstract methods + + std::string toString() const; + + private: + HttpResponseFirstLine* m_FirstLine; +}; + +// -------- Class HttpRequestFirstLine ----------------- + +/** + * @class HttpRequestFirstLine + * Represents an HTTP request header first line. The first line includes 3 + * parameters: HTTP method (e.g GET, POST, etc.), URI (e.g /main/index.html) and + * HTTP version (e.g HTTP/1.1). All these parameters are included in this class, + * and the user can retrieve or set them. This class cannot be instantiated by + * users, it's created inside HttpRequestLayer and user can get a pointer to an + * instance of it. All "get" methods of this class will retrieve the actual data + * of the HTTP request and the "set" methods will change the packet data. Since + * HTTP is a textual protocol, most fields aren't of fixed size and this also + * applies to the first line parameters. So most "set" methods of this class + * need in most cases to shorten or extend the data in HttpRequestLayer. These + * methods will return a false value if this action failed + */ +class HttpRequestFirstLine { + friend class HttpRequestLayer; + + public: + /** + * @return The HTTP method + */ + HttpRequestLayer::HttpMethod getMethod() const { return m_Method; } + + /** + * Set the HTTP method + * @param[in] newMethod The method to set + * @return False if newMethod is HttpRequestLayer#HttpMethodUnknown or if + * shortening/extending the HttpRequestLayer data failed. True otherwise + */ + bool setMethod(HttpRequestLayer::HttpMethod newMethod); + + /** + * @return A copied version of the URI (notice changing the return value won't + * change the actual data of the packet) + */ + std::string getUri() const; + + /** + * Set the URI + * @param[in] newUri The URI to set + * @return False if shortening/extending the HttpRequestLayer data failed. + * True otherwise + */ + bool setUri(std::string newUri); + + /** + * @return The HTTP version + */ + HttpVersion getVersion() const { return m_Version; } + + /** + * Set the HTTP version. This method doesn't return a value since all + * supported HTTP versions are of the same size (HTTP/0.9, HTTP/1.0, HTTP/1.1) + * @param[in] newVersion The HTTP version to set + */ + void setVersion(HttpVersion newVersion); + + /** + * A static method for parsing the HTTP method out of raw data + * @param[in] data The raw data + * @param[in] dataLen The raw data length + * @return The parsed HTTP method + */ + static HttpRequestLayer::HttpMethod parseMethod(const char* data, + size_t dataLen); + + /** + * @return The size in bytes of the HTTP first line + */ + int getSize() const { return m_FirstLineEndOffset; } + + /** + * As explained in HttpRequestLayer, an HTTP header can spread over more than + * 1 packet, so when looking at a single packet the header can be partial. + * Same goes for the first line - it can spread over more than 1 packet. This + * method returns an indication whether the first line is partial + * @return False if the first line is partial, true if it's complete + */ + bool isComplete() const { return m_IsComplete; } + + /** + * @class HttpRequestFirstLineException + * This exception can be thrown while constructing HttpRequestFirstLine (the + * constructor is private, so the construction happens only in + * HttpRequestLayer). This kind of exception will be thrown if trying to + * construct with HTTP method of HttpRequestLayer#HttpMethodUnknown or with + * undefined HTTP version ::HttpVersionUnknown + */ + class HttpRequestFirstLineException : public std::exception { + public: + ~HttpRequestFirstLineException() throw() {} + void setMessage(const std::string& message) { m_Message = message; } + virtual const char* what() const throw() { return m_Message.c_str(); } + + private: + std::string m_Message; + }; + + private: + HttpRequestFirstLine(HttpRequestLayer* httpRequest); + HttpRequestFirstLine(HttpRequestLayer* httpRequest, + HttpRequestLayer::HttpMethod method, HttpVersion version, + const std::string& uri = "/"); + + void parseVersion(); + + HttpRequestLayer* m_HttpRequest; + HttpRequestLayer::HttpMethod m_Method; + HttpVersion m_Version; + int m_VersionOffset; + int m_UriOffset; + int m_FirstLineEndOffset; + bool m_IsComplete; + HttpRequestFirstLineException m_Exception; +}; + +// -------- Class HttpResponseFirstLine ----------------- + +/** + * @class HttpResponseFirstLine + * Represents an HTTP response header first line. The first line includes 2 + * parameters: status code (e.g 200 OK, 404 Not Found, etc.), and HTTP version + * (e.g HTTP/1.1). These 2 parameters are included in this class, and the user + * can retrieve or set them. This class cannot be instantiated by users, it's + * created inside HttpResponseLayer and user can get a pointer to an instance of + * it. The "get" methods of this class will retrieve the actual data of the HTTP + * response and the "set" methods will change the packet data. Since HTTP is a + * textual protocol, most fields aren't of fixed size and this also applies to + * the first line parameters. So most "set" methods of this class need in most + * cases to shorten or extend the data in HttpResponseLayer. These methods will + * return a false value if this action failed + */ +class HttpResponseFirstLine { + friend class HttpResponseLayer; + + public: + /** + * @return The status code as HttpResponseStatusCode enum + */ + HttpResponseStatusCode getStatusCode() const { return m_StatusCode; } + + /** + * @return The status code number as integer (e.g 200, 404, etc.) + */ + int getStatusCodeAsInt() const; + + /** + * @return The status code message (e.g "OK", "Not Found", etc.) + */ + std::string getStatusCodeString() const; + + /** + * Set the status code + * @param[in] newStatusCode The new status code to set + * @param[in] statusCodeString An optional parameter: set a non-default status + * code message (e.g "Bla Bla" instead of "Not Found"). If this parameter + * isn't supplied or supplied as empty string (""), the default message for + * the status code will be set + * @return True if setting the status code was completed successfully, false + * otherwise + * @deprecated Use the other overload instead. + */ + PCPP_DEPRECATED bool + setStatusCode(const HttpResponseStatusCode& newStatusCode, + const std::string& statusCodeString); + + /** + * Set the status code + * @param[in] newStatusCode The new status code to set + * @return True if setting the status code was completed successfully, false + * otherwise + */ + bool setStatusCode(const HttpResponseStatusCode& newStatusCode); + + /** + * @return The HTTP version + */ + HttpVersion getVersion() const { return m_Version; } + + /** + * Set the HTTP version. This method doesn't return a value since all + * supported HTTP versions are of the same size (HTTP/0.9, HTTP/1.0, HTTP/1.1) + * @param[in] newVersion The HTTP version to set + */ + void setVersion(HttpVersion newVersion); + + /** + * A static method for parsing the HTTP status code out of raw data + * @param[in] data The raw data + * @param[in] dataLen The raw data length + * @return The parsed HTTP status code as enum + */ + static HttpResponseStatusCode parseStatusCode(const char* data, + size_t dataLen); + + /** + * A static method for parsing the HTTP version out of raw first line data + * (e.g "HTTP/x.y") + * @param[in] data The raw data + * @param[in] dataLen The raw data length + * @return The parsed HTTP status code as enum + */ + static HttpVersion parseVersion(const char* data, size_t dataLen); + + /** + * @return The size in bytes of the HTTP first line + */ + int getSize() const { return m_FirstLineEndOffset; } + + /** + * As explained in HttpResponseLayer, an HTTP header can spread over more than + * 1 packet, so when looking at a single packet the header can be partial. + * Same goes for the first line - it can spread over more than 1 packet. This + * method returns an indication whether the first line is partial + * @return False if the first line is partial, true if it's complete + */ + bool isComplete() const { return m_IsComplete; } + + /** + * @class HttpResponseFirstLineException + * This exception can be thrown while constructing HttpResponseFirstLine (the + * constructor is private, so the construction happens only in + * HttpResponseLayer). This kind of exception will be thrown if trying to + * construct with a HTTP status code that is not in HttpResponseStatusCode or + * with undefined HTTP version ::HttpVersionUnknown + */ + class HttpResponseFirstLineException : public std::exception { + public: + ~HttpResponseFirstLineException() throw() {} + void setMessage(const std::string& message) { m_Message = message; } + virtual const char* what() const throw() { return m_Message.c_str(); } + + private: + std::string m_Message; + }; + + private: + HttpResponseFirstLine(HttpResponseLayer* httpResponse); + HttpResponseFirstLine(HttpResponseLayer* httpResponse, HttpVersion version, + const HttpResponseStatusCode& statusCode); + + HttpResponseLayer* m_HttpResponse; + HttpVersion m_Version; + HttpResponseStatusCode m_StatusCode; + int m_FirstLineEndOffset; + bool m_IsComplete; + HttpResponseFirstLineException m_Exception; +}; } // namespace pcpp diff --git a/Packet++/header/IPLayer.h b/Packet++/header/IPLayer.h index f932e1b803..be87aba2ff 100644 --- a/Packet++/header/IPLayer.h +++ b/Packet++/header/IPLayer.h @@ -10,37 +10,38 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - /** - * @class IPLayer - * This is an interface (abstract class) implemented in the IP layers (IPv4Layer and IPv6Layer). - * It provides methods to fetch the source and destination IP addresses in an abdtract way - * that hides the IP type (IPv4 or IPv6). This is useful for use-cases in which the IP type doesn't matter. - * For example: if you're only interested in printing the IP address the IP type shouldn't matter. - */ - class IPLayer - { - protected: - IPLayer() {} - public: - /** - * An abstract method to get the source IP address - * @return An IPAddress object containing the source address - */ - virtual IPAddress getSrcIPAddress() const = 0; +namespace pcpp { +/** + * @class IPLayer + * This is an interface (abstract class) implemented in the IP layers (IPv4Layer + * and IPv6Layer). It provides methods to fetch the source and destination IP + * addresses in an abdtract way that hides the IP type (IPv4 or IPv6). This is + * useful for use-cases in which the IP type doesn't matter. For example: if + * you're only interested in printing the IP address the IP type shouldn't + * matter. + */ +class IPLayer { + protected: + IPLayer() {} + + public: + /** + * An abstract method to get the source IP address + * @return An IPAddress object containing the source address + */ + virtual IPAddress getSrcIPAddress() const = 0; - /** - * An abstract method to get the destination IP address - * @return An IPAddress object containing the destination address - */ - virtual IPAddress getDstIPAddress() const = 0; + /** + * An abstract method to get the destination IP address + * @return An IPAddress object containing the destination address + */ + virtual IPAddress getDstIPAddress() const = 0; - /** - * An empty destructor - */ - virtual ~IPLayer() {} - }; -} + /** + * An empty destructor + */ + virtual ~IPLayer() {} +}; +} // namespace pcpp #endif // PACKETPP_IP_LAYER diff --git a/Packet++/header/IPReassembly.h b/Packet++/header/IPReassembly.h index 7087ba5aac..a24c2dd670 100644 --- a/Packet++/header/IPReassembly.h +++ b/Packet++/header/IPReassembly.h @@ -1,461 +1,550 @@ #ifndef PACKETPP_IP_REASSEMBLY #define PACKETPP_IP_REASSEMBLY -#include "Packet.h" -#include "LRUList.h" #include "IpAddress.h" +#include "LRUList.h" +#include "Packet.h" #include "PointerVector.h" #include /** * @file - * This file includes an implementation of IP reassembly mechanism (a.k.a IP de-fragmentation), which is the mechanism of assembling IPv4 or IPv6 - * fragments back into one whole packet. As the previous sentence imply, this module supports both IPv4 and IPv6 reassembly which means - * the same pcpp#IPReassembly instance can reassemble both IPv4 and IPv6 fragments. You can read more about IP fragmentation here: + * This file includes an implementation of IP reassembly mechanism (a.k.a IP + * de-fragmentation), which is the mechanism of assembling IPv4 or IPv6 + * fragments back into one whole packet. As the previous sentence imply, this + * module supports both IPv4 and IPv6 reassembly which means the same + * pcpp#IPReassembly instance can reassemble both IPv4 and IPv6 fragments. You + * can read more about IP fragmentation here: * https://en.wikipedia.org/wiki/IP_fragmentation.
- * The API is rather simple and contains one main method: pcpp#IPReassembly#processPacket() which gets a fragment packet as a parameter, does the - * reassembly and returns a fully reassembled packet when done.
+ * The API is rather simple and contains one main method: + * pcpp#IPReassembly#processPacket() which gets a fragment packet as a + * parameter, does the reassembly and returns a fully reassembled packet when + * done.
* * The logic works as follows: - * - There is an internal map that stores the reassembly data for each packet. The key to this map, meaning the way to uniquely associate a - * fragment to a (reassembled) packet is the triplet of source IP, destination IP and IP ID (for IPv4) or Fragment ID (for IPv6) - * - When the first fragment arrives a new record is created in the map and the fragment data is copied - * - With each fragment arriving the fragment data is copied right after the previous fragment and the reassembled packet is gradually being built - * - When the last fragment arrives the packet is fully reassembled and returned to the user. Since all fragment data is copied, the packet pointer - * returned to the user has to be freed by the user when done using it - * - The logic supports out-of-order fragments, meaning that a fragment which arrives out-of-order, its data will be copied to a list of out-of-order - * fragments where it waits for its turn. This list is observed each time a new fragment arrives to see if the next fragment(s) wait(s) in this - * list + * - There is an internal map that stores the reassembly data for each packet. + * The key to this map, meaning the way to uniquely associate a fragment to a + * (reassembled) packet is the triplet of source IP, destination IP and IP ID + * (for IPv4) or Fragment ID (for IPv6) + * - When the first fragment arrives a new record is created in the map and the + * fragment data is copied + * - With each fragment arriving the fragment data is copied right after the + * previous fragment and the reassembled packet is gradually being built + * - When the last fragment arrives the packet is fully reassembled and returned + * to the user. Since all fragment data is copied, the packet pointer returned + * to the user has to be freed by the user when done using it + * - The logic supports out-of-order fragments, meaning that a fragment which + * arrives out-of-order, its data will be copied to a list of out-of-order + * fragments where it waits for its turn. This list is observed each time a + * new fragment arrives to see if the next fragment(s) wait(s) in this list * - If a non-IP packet arrives it's returned as is to the user * - If a non-fragment packet arrives it's returned as is to the user * - * In order to limit the amount of memory used by this mechanism there is a limit to the number of concurrent packets being reassembled. - * The default limit is #PCPP_IP_REASSEMBLY_DEFAULT_MAX_PACKETS_TO_STORE but the user can set any value (determined in pcpp#IPReassembly - * c'tor). Once capacity (the number of concurrent reassembled packets) exceeds this number, the packet that was least recently used will be - * dropped from the map along with all the data that was reassembled so far. This means that if the next fragment from this packet suddenly - * appears it will be treated as a new reassembled packet (which will create another record in the map). The user can be notified when - * reassembled packets are removed from the map by registering to the pcpp#IPReassembly#OnFragmentsClean callback in pcpp#IPReassembly c'tor + * In order to limit the amount of memory used by this mechanism there is a + * limit to the number of concurrent packets being reassembled. The default + * limit is #PCPP_IP_REASSEMBLY_DEFAULT_MAX_PACKETS_TO_STORE but the user can + * set any value (determined in pcpp#IPReassembly c'tor). Once capacity (the + * number of concurrent reassembled packets) exceeds this number, the packet + * that was least recently used will be dropped from the map along with all the + * data that was reassembled so far. This means that if the next fragment from + * this packet suddenly appears it will be treated as a new reassembled packet + * (which will create another record in the map). The user can be notified when + * reassembled packets are removed from the map by registering to the + * pcpp#IPReassembly#OnFragmentsClean callback in pcpp#IPReassembly c'tor */ /** * @namespace pcpp * @brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - - /** IP reassembly mechanism default capacity. If concurrent packet volume exceeds this numbers, packets will start to be dropped in - * a LRU manner - */ - #define PCPP_IP_REASSEMBLY_DEFAULT_MAX_PACKETS_TO_STORE 500000 - - /** - * @class IPReassembly - * Contains the IP reassembly (a.k.a IP de-fragmentation) mechanism. Encapsulates both IPv4 and IPv6 reassembly. - * Please refer to the documentation at the top of IPReassembly.h - * to understand how this mechanism works. The main APIs are: - * - IPReassembly#processPacket() - process a fragment. This is the main method which should be called whenever a new fragment arrives. - * This method processes the fragment, runs the reassembly logic and returns the result packet when it's fully reassembled - * - IPReassembly#getCurrentPacket() - get the reassembled data that is currently available, even if reassembly process is not yet completed - * - IPReassembly#removePacket() - remove all data that is currently stored for a packet, including the reassembled data that was gathered - * so far - */ - class IPReassembly - { - public: - - /** - * @class PacketKey - * An abstract class that represents a key that can uniquely identify an IP packet. This class cannot be instantiated or copied, - * only its derived classes can - */ - class PacketKey - { - public: - - /** - * A default virtual d'tor - */ - virtual ~PacketKey() = default; - - /** - * @return A 4-byte hash value of the packet key - */ - virtual uint32_t getHashValue() const = 0; - - /** - * @return The IP protocol this key represents (pcpp#IPv4 or pcpp#IPv6) - */ - virtual ProtocolType getProtocolType() const = 0; - - /** - * @return A pointer to a new instance which is a clone of the current instance - */ - virtual PacketKey* clone() const = 0; - - protected: - // private c'tor - PacketKey() = default; - - // private copy c'tor - PacketKey(const PacketKey& other) = default; - }; - - - /** - * @class IPv4PacketKey - * Represents a key that can uniquely identify IPv4 packets. The key comprises of source IPv4 address, dest IPv4 address and IP ID - */ - class IPv4PacketKey : public PacketKey - { - public: - - /** - * A default c'tor which zeros all members - */ - IPv4PacketKey() : m_IpID(0), m_SrcIP(IPv4Address::Zero), m_DstIP(IPv4Address::Zero) { } - - /** - * A c'tor that sets values in each one of the members - * @param[in] ipid IP ID value - * @param[in] srcip Source IPv4 address - * @param[in] dstip Dest IPv4 address - */ - IPv4PacketKey(uint16_t ipid, IPv4Address srcip, IPv4Address dstip) : m_IpID(ipid), m_SrcIP(srcip), m_DstIP(dstip) { } - - /** - * A copy c'tor for this class - * @param[in] other The instance to copy from - */ - IPv4PacketKey(const IPv4PacketKey& other) : PacketKey(other), m_IpID(other.m_IpID), m_SrcIP(other.m_SrcIP), m_DstIP(other.m_DstIP) { } - - /** - * Assignment operator for this class - * @param[in] other The instance to assign from - */ - IPv4PacketKey& operator=(const IPv4PacketKey& other) - { - m_IpID = other.m_IpID; - m_SrcIP = other.m_SrcIP; - m_DstIP = other.m_DstIP; - return *this; - } - - /** - * @return IP ID value - */ - uint16_t getIpID() const { return m_IpID; } - - /** - * @return Source IP address - */ - IPv4Address getSrcIP() const { return m_SrcIP; } - - /** - * @return Dest IP address - */ - IPv4Address getDstIP() const { return m_DstIP; } - - /** - * Set IP ID - * @param[in] ipID IP ID value to set - */ - void setIpID(uint16_t ipID) { m_IpID = ipID; } - - /** - * Set source IPv4 address - * @param[in] srcIP Source IP to set - */ - void setSrcIP(const IPv4Address& srcIP) { m_SrcIP = srcIP; } - - /** - * Set dest IPv4 address - * @param[in] dstIP Dest IP to set - */ - void setDstIP(const IPv4Address& dstIP) { m_DstIP = dstIP; } - - - // implement abstract methods - - uint32_t getHashValue() const; - - /** - * @return pcpp#IPv4 protocol - */ - ProtocolType getProtocolType() const { return IPv4; } - - PacketKey* clone() const { return new IPv4PacketKey(*this); } - - private: - uint16_t m_IpID; - IPv4Address m_SrcIP; - IPv4Address m_DstIP; - }; - - - /** - * @class IPv6PacketKey - * Represents a key that can uniquely identify IPv6 fragment packets. The key comprises of source IPv6 address, dest IPv6 address - * and fragment ID (which resides in the IPv6 fragmentation extension) - */ - class IPv6PacketKey : public PacketKey - { - public: - - /** - * A default c'tor which zeros all members - */ - IPv6PacketKey() : m_FragmentID(0), m_SrcIP(IPv6Address::Zero), m_DstIP(IPv6Address::Zero) { } - - /** - * A c'tor that sets values in each one of the members - * @param[in] fragmentID Fragment ID value - * @param[in] srcip Source IPv6 address - * @param[in] dstip Dest IPv6 address - */ - IPv6PacketKey(uint32_t fragmentID, IPv6Address srcip, IPv6Address dstip) : m_FragmentID(fragmentID), m_SrcIP(srcip), m_DstIP(dstip) { } - - /** - * A copy c'tor for this class - * @param[in] other The instance to copy from - */ - IPv6PacketKey(const IPv6PacketKey& other) : PacketKey(other), m_FragmentID(other.m_FragmentID), m_SrcIP(other.m_SrcIP), m_DstIP(other.m_DstIP) { } - - /** - * Assignment operator for this class - * @param[in] other The instance to assign from - */ - IPv6PacketKey& operator=(const IPv6PacketKey& other) - { - m_FragmentID = other.m_FragmentID; - m_SrcIP = other.m_SrcIP; - m_DstIP = other.m_DstIP; - return *this; - } - - /** - * @return Fragment ID value - */ - uint32_t getFragmentID() const { return m_FragmentID; } - - /** - * @return Source IP address - */ - IPv6Address getSrcIP() const { return m_SrcIP; } - - /** - * @return Dest IP address - */ - IPv6Address getDstIP() const { return m_DstIP; } - - /** - * Set fragment ID - * @param[in] fragID Fragment ID value to set - */ - void setFragmentID(uint32_t fragID) { m_FragmentID = fragID; } - - /** - * Set source IPv6 address - * @param[in] srcIP Source IP to set - */ - void setSrcIP(const IPv6Address& srcIP) { m_SrcIP = srcIP; } - - /** - * Set dest IPv6 address - * @param[in] dstIP Dest IP to set - */ - void setDstIP(const IPv6Address& dstIP) { m_DstIP = dstIP; } - - - // implement abstract methods - - uint32_t getHashValue() const; - - /** - * @return pcpp#IPv6 protocol - */ - ProtocolType getProtocolType() const { return IPv6; } - - PacketKey* clone() const { return new IPv6PacketKey(*this); } - - private: - uint32_t m_FragmentID; - IPv6Address m_SrcIP; - IPv6Address m_DstIP; - }; - - - /** - * @typedef OnFragmentsClean - * The IP reassembly mechanism has a certain capacity of concurrent packets it can handle. This capacity is determined in its c'tor - * (default value is #PCPP_IP_REASSEMBLY_DEFAULT_MAX_PACKETS_TO_STORE). When traffic volume exceeds this capacity the mechanism starts - * dropping packets in a LRU manner (least recently used are dropped first). Whenever a packet is dropped this callback is fired - * @param[in] key A pointer to the identifier of the packet that is being dropped - * @param[in] userCookie A pointer to the cookie provided by the user in IPReassemby c'tor (or NULL if no cookie provided) - */ - typedef void (*OnFragmentsClean)(const PacketKey* key, void* userCookie); - - /** - * An enum representing the status returned from processing a fragment - */ - enum ReassemblyStatus - { - /** The processed packet isn't of type IPv4 or IPv6 */ - NON_IP_PACKET = 0x00, - /** The processed packet isn't a fragment */ - NON_FRAGMENT = 0x01, - /** The processed fragment is the first fragment */ - FIRST_FRAGMENT = 0x02, - /** The processed fragment is a fragment (but not the first one) */ - FRAGMENT = 0x04, - /** The processed fragment is not the fragment that was expected at this time */ - OUT_OF_ORDER_FRAGMENT = 0x08, - /** The processed fragment is malformed, meaning a fragment which has offset of zero but isn't the first fragment */ - MALFORMED_FRAGMENT = 0x10, - /** Packet is now fully reassembled */ - REASSEMBLED = 0x20 - }; - - /** - * A c'tor for this class. - * @param[in] onFragmentsCleanCallback The callback to be called when packets are dropped due to capacity limit. - * Please read more about capacity limit in IPReassembly.h file description. This parameter is optional, default value is NULL (no callback) - * @param[in] callbackUserCookie A pointer to an object provided by the user. This pointer will be returned when invoking the - * onFragmentsCleanCallback. This parameter is optional, default cookie is NULL - * @param[in] maxPacketsToStore Set the capacity limit of the IP reassembly mechanism. Default capacity is #PCPP_IP_REASSEMBLY_DEFAULT_MAX_PACKETS_TO_STORE - */ - explicit IPReassembly(OnFragmentsClean onFragmentsCleanCallback = NULL, void *callbackUserCookie = NULL, size_t maxPacketsToStore = PCPP_IP_REASSEMBLY_DEFAULT_MAX_PACKETS_TO_STORE) - : m_PacketLRU(maxPacketsToStore), m_OnFragmentsCleanCallback(onFragmentsCleanCallback), m_CallbackUserCookie(callbackUserCookie) {} - - /** - * A d'tor for this class - */ - ~IPReassembly(); - - /** - * The main API that drives IPReassembly. This method should be called whenever a fragment arrives. This method finds the relevant - * packet this fragment belongs to and runs the IP reassembly logic that is described in IPReassembly.h. - * @param[in] fragment The fragment to process (IPv4 or IPv6). Please notice that the reassembly logic doesn't change or manipulate - * this object in any way. All of its data is copied to internal structures and manipulated there - * @param[out] status An indication of the packet reassembly status following the processing of this fragment. Possible values are: - * - The input fragment is not a IPv4 or IPv6 packet - * - The input fragment is not a IPv4 or IPv6 fragment packet - * - The input fragment is the first fragment of the packet - * - The input fragment is not the first or last fragment - * - The input fragment came out-of-order, meaning that wasn't the fragment that was currently expected (it's data is copied to - * the out-of-order fragment list) - * - The input fragment is malformed and will be ignored - * - The input fragment is the last one and the packet is now fully reassembled. In this case the return value will contain - * a pointer to the reassembled packet - * @param[in] parseUntil Optional parameter. Parse the reassembled packet until you reach a certain protocol (inclusive). Can be useful for cases when you need to parse only up to a - * certain layer and want to avoid the performance impact and memory consumption of parsing the whole packet. Note that setting this to a protocol which doesn't - * include the IP-Layer will result in IPReassembly not finding the IP-Layer and thus failing to work properly. Default value is ::UnknownProtocol which means - * don't take this parameter into account - * @param[in] parseUntilLayer Optional parameter. Parse the reassembled packet until you reach a certain layer in the OSI model (inclusive). Can be useful for cases when you need to - * parse only up to a certain OSI layer (for example transport layer) and want to avoid the performance impact and memory consumption of parsing the whole packet. - * Note that setting this value to OsiModelPhysicalLayer will result in IPReassembly not finding the IP-layer and thus failing to work properly. - * Default value is ::OsiModelLayerUnknown which means don't take this parameter into account - * @return - * - If the input fragment isn't an IPv4/IPv6 packet or if it isn't an IPv4/IPv6 fragment, the return value is a pointer to the input fragment - * - If the input fragment is the last one and the reassembled packet is ready - a pointer to the reassembled packet is - * returned. Notice it's the user's responsibility to free this pointer when done using it - * - If the reassembled packet isn't ready then NULL is returned - */ - Packet* processPacket(Packet* fragment, ReassemblyStatus& status, ProtocolType parseUntil = UnknownProtocol, OsiModelLayer parseUntilLayer = OsiModelLayerUnknown); - - /** - * The main API that drives IPReassembly. This method should be called whenever a fragment arrives. This method finds the relevant - * packet this fragment belongs to and runs the IPv4 reassembly logic that is described in IPReassembly.h. - * @param[in] fragment The fragment to process (IPv4 or IPv6). Please notice that the reassembly logic doesn't change or manipulate - * this object in any way. All of its data is copied to internal structures and manipulated there - * @param[out] status An indication of the packet reassembly status following the processing of this fragment. Possible values are: - * - The input fragment is not a IPv4 or IPv6 packet - * - The input fragment is not a IPv4 or IPv6 fragment packet - * - The input fragment is the first fragment of the packet - * - The input fragment is not the first or last fragment - * - The input fragment came out-of-order, meaning that wasn't the fragment that was currently expected (it's data is copied to - * the out-of-order fragment list) - * - The input fragment is malformed and will be ignored - * - The input fragment is the last one and the packet is now fully reassembled. In this case the return value will contain - * a pointer to the reassembled packet - * @param[in] parseUntil Optional parameter. Parse the raw and reassembled packets until you reach a certain protocol (inclusive). Can be useful for cases when you need to parse only up to a - * certain layer and want to avoid the performance impact and memory consumption of parsing the whole packet. Note that setting this to a protocol which doesn't - * include the IP-Layer will result in IPReassembly not finding the IP-Layer and thus failing to work properly. Default value is ::UnknownProtocol which means - * don't take this parameter into account - * @param[in] parseUntilLayer Optional parameter. Parse the raw and reassembled packets until you reach a certain layer in the OSI model (inclusive). Can be useful for cases when you need to - * parse only up to a certain OSI layer (for example transport layer) and want to avoid the performance impact and memory consumption of parsing the whole packet. - * Note that setting this value to OsiModelPhysicalLayer will result in IPReassembly not finding the IP-layer and thus failing to work properly. - *Default value is ::UnknownProtocol which means don't take this parameter into account - * Default value is ::OsiModelLayerUnknown which means don't take this parameter into account - * @return - * - If the input fragment isn't an IPv4/IPv6 packet or if it isn't an IPv4/IPv6 fragment, the return value is a pointer to a Packet object - * wrapping the input fragment RawPacket object. It's the user responsibility to free this instance - * - If the input fragment is the last one and the reassembled packet is ready - a pointer to the reassembled packet is - * returned. Notice it's the user's responsibility to free this pointer when done using it - * - If the reassembled packet isn't ready then NULL is returned - */ - Packet* processPacket(RawPacket* fragment, ReassemblyStatus& status, ProtocolType parseUntil = UnknownProtocol, OsiModelLayer parseUntilLayer = OsiModelLayerUnknown); - - /** - * Get a partially reassembled packet. This method returns all the reassembled data that was gathered so far which is obviously not - * a fully reassembled packet (otherwise it would have returned by processPacket()). Notice all data is being copied so the user is - * responsible to free the returned Packet object when done using it. Notice#2 - calling this method doesn't interfere with the - * reassembly of this packet - all internal structures and data remain - * @param[in] key The identifiers of the packet to return - * @return A pointer to a Packet object containing the partially reassembled packet. Notice the user is responsible to free this - * object when done using it - */ - Packet* getCurrentPacket(const PacketKey& key); - - /** - * Remove a partially reassembled packet from all internal structures. That means that if another fragment of this packet appears - * it will be treated as a new packet - * @param[in] key The identifiers of the packet to remove - */ - void removePacket(const PacketKey& key); - - /** - * Get the maximum capacity as determined in the c'tor - */ - size_t getMaxCapacity() const { return m_PacketLRU.getMaxSize(); } - - /** - * Get the current number of packets being processed - */ - size_t getCurrentCapacity() const { return m_FragmentMap.size(); } - - private: - - struct IPFragment - { - uint16_t fragmentOffset; - bool lastFragment; - uint8_t* fragmentData; - size_t fragmentDataLen; - IPFragment() { fragmentOffset = 0; lastFragment = false; fragmentData = NULL; fragmentDataLen = 0; } - ~IPFragment() { delete [] fragmentData; } - }; - - struct IPFragmentData - { - uint16_t currentOffset; - RawPacket* data; - bool deleteData; - uint32_t fragmentID; - PacketKey* packetKey; - PointerVector outOfOrderFragments; - IPFragmentData(PacketKey* pktKey, uint32_t fragId) { currentOffset = 0; data = NULL; deleteData = true; fragmentID = fragId; packetKey = pktKey; } - ~IPFragmentData() { delete packetKey; if (deleteData && data != NULL) { delete data; } } - }; - - LRUList m_PacketLRU; - std::map m_FragmentMap; - OnFragmentsClean m_OnFragmentsCleanCallback; - void* m_CallbackUserCookie; - - void addNewFragment(uint32_t hash, IPFragmentData* fragData); - bool matchOutOfOrderFragments(IPFragmentData* fragData); - }; +namespace pcpp { + +/** IP reassembly mechanism default capacity. If concurrent packet volume + * exceeds this numbers, packets will start to be dropped in a LRU manner + */ +#define PCPP_IP_REASSEMBLY_DEFAULT_MAX_PACKETS_TO_STORE 500000 + +/** + * @class IPReassembly + * Contains the IP reassembly (a.k.a IP de-fragmentation) mechanism. + * Encapsulates both IPv4 and IPv6 reassembly. Please refer to the documentation + * at the top of IPReassembly.h to understand how this mechanism works. The main + * APIs are: + * - IPReassembly#processPacket() - process a fragment. This is the main method + * which should be called whenever a new fragment arrives. This method processes + * the fragment, runs the reassembly logic and returns the result packet when + * it's fully reassembled + * - IPReassembly#getCurrentPacket() - get the reassembled data that is + * currently available, even if reassembly process is not yet completed + * - IPReassembly#removePacket() - remove all data that is currently stored for + * a packet, including the reassembled data that was gathered so far + */ +class IPReassembly { + public: + /** + * @class PacketKey + * An abstract class that represents a key that can uniquely identify an IP + * packet. This class cannot be instantiated or copied, only its derived + * classes can + */ + class PacketKey { + public: + /** + * A default virtual d'tor + */ + virtual ~PacketKey() = default; + + /** + * @return A 4-byte hash value of the packet key + */ + virtual uint32_t getHashValue() const = 0; + + /** + * @return The IP protocol this key represents (pcpp#IPv4 or pcpp#IPv6) + */ + virtual ProtocolType getProtocolType() const = 0; + + /** + * @return A pointer to a new instance which is a clone of the current + * instance + */ + virtual PacketKey* clone() const = 0; + + protected: + // private c'tor + PacketKey() = default; + + // private copy c'tor + PacketKey(const PacketKey& other) = default; + }; + + /** + * @class IPv4PacketKey + * Represents a key that can uniquely identify IPv4 packets. The key comprises + * of source IPv4 address, dest IPv4 address and IP ID + */ + class IPv4PacketKey : public PacketKey { + public: + /** + * A default c'tor which zeros all members + */ + IPv4PacketKey() + : m_IpID(0), m_SrcIP(IPv4Address::Zero), m_DstIP(IPv4Address::Zero) {} + + /** + * A c'tor that sets values in each one of the members + * @param[in] ipid IP ID value + * @param[in] srcip Source IPv4 address + * @param[in] dstip Dest IPv4 address + */ + IPv4PacketKey(uint16_t ipid, IPv4Address srcip, IPv4Address dstip) + : m_IpID(ipid), m_SrcIP(srcip), m_DstIP(dstip) {} + + /** + * A copy c'tor for this class + * @param[in] other The instance to copy from + */ + IPv4PacketKey(const IPv4PacketKey& other) + : PacketKey(other), m_IpID(other.m_IpID), m_SrcIP(other.m_SrcIP), + m_DstIP(other.m_DstIP) {} + + /** + * Assignment operator for this class + * @param[in] other The instance to assign from + */ + IPv4PacketKey& operator=(const IPv4PacketKey& other) { + m_IpID = other.m_IpID; + m_SrcIP = other.m_SrcIP; + m_DstIP = other.m_DstIP; + return *this; + } + + /** + * @return IP ID value + */ + uint16_t getIpID() const { return m_IpID; } + + /** + * @return Source IP address + */ + IPv4Address getSrcIP() const { return m_SrcIP; } + + /** + * @return Dest IP address + */ + IPv4Address getDstIP() const { return m_DstIP; } + + /** + * Set IP ID + * @param[in] ipID IP ID value to set + */ + void setIpID(uint16_t ipID) { m_IpID = ipID; } + + /** + * Set source IPv4 address + * @param[in] srcIP Source IP to set + */ + void setSrcIP(const IPv4Address& srcIP) { m_SrcIP = srcIP; } + + /** + * Set dest IPv4 address + * @param[in] dstIP Dest IP to set + */ + void setDstIP(const IPv4Address& dstIP) { m_DstIP = dstIP; } + + // implement abstract methods + + uint32_t getHashValue() const; + + /** + * @return pcpp#IPv4 protocol + */ + ProtocolType getProtocolType() const { return IPv4; } + + PacketKey* clone() const { return new IPv4PacketKey(*this); } + + private: + uint16_t m_IpID; + IPv4Address m_SrcIP; + IPv4Address m_DstIP; + }; + + /** + * @class IPv6PacketKey + * Represents a key that can uniquely identify IPv6 fragment packets. The key + * comprises of source IPv6 address, dest IPv6 address and fragment ID (which + * resides in the IPv6 fragmentation extension) + */ + class IPv6PacketKey : public PacketKey { + public: + /** + * A default c'tor which zeros all members + */ + IPv6PacketKey() + : m_FragmentID(0), m_SrcIP(IPv6Address::Zero), + m_DstIP(IPv6Address::Zero) {} + + /** + * A c'tor that sets values in each one of the members + * @param[in] fragmentID Fragment ID value + * @param[in] srcip Source IPv6 address + * @param[in] dstip Dest IPv6 address + */ + IPv6PacketKey(uint32_t fragmentID, IPv6Address srcip, IPv6Address dstip) + : m_FragmentID(fragmentID), m_SrcIP(srcip), m_DstIP(dstip) {} + + /** + * A copy c'tor for this class + * @param[in] other The instance to copy from + */ + IPv6PacketKey(const IPv6PacketKey& other) + : PacketKey(other), m_FragmentID(other.m_FragmentID), + m_SrcIP(other.m_SrcIP), m_DstIP(other.m_DstIP) {} + + /** + * Assignment operator for this class + * @param[in] other The instance to assign from + */ + IPv6PacketKey& operator=(const IPv6PacketKey& other) { + m_FragmentID = other.m_FragmentID; + m_SrcIP = other.m_SrcIP; + m_DstIP = other.m_DstIP; + return *this; + } + + /** + * @return Fragment ID value + */ + uint32_t getFragmentID() const { return m_FragmentID; } + + /** + * @return Source IP address + */ + IPv6Address getSrcIP() const { return m_SrcIP; } + + /** + * @return Dest IP address + */ + IPv6Address getDstIP() const { return m_DstIP; } + + /** + * Set fragment ID + * @param[in] fragID Fragment ID value to set + */ + void setFragmentID(uint32_t fragID) { m_FragmentID = fragID; } + + /** + * Set source IPv6 address + * @param[in] srcIP Source IP to set + */ + void setSrcIP(const IPv6Address& srcIP) { m_SrcIP = srcIP; } + + /** + * Set dest IPv6 address + * @param[in] dstIP Dest IP to set + */ + void setDstIP(const IPv6Address& dstIP) { m_DstIP = dstIP; } + + // implement abstract methods + + uint32_t getHashValue() const; + + /** + * @return pcpp#IPv6 protocol + */ + ProtocolType getProtocolType() const { return IPv6; } + + PacketKey* clone() const { return new IPv6PacketKey(*this); } + + private: + uint32_t m_FragmentID; + IPv6Address m_SrcIP; + IPv6Address m_DstIP; + }; + + /** + * @typedef OnFragmentsClean + * The IP reassembly mechanism has a certain capacity of concurrent packets it + * can handle. This capacity is determined in its c'tor (default value is + * #PCPP_IP_REASSEMBLY_DEFAULT_MAX_PACKETS_TO_STORE). When traffic volume + * exceeds this capacity the mechanism starts dropping packets in a LRU manner + * (least recently used are dropped first). Whenever a packet is dropped this + * callback is fired + * @param[in] key A pointer to the identifier of the packet that is being + * dropped + * @param[in] userCookie A pointer to the cookie provided by the user in + * IPReassemby c'tor (or NULL if no cookie provided) + */ + typedef void (*OnFragmentsClean)(const PacketKey* key, void* userCookie); + + /** + * An enum representing the status returned from processing a fragment + */ + enum ReassemblyStatus { + /** The processed packet isn't of type IPv4 or IPv6 */ + NON_IP_PACKET = 0x00, + /** The processed packet isn't a fragment */ + NON_FRAGMENT = 0x01, + /** The processed fragment is the first fragment */ + FIRST_FRAGMENT = 0x02, + /** The processed fragment is a fragment (but not the first one) */ + FRAGMENT = 0x04, + /** The processed fragment is not the fragment that was expected at this + time */ + OUT_OF_ORDER_FRAGMENT = 0x08, + /** The processed fragment is malformed, meaning a fragment which has offset + of zero but isn't the first fragment */ + MALFORMED_FRAGMENT = 0x10, + /** Packet is now fully reassembled */ + REASSEMBLED = 0x20 + }; + + /** + * A c'tor for this class. + * @param[in] onFragmentsCleanCallback The callback to be called when packets + * are dropped due to capacity limit. Please read more about capacity limit in + * IPReassembly.h file description. This parameter is optional, default value + * is NULL (no callback) + * @param[in] callbackUserCookie A pointer to an object provided by the user. + * This pointer will be returned when invoking the onFragmentsCleanCallback. + * This parameter is optional, default cookie is NULL + * @param[in] maxPacketsToStore Set the capacity limit of the IP reassembly + * mechanism. Default capacity is + * #PCPP_IP_REASSEMBLY_DEFAULT_MAX_PACKETS_TO_STORE + */ + explicit IPReassembly(OnFragmentsClean onFragmentsCleanCallback = NULL, + void* callbackUserCookie = NULL, + size_t maxPacketsToStore = + PCPP_IP_REASSEMBLY_DEFAULT_MAX_PACKETS_TO_STORE) + : m_PacketLRU(maxPacketsToStore), + m_OnFragmentsCleanCallback(onFragmentsCleanCallback), + m_CallbackUserCookie(callbackUserCookie) {} + + /** + * A d'tor for this class + */ + ~IPReassembly(); + + /** + * The main API that drives IPReassembly. This method should be called + * whenever a fragment arrives. This method finds the relevant packet this + * fragment belongs to and runs the IP reassembly logic that is described in + * IPReassembly.h. + * @param[in] fragment The fragment to process (IPv4 or IPv6). Please notice + * that the reassembly logic doesn't change or manipulate this object in any + * way. All of its data is copied to internal structures and manipulated there + * @param[out] status An indication of the packet reassembly status following + * the processing of this fragment. Possible values are: + * - The input fragment is not a IPv4 or IPv6 packet + * - The input fragment is not a IPv4 or IPv6 fragment packet + * - The input fragment is the first fragment of the packet + * - The input fragment is not the first or last fragment + * - The input fragment came out-of-order, meaning that wasn't the fragment + * that was currently expected (it's data is copied to the out-of-order + * fragment list) + * - The input fragment is malformed and will be ignored + * - The input fragment is the last one and the packet is now fully + * reassembled. In this case the return value will contain a pointer to the + * reassembled packet + * @param[in] parseUntil Optional parameter. Parse the reassembled packet + * until you reach a certain protocol (inclusive). Can be useful for cases + * when you need to parse only up to a certain layer and want to avoid the + * performance impact and memory consumption of parsing the whole packet. Note + * that setting this to a protocol which doesn't include the IP-Layer will + * result in IPReassembly not finding the IP-Layer and thus failing to work + * properly. Default value is ::UnknownProtocol which means don't take this + * parameter into account + * @param[in] parseUntilLayer Optional parameter. Parse the reassembled packet + * until you reach a certain layer in the OSI model (inclusive). Can be useful + * for cases when you need to parse only up to a certain OSI layer (for + * example transport layer) and want to avoid the performance impact and + * memory consumption of parsing the whole packet. Note that setting this + * value to OsiModelPhysicalLayer will result in IPReassembly not finding the + * IP-layer and thus failing to work properly. Default value is + * ::OsiModelLayerUnknown which means don't take this parameter into account + * @return + * - If the input fragment isn't an IPv4/IPv6 packet or if it isn't an + * IPv4/IPv6 fragment, the return value is a pointer to the input fragment + * - If the input fragment is the last one and the reassembled packet is ready + * - a pointer to the reassembled packet is returned. Notice it's the user's + * responsibility to free this pointer when done using it + * - If the reassembled packet isn't ready then NULL is returned + */ + Packet* processPacket(Packet* fragment, ReassemblyStatus& status, + ProtocolType parseUntil = UnknownProtocol, + OsiModelLayer parseUntilLayer = OsiModelLayerUnknown); + + /** + * The main API that drives IPReassembly. This method should be called + *whenever a fragment arrives. This method finds the relevant packet this + *fragment belongs to and runs the IPv4 reassembly logic that is described in + *IPReassembly.h. + * @param[in] fragment The fragment to process (IPv4 or IPv6). Please notice + *that the reassembly logic doesn't change or manipulate this object in any + *way. All of its data is copied to internal structures and manipulated there + * @param[out] status An indication of the packet reassembly status following + *the processing of this fragment. Possible values are: + * - The input fragment is not a IPv4 or IPv6 packet + * - The input fragment is not a IPv4 or IPv6 fragment packet + * - The input fragment is the first fragment of the packet + * - The input fragment is not the first or last fragment + * - The input fragment came out-of-order, meaning that wasn't the fragment + *that was currently expected (it's data is copied to the out-of-order + *fragment list) + * - The input fragment is malformed and will be ignored + * - The input fragment is the last one and the packet is now fully + *reassembled. In this case the return value will contain a pointer to the + *reassembled packet + * @param[in] parseUntil Optional parameter. Parse the raw and reassembled + *packets until you reach a certain protocol (inclusive). Can be useful for + *cases when you need to parse only up to a certain layer and want to avoid + *the performance impact and memory consumption of parsing the whole packet. + *Note that setting this to a protocol which doesn't include the IP-Layer will + *result in IPReassembly not finding the IP-Layer and thus failing to work + *properly. Default value is ::UnknownProtocol which means don't take this + *parameter into account + * @param[in] parseUntilLayer Optional parameter. Parse the raw and + *reassembled packets until you reach a certain layer in the OSI model + *(inclusive). Can be useful for cases when you need to parse only up to a + *certain OSI layer (for example transport layer) and want to avoid the + *performance impact and memory consumption of parsing the whole packet. Note + *that setting this value to OsiModelPhysicalLayer will result in IPReassembly + *not finding the IP-layer and thus failing to work properly. Default value is + *::UnknownProtocol which means don't take this parameter into account Default + *value is ::OsiModelLayerUnknown which means don't take this parameter into + *account + * @return + * - If the input fragment isn't an IPv4/IPv6 packet or if it isn't an + *IPv4/IPv6 fragment, the return value is a pointer to a Packet object + * wrapping the input fragment RawPacket object. It's the user + *responsibility to free this instance + * - If the input fragment is the last one and the reassembled packet is ready + *- a pointer to the reassembled packet is returned. Notice it's the user's + *responsibility to free this pointer when done using it + * - If the reassembled packet isn't ready then NULL is returned + */ + Packet* processPacket(RawPacket* fragment, ReassemblyStatus& status, + ProtocolType parseUntil = UnknownProtocol, + OsiModelLayer parseUntilLayer = OsiModelLayerUnknown); + + /** + * Get a partially reassembled packet. This method returns all the reassembled + * data that was gathered so far which is obviously not a fully reassembled + * packet (otherwise it would have returned by processPacket()). Notice all + * data is being copied so the user is responsible to free the returned Packet + * object when done using it. Notice#2 - calling this method doesn't interfere + * with the reassembly of this packet - all internal structures and data + * remain + * @param[in] key The identifiers of the packet to return + * @return A pointer to a Packet object containing the partially reassembled + * packet. Notice the user is responsible to free this object when done using + * it + */ + Packet* getCurrentPacket(const PacketKey& key); + + /** + * Remove a partially reassembled packet from all internal structures. That + * means that if another fragment of this packet appears it will be treated as + * a new packet + * @param[in] key The identifiers of the packet to remove + */ + void removePacket(const PacketKey& key); + + /** + * Get the maximum capacity as determined in the c'tor + */ + size_t getMaxCapacity() const { return m_PacketLRU.getMaxSize(); } + + /** + * Get the current number of packets being processed + */ + size_t getCurrentCapacity() const { return m_FragmentMap.size(); } + + private: + struct IPFragment { + uint16_t fragmentOffset; + bool lastFragment; + uint8_t* fragmentData; + size_t fragmentDataLen; + IPFragment() { + fragmentOffset = 0; + lastFragment = false; + fragmentData = NULL; + fragmentDataLen = 0; + } + ~IPFragment() { delete[] fragmentData; } + }; + + struct IPFragmentData { + uint16_t currentOffset; + RawPacket* data; + bool deleteData; + uint32_t fragmentID; + PacketKey* packetKey; + PointerVector outOfOrderFragments; + IPFragmentData(PacketKey* pktKey, uint32_t fragId) { + currentOffset = 0; + data = NULL; + deleteData = true; + fragmentID = fragId; + packetKey = pktKey; + } + ~IPFragmentData() { + delete packetKey; + if (deleteData && data != NULL) { + delete data; + } + } + }; + + LRUList m_PacketLRU; + std::map m_FragmentMap; + OnFragmentsClean m_OnFragmentsCleanCallback; + void* m_CallbackUserCookie; + + void addNewFragment(uint32_t hash, IPFragmentData* fragData); + bool matchOutOfOrderFragments(IPFragmentData* fragData); +}; } // namespace pcpp diff --git a/Packet++/header/IPSecLayer.h b/Packet++/header/IPSecLayer.h index bdc09c4b14..61f471ee8a 100644 --- a/Packet++/header/IPSecLayer.h +++ b/Packet++/header/IPSecLayer.h @@ -9,204 +9,212 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - /** - * @struct ipsec_authentication_header - * Represents IPSec AuthenticationHeader (AH) structure - */ +namespace pcpp { +/** + * @struct ipsec_authentication_header + * Represents IPSec AuthenticationHeader (AH) structure + */ #pragma pack(push, 1) - struct ipsec_authentication_header - { - /** Type of the next header */ - uint8_t nextHeader; - /** The length of the Authentication Header in 4-octet units, minus 2 */ - uint8_t payloadLen; - /** Reserved */ - uint16_t reserved; - /** Security Parameters Index */ - uint32_t spi; - /** Sequence Number */ - uint32_t sequenceNumber; - }; +struct ipsec_authentication_header { + /** Type of the next header */ + uint8_t nextHeader; + /** The length of the Authentication Header in 4-octet units, minus 2 */ + uint8_t payloadLen; + /** Reserved */ + uint16_t reserved; + /** Security Parameters Index */ + uint32_t spi; + /** Sequence Number */ + uint32_t sequenceNumber; +}; #pragma pack(pop) - /** - * @struct ipsec_esp - * Represents IPSec Encapsulating Security Payload (ESP) structure - */ +/** + * @struct ipsec_esp + * Represents IPSec Encapsulating Security Payload (ESP) structure + */ #pragma pack(push, 1) - struct ipsec_esp - { - /** Security Parameters Index */ - uint32_t spi; - /** Sequence Number */ - uint32_t sequenceNumber; - }; +struct ipsec_esp { + /** Security Parameters Index */ + uint32_t spi; + /** Sequence Number */ + uint32_t sequenceNumber; +}; #pragma pack(pop) - /** - * @class AuthenticationHeaderLayer - * Represents an IPSec AuthenticationHeader (AH) layer - */ - class AuthenticationHeaderLayer : public Layer - { - public: - /** 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 - */ - AuthenticationHeaderLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) { m_Protocol = AuthenticationHeader; } - - /** - * Get a pointer to the raw AH header. Notice this points directly to the data, so every change will change the actual packet data - * @return A pointer to the ipsec_authentication_header - */ - ipsec_authentication_header* getAHHeader() const { return (ipsec_authentication_header*)m_Data; } - - /** - * @return The Security Parameters Index (SPI) field value - */ - uint32_t getSPI() const; - - /** - * @return The sequence number value - */ - uint32_t getSequenceNumber() const; - - /** - * @return The size of the Integrity Check Value (ICV) - */ - size_t getICVLength() const; - - /** - * @return A pointer to the raw data of the Integrity Check Value (ICV) - */ - uint8_t* getICVBytes() const; - - /** - * @return The value of the Integrity Check Value (ICV) as a hex string - */ - std::string getICVHexStream() const; - - /** - * A static method that validates the input data - * @param[in] data The pointer to the beginning of a byte stream of a AuthenticationHeader layer - * @param[in] dataLen The length of byte stream - * @return True if the data is valid and can represent an AuthenticationHeader layer - */ - static inline bool isDataValid(const uint8_t* data, size_t dataLen); - - // implement abstract methods - - /** - * @return The size of the AH header - */ - size_t getHeaderLen() const { return 4*(getAHHeader()->payloadLen + 2); } - - /** - * Currently identifies the following next layers: UdpLayer, TcpLayer, IPv4Layer, IPv6Layer and ESPLayer. Otherwise sets PayloadLayer - */ - void parseNextLayer(); - - /** - * Does nothing for this layer - */ - void computeCalculateFields() {} - - std::string toString() const; - - OsiModelLayer getOsiModelLayer() const { return OsiModelNetworkLayer; } - - private: - // this layer supports parsing only - AuthenticationHeaderLayer() {} - }; - - - - /** - * @class ESPLayer - * Represents an IPSec Encapsulating Security Payload (ESP) layer - */ - class ESPLayer : public Layer - { - public: - /** 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 - */ - ESPLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) { m_Protocol = ESP; } - - ipsec_esp* getESPHeader() const { return (ipsec_esp*)m_Data; } - - /** - * @return The Security Parameters Index (SPI) field value - */ - uint32_t getSPI() const; - - /** - * @return The sequence number value - */ - uint32_t getSequenceNumber() const; - - /** - * A static method that validates the input data - * @param[in] data The pointer to the beginning of a byte stream of a ESP layer - * @param[in] dataLen The length of byte stream - * @return True if the data is valid and can represent an ESP layer - */ - static inline bool isDataValid(const uint8_t* data, size_t dataLen); - - // implement abstract methods - - /** - * @return The size of the ESP header (8 bytes) - */ - size_t getHeaderLen() const { return sizeof(ipsec_esp); } - - /** - * The payload of an ESP layer is encrypted, hence the next layer is always a generic payload (PayloadLayer) - */ - void parseNextLayer(); - - /** - * Does nothing for this layer - */ - void computeCalculateFields() {} - - std::string toString() const; - - OsiModelLayer getOsiModelLayer() const { return OsiModelTransportLayer; } - - private: - // this layer supports parsing only - ESPLayer() {} - }; - - - // implementation of inline methods - - bool AuthenticationHeaderLayer::isDataValid(const uint8_t* data, size_t dataLen) - { - if (dataLen < sizeof(ipsec_authentication_header)) - return false; - - size_t payloadLen = 4 * (data[1] + 2); - if (payloadLen < sizeof(ipsec_authentication_header) || payloadLen > dataLen) - return false; - - return true; - } - - bool ESPLayer::isDataValid(const uint8_t* data, size_t dataLen) - { - return data && dataLen >= sizeof(ipsec_esp); - } +/** + * @class AuthenticationHeaderLayer + * Represents an IPSec AuthenticationHeader (AH) layer + */ +class AuthenticationHeaderLayer : public Layer { + public: + /** 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 + */ + AuthenticationHeaderLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet) + : Layer(data, dataLen, prevLayer, packet) { + m_Protocol = AuthenticationHeader; + } + + /** + * Get a pointer to the raw AH header. Notice this points directly to the + * data, so every change will change the actual packet data + * @return A pointer to the ipsec_authentication_header + */ + ipsec_authentication_header* getAHHeader() const { + return (ipsec_authentication_header*)m_Data; + } + + /** + * @return The Security Parameters Index (SPI) field value + */ + uint32_t getSPI() const; + + /** + * @return The sequence number value + */ + uint32_t getSequenceNumber() const; + + /** + * @return The size of the Integrity Check Value (ICV) + */ + size_t getICVLength() const; + + /** + * @return A pointer to the raw data of the Integrity Check Value (ICV) + */ + uint8_t* getICVBytes() const; + + /** + * @return The value of the Integrity Check Value (ICV) as a hex string + */ + std::string getICVHexStream() const; + + /** + * A static method that validates the input data + * @param[in] data The pointer to the beginning of a byte stream of a + * AuthenticationHeader layer + * @param[in] dataLen The length of byte stream + * @return True if the data is valid and can represent an AuthenticationHeader + * layer + */ + static inline bool isDataValid(const uint8_t* data, size_t dataLen); + + // implement abstract methods + + /** + * @return The size of the AH header + */ + size_t getHeaderLen() const { return 4 * (getAHHeader()->payloadLen + 2); } + + /** + * Currently identifies the following next layers: UdpLayer, TcpLayer, + * IPv4Layer, IPv6Layer and ESPLayer. Otherwise sets PayloadLayer + */ + void parseNextLayer(); + + /** + * Does nothing for this layer + */ + void computeCalculateFields() {} + + std::string toString() const; + + OsiModelLayer getOsiModelLayer() const { return OsiModelNetworkLayer; } + + private: + // this layer supports parsing only + AuthenticationHeaderLayer() {} +}; + +/** + * @class ESPLayer + * Represents an IPSec Encapsulating Security Payload (ESP) layer + */ +class ESPLayer : public Layer { + public: + /** 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 + */ + ESPLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : Layer(data, dataLen, prevLayer, packet) { + m_Protocol = ESP; + } + + ipsec_esp* getESPHeader() const { return (ipsec_esp*)m_Data; } + + /** + * @return The Security Parameters Index (SPI) field value + */ + uint32_t getSPI() const; + + /** + * @return The sequence number value + */ + uint32_t getSequenceNumber() const; + + /** + * A static method that validates the input data + * @param[in] data The pointer to the beginning of a byte stream of a ESP + * layer + * @param[in] dataLen The length of byte stream + * @return True if the data is valid and can represent an ESP layer + */ + static inline bool isDataValid(const uint8_t* data, size_t dataLen); + + // implement abstract methods + + /** + * @return The size of the ESP header (8 bytes) + */ + size_t getHeaderLen() const { return sizeof(ipsec_esp); } + + /** + * The payload of an ESP layer is encrypted, hence the next layer is always a + * generic payload (PayloadLayer) + */ + void parseNextLayer(); + + /** + * Does nothing for this layer + */ + void computeCalculateFields() {} + + std::string toString() const; + + OsiModelLayer getOsiModelLayer() const { return OsiModelTransportLayer; } + + private: + // this layer supports parsing only + ESPLayer() {} +}; + +// implementation of inline methods + +bool AuthenticationHeaderLayer::isDataValid(const uint8_t* data, + size_t dataLen) { + if (dataLen < sizeof(ipsec_authentication_header)) + return false; + + size_t payloadLen = 4 * (data[1] + 2); + if (payloadLen < sizeof(ipsec_authentication_header) || payloadLen > dataLen) + return false; + + return true; +} + +bool ESPLayer::isDataValid(const uint8_t* data, size_t dataLen) { + return data && dataLen >= sizeof(ipsec_esp); } +} // namespace pcpp #endif // PACKETPP_IPSEC_LAYER diff --git a/Packet++/header/IPv4Layer.h b/Packet++/header/IPv4Layer.h index aa66b4cf6c..3c69fc5393 100644 --- a/Packet++/header/IPv4Layer.h +++ b/Packet++/header/IPv4Layer.h @@ -1,10 +1,10 @@ #ifndef PACKETPP_IPV4_LAYER #define PACKETPP_IPV4_LAYER +#include "IPLayer.h" +#include "IpAddress.h" #include "Layer.h" #include "TLVData.h" -#include "IpAddress.h" -#include "IPLayer.h" #include #include @@ -14,674 +14,716 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ +namespace pcpp { - /** - * @struct iphdr - * Represents an IPv4 protocol header - */ +/** + * @struct iphdr + * Represents an IPv4 protocol header + */ #pragma pack(push, 1) - struct iphdr - { +struct iphdr { #if (BYTE_ORDER == LITTLE_ENDIAN) - /** IP header length, has the value of 5 for IPv4 */ - uint8_t internetHeaderLength:4, - /** IP version number, has the value of 4 for IPv4 */ - ipVersion:4; + /** IP header length, has the value of 5 for IPv4 */ + uint8_t internetHeaderLength : 4, + /** IP version number, has the value of 4 for IPv4 */ + ipVersion : 4; #else - /** IP version number, has the value of 4 for IPv4 */ - uint8_t ipVersion:4, - /** IP header length, has the value of 5 for IPv4 */ - internetHeaderLength:4; + /** IP version number, has the value of 4 for IPv4 */ + uint8_t ipVersion : 4, + /** IP header length, has the value of 5 for IPv4 */ + internetHeaderLength : 4; #endif - /** type of service, same as Differentiated Services Code Point (DSCP)*/ - uint8_t typeOfService; - /** Entire packet (fragment) size, including header and data, in bytes */ - uint16_t totalLength; - /** Identification field. Primarily used for uniquely identifying the group of fragments of a single IP datagram*/ - uint16_t ipId; - /** Fragment offset field, measured in units of eight-byte blocks (64 bits) */ - uint16_t fragmentOffset; - /** An eight-bit time to live field helps prevent datagrams from persisting (e.g. going in circles) on an internet. In practice, the field has become a hop count */ - uint8_t timeToLive; - /** Defines the protocol used in the data portion of the IP datagram. Must be one of ::IPProtocolTypes */ - uint8_t protocol; - /** Error-checking of the header */ - uint16_t headerChecksum; - /** IPv4 address of the sender of the packet */ - uint32_t ipSrc; - /** IPv4 address of the receiver of the packet */ - uint32_t ipDst; - /*The options start here. */ - }; + /** type of service, same as Differentiated Services Code Point (DSCP)*/ + uint8_t typeOfService; + /** Entire packet (fragment) size, including header and data, in bytes */ + uint16_t totalLength; + /** Identification field. Primarily used for uniquely identifying the group of + * fragments of a single IP datagram*/ + uint16_t ipId; + /** Fragment offset field, measured in units of eight-byte blocks (64 bits) */ + uint16_t fragmentOffset; + /** An eight-bit time to live field helps prevent datagrams from persisting + * (e.g. going in circles) on an internet. In practice, the field has become + * a hop count */ + uint8_t timeToLive; + /** Defines the protocol used in the data portion of the IP datagram. Must be + * one of ::IPProtocolTypes */ + uint8_t protocol; + /** Error-checking of the header */ + uint16_t headerChecksum; + /** IPv4 address of the sender of the packet */ + uint32_t ipSrc; + /** IPv4 address of the receiver of the packet */ + uint32_t ipDst; + /*The options start here. */ +}; #pragma pack(pop) - /** - * An enum for all possible IPv4 and IPv6 protocol types - */ - enum IPProtocolTypes - { - /** Dummy protocol for TCP */ - PACKETPP_IPPROTO_IP = 0, - /** IPv6 Hop-by-Hop options */ - PACKETPP_IPPROTO_HOPOPTS = 0, - /** Internet Control Message Protocol */ - PACKETPP_IPPROTO_ICMP = 1, - /** Internet Gateway Management Protocol */ - PACKETPP_IPPROTO_IGMP = 2, - /** IPIP tunnels (older KA9Q tunnels use 94) */ - PACKETPP_IPPROTO_IPIP = 4, - /** Transmission Control Protocol */ - PACKETPP_IPPROTO_TCP = 6, - /** Exterior Gateway Protocol */ - PACKETPP_IPPROTO_EGP = 8, - /** PUP protocol */ - PACKETPP_IPPROTO_PUP = 12, - /** User Datagram Protocol */ - PACKETPP_IPPROTO_UDP = 17, - /** XNS IDP protocol */ - PACKETPP_IPPROTO_IDP = 22, - /** IPv6 header */ - PACKETPP_IPPROTO_IPV6 = 41, - /** IPv6 Routing header */ - PACKETPP_IPPROTO_ROUTING = 43, - /** IPv6 fragmentation header */ - PACKETPP_IPPROTO_FRAGMENT = 44, - /** GRE protocol */ - PACKETPP_IPPROTO_GRE = 47, - /** encapsulating security payload */ - PACKETPP_IPPROTO_ESP = 50, - /** authentication header */ - PACKETPP_IPPROTO_AH = 51, - /** ICMPv6 */ - PACKETPP_IPPROTO_ICMPV6 = 58, - /** IPv6 no next header */ - PACKETPP_IPPROTO_NONE = 59, - /** IPv6 Destination options */ - PACKETPP_IPPROTO_DSTOPTS = 60, - /** VRRP protocol */ - PACKETPP_IPPROTO_VRRP = 112, - /** Raw IP packets */ - PACKETPP_IPPROTO_RAW = 255, - /** Maximum value */ - PACKETPP_IPPROTO_MAX - }; - - - /** - * An enum for supported IPv4 option types - */ - enum IPv4OptionTypes - { - /** End of Options List */ - IPV4OPT_EndOfOptionsList = 0, - /** No Operation */ - IPV4OPT_NOP = 1, - /** Record Route */ - IPV4OPT_RecordRoute = 7, - /** MTU Probe */ - IPV4OPT_MTUProbe = 11, - /** MTU Reply */ - IPV4OPT_MTUReply = 12, - /** Quick-Start */ - IPV4OPT_QuickStart = 25, - /** Timestamp */ - IPV4OPT_Timestamp = 68, - /** Traceroute */ - IPV4OPT_Traceroute = 82, - /** Security */ - IPV4OPT_Security = 130, - /** Loose Source Route */ - IPV4OPT_LooseSourceRoute = 131, - /** Extended Security */ - IPV4OPT_ExtendedSecurity = 133, - /** Commercial Security */ - IPV4OPT_CommercialSecurity = 134, - /** Stream ID */ - IPV4OPT_StreamID = 136, - /** Strict Source Route */ - IPV4OPT_StrictSourceRoute = 137, - /** Extended Internet Protocol */ - IPV4OPT_ExtendedInternetProtocol = 145, - /** Address Extension */ - IPV4OPT_AddressExtension = 147, - /** Router Alert */ - IPV4OPT_RouterAlert = 148, - /** Selective Directed Broadcast */ - IPV4OPT_SelectiveDirectedBroadcast = 149, - /** Dynamic Packet State */ - IPV4OPT_DynamicPacketState = 151, - /** Upstream Multicast Pkt. */ - IPV4OPT_UpstreamMulticastPkt = 152, - /** Unknown IPv4 option */ - IPV4OPT_Unknown - }; - -#define PCPP_IP_DONT_FRAGMENT 0x40 +/** + * An enum for all possible IPv4 and IPv6 protocol types + */ +enum IPProtocolTypes { + /** Dummy protocol for TCP */ + PACKETPP_IPPROTO_IP = 0, + /** IPv6 Hop-by-Hop options */ + PACKETPP_IPPROTO_HOPOPTS = 0, + /** Internet Control Message Protocol */ + PACKETPP_IPPROTO_ICMP = 1, + /** Internet Gateway Management Protocol */ + PACKETPP_IPPROTO_IGMP = 2, + /** IPIP tunnels (older KA9Q tunnels use 94) */ + PACKETPP_IPPROTO_IPIP = 4, + /** Transmission Control Protocol */ + PACKETPP_IPPROTO_TCP = 6, + /** Exterior Gateway Protocol */ + PACKETPP_IPPROTO_EGP = 8, + /** PUP protocol */ + PACKETPP_IPPROTO_PUP = 12, + /** User Datagram Protocol */ + PACKETPP_IPPROTO_UDP = 17, + /** XNS IDP protocol */ + PACKETPP_IPPROTO_IDP = 22, + /** IPv6 header */ + PACKETPP_IPPROTO_IPV6 = 41, + /** IPv6 Routing header */ + PACKETPP_IPPROTO_ROUTING = 43, + /** IPv6 fragmentation header */ + PACKETPP_IPPROTO_FRAGMENT = 44, + /** GRE protocol */ + PACKETPP_IPPROTO_GRE = 47, + /** encapsulating security payload */ + PACKETPP_IPPROTO_ESP = 50, + /** authentication header */ + PACKETPP_IPPROTO_AH = 51, + /** ICMPv6 */ + PACKETPP_IPPROTO_ICMPV6 = 58, + /** IPv6 no next header */ + PACKETPP_IPPROTO_NONE = 59, + /** IPv6 Destination options */ + PACKETPP_IPPROTO_DSTOPTS = 60, + /** VRRP protocol */ + PACKETPP_IPPROTO_VRRP = 112, + /** Raw IP packets */ + PACKETPP_IPPROTO_RAW = 255, + /** Maximum value */ + PACKETPP_IPPROTO_MAX +}; + +/** + * An enum for supported IPv4 option types + */ +enum IPv4OptionTypes { + /** End of Options List */ + IPV4OPT_EndOfOptionsList = 0, + /** No Operation */ + IPV4OPT_NOP = 1, + /** Record Route */ + IPV4OPT_RecordRoute = 7, + /** MTU Probe */ + IPV4OPT_MTUProbe = 11, + /** MTU Reply */ + IPV4OPT_MTUReply = 12, + /** Quick-Start */ + IPV4OPT_QuickStart = 25, + /** Timestamp */ + IPV4OPT_Timestamp = 68, + /** Traceroute */ + IPV4OPT_Traceroute = 82, + /** Security */ + IPV4OPT_Security = 130, + /** Loose Source Route */ + IPV4OPT_LooseSourceRoute = 131, + /** Extended Security */ + IPV4OPT_ExtendedSecurity = 133, + /** Commercial Security */ + IPV4OPT_CommercialSecurity = 134, + /** Stream ID */ + IPV4OPT_StreamID = 136, + /** Strict Source Route */ + IPV4OPT_StrictSourceRoute = 137, + /** Extended Internet Protocol */ + IPV4OPT_ExtendedInternetProtocol = 145, + /** Address Extension */ + IPV4OPT_AddressExtension = 147, + /** Router Alert */ + IPV4OPT_RouterAlert = 148, + /** Selective Directed Broadcast */ + IPV4OPT_SelectiveDirectedBroadcast = 149, + /** Dynamic Packet State */ + IPV4OPT_DynamicPacketState = 151, + /** Upstream Multicast Pkt. */ + IPV4OPT_UpstreamMulticastPkt = 152, + /** Unknown IPv4 option */ + IPV4OPT_Unknown +}; + +#define PCPP_IP_DONT_FRAGMENT 0x40 #define PCPP_IP_MORE_FRAGMENTS 0x20 - /** - * @struct IPv4TimestampOptionValue - * A struct representing a parsed value of the IPv4 timestamp option. This struct is used returned in IPv4OptionData#getTimestampOptionValue() method - */ - struct IPv4TimestampOptionValue - { - /** - * An enum for IPv4 timestamp option types - */ - enum TimestampType - { - /** Value containing only timestamps */ - TimestampOnly = 0, - /** Value containing both timestamps and IPv4 addresses */ - TimestampAndIP = 1, - /** The IPv4 addresses are prespecified */ - TimestampsForPrespecifiedIPs = 2, - /** Invalid or unknown value type */ - Unknown = 3 - }; - - /** The timestamp value type */ - TimestampType type; - - /** A list of timestamps parsed from the IPv4 timestamp option value */ - std::vector timestamps; - - /** A list of IPv4 addresses parsed from the IPv4 timestamp option value */ - std::vector ipAddresses; - - /** The default constructor */ - IPv4TimestampOptionValue() : type(IPv4TimestampOptionValue::Unknown) {} - - /** - * Clear the structure. Clean the timestamps and IP addresses vectors and set the type as IPv4TimestampOptionValue#Unknown - */ - void clear() - { - type = IPv4TimestampOptionValue::Unknown; - timestamps.clear(); - ipAddresses.clear(); - } - }; - - - /** - * @class IPv4Option - * A wrapper class for IPv4 options. This class does not create or modify IPv4 option records, but rather - * serves as a wrapper and provides useful methods for retrieving data from them - */ - class IPv4Option : public TLVRecord - { - public: - - /** - * A c'tor for this class that gets a pointer to the option raw data (byte array) - * @param[in] optionRawData A pointer to the IPv4 option raw data - */ - explicit IPv4Option(uint8_t* optionRawData) : TLVRecord(optionRawData) { } - - /** - * A d'tor for this class, currently does nothing - */ - ~IPv4Option() { } - - /** - * A method for parsing the IPv4 option value as a list of IPv4 addresses. This method is relevant only for certain types of IPv4 options which their value is a list of IPv4 addresses - * such as ::IPV4OPT_RecordRoute, ::IPV4OPT_StrictSourceRoute, ::IPV4OPT_LooseSourceRoute, etc. This method returns a vector of the IPv4 addresses. Blank IP addresses - * (meaning zeroed addresses - 0.0.0.0) will not be added to the returned list. If some error occurs during the parsing or the value is invalid an empty vector is returned - * @return A vector of IPv4 addresses parsed from the IPv4 option value - */ - std::vector getValueAsIpList() const - { - std::vector res; - - if (m_Data == nullptr) - return res; - - size_t dataSize = getDataSize(); - if (dataSize < 2) - return res; - - uint8_t valueOffset = (uint8_t)(1); - - while ((size_t)valueOffset < dataSize) - { - uint32_t curValue; - memcpy(&curValue, m_Data->recordValue + valueOffset, sizeof(uint32_t)); - if (curValue == 0) - break; - - res.push_back(IPv4Address(curValue)); - - valueOffset += (uint8_t)(4); - } - - return res; - } - - /** - * A method for parsing the IPv4 timestamp option value. This method is relevant only for IPv4 timestamp option. For other option types an empty result will be returned. - * The returned structure contains the timestamp value type (timestamp only, timestamp + IP addresses, etc.) as well as 2 vectors containing the list of timestamps and the list - * of IP addresses (if applicable for the timestamp value type). Blank timestamps or IP addresses (meaning zeroed values - timestamp=0 or IP address=0.0.0.0) will not be added to - * the lists. If some error occurs during the parsing or the value is invalid an empty result is returned - * @return A structured containing the IPv4 timestamp value - */ - IPv4TimestampOptionValue getTimestampOptionValue() const - { - IPv4TimestampOptionValue res; - res.clear(); - - if (m_Data == nullptr) - return res; - - if (getIPv4OptionType() != IPV4OPT_Timestamp) - return res; - - size_t dataSize = getDataSize(); - if (dataSize < 2) - return res; - - res.type = (IPv4TimestampOptionValue::TimestampType)m_Data->recordValue[1]; - - uint8_t valueOffset = (uint8_t)(2); - bool readIPAddr = (res.type == IPv4TimestampOptionValue::TimestampAndIP); - - while ((size_t)valueOffset < dataSize) - { - uint32_t curValue; - memcpy(&curValue, m_Data->recordValue + valueOffset, sizeof(uint32_t)); - if (curValue == 0) - break; - - if (readIPAddr) - res.ipAddresses.push_back(IPv4Address(curValue)); - else - res.timestamps.push_back(curValue); - - if (res.type == IPv4TimestampOptionValue::TimestampAndIP) - readIPAddr = !readIPAddr; - - valueOffset += (uint8_t)(4); - } - - return res; - } - - /** - * @return IPv4 option type casted as pcpp::IPv4OptionTypes enum - */ - IPv4OptionTypes getIPv4OptionType() const - { - return getIPv4OptionType(m_Data); - } - - /** - * Check if a pointer can be assigned to the TLV record data - * @param[in] recordRawData A pointer to the TLV record raw data - * @param[in] tlvDataLen The size of the TLV record raw data - * @return True if data is valid and can be assigned - */ - static bool canAssign(const uint8_t* recordRawData, size_t tlvDataLen) - { - auto data = (TLVRawData*)recordRawData; - if (data == nullptr) - return false; - - if (tlvDataLen < sizeof(TLVRawData::recordType)) - return false; - - if (getIPv4OptionType(data) == (uint8_t)IPV4OPT_EndOfOptionsList || data->recordType == (uint8_t)IPV4OPT_NOP) - return true; - - return TLVRecord::canAssign(recordRawData, tlvDataLen); - } - - // implement abstract methods - - size_t getTotalSize() const - { - if (m_Data == nullptr) - return 0; - - if (getIPv4OptionType() == (uint8_t)IPV4OPT_EndOfOptionsList || m_Data->recordType == (uint8_t)IPV4OPT_NOP) - return sizeof(uint8_t); - - return (size_t)m_Data->recordLen; - } - - size_t getDataSize() const - { - if (m_Data == nullptr) - return 0; - - if (getIPv4OptionType() == (uint8_t)IPV4OPT_EndOfOptionsList || m_Data->recordType == (uint8_t)IPV4OPT_NOP) - return (size_t)0; - - return (size_t)m_Data->recordLen - (2*sizeof(uint8_t)); - } - - private: - /** - * @return IPv4 option type casted as pcpp::IPv4OptionTypes enum - */ - static IPv4OptionTypes getIPv4OptionType(const TLVRawData* data) - { - if (data == nullptr) - return IPV4OPT_Unknown; - - return (IPv4OptionTypes)data->recordType; - } - }; - - - /** - * @class IPv4OptionBuilder - * A class for building IPv4 option records. This builder receives the IPv4 option parameters in its c'tor, - * builds the IPv4 option raw buffer and provides a build() method to get a IPv4Option object out of it - */ - class IPv4OptionBuilder : public TLVRecordBuilder - { - private: - bool m_BuilderParamsValid; - - public: - - /** - * A c'tor for building IPv4 options which their value is a byte array. The IPv4Option object can be later - * retrieved by calling build() - * @param[in] optionType IPv4 option type - * @param[in] optionValue A buffer containing the option value. This buffer is read-only and isn't modified in any way. - * For option types ::IPV4OPT_NOP and ::IPV4OPT_EndOfOptionsList this parameter is ignored (expected to be NULL) as these - * option types don't contain any data - * @param[in] optionValueLen Option value length in bytes - */ - IPv4OptionBuilder(IPv4OptionTypes optionType, const uint8_t* optionValue, uint8_t optionValueLen) : - TLVRecordBuilder((uint8_t)optionType, optionValue, optionValueLen) { m_BuilderParamsValid = true; } - - /** - * A c'tor for building IPv4 options which have a 2-byte value. The IPv4Option object can be later retrieved - * by calling build() - * @param[in] optionType IPv4 option type - * @param[in] optionValue A 2-byte option value - */ - IPv4OptionBuilder(IPv4OptionTypes optionType, uint16_t optionValue) : - TLVRecordBuilder((uint8_t)optionType, optionValue) { m_BuilderParamsValid = true; } - - /** - * A c'tor for building IPv4 options which their value is a list of IPv4 addresses, for example: - * ::IPV4OPT_RecordRoute, ::IPV4OPT_StrictSourceRoute, ::IPV4OPT_LooseSourceRoute. The IPv4Option object can be later retrieved - * by calling build() - * @param[in] optionType IPv4 option type - * @param[in] ipList A vector of IPv4 addresses that will be used as the option value - */ - IPv4OptionBuilder(IPv4OptionTypes optionType, const std::vector& ipList); - - /** - * A c'tor for building IPv4 timestamp option (::IPV4OPT_Timestamp). The IPv4Option object can be later retrieved by calling build() - * @param[in] timestampValue The timestamp value to build the IPv4 option with - */ - explicit IPv4OptionBuilder(const IPv4TimestampOptionValue& timestampValue); - - /** - * Build the IPv4Option object out of the parameters defined in the c'tor - * @return The IPv4Option object - */ - IPv4Option build() const; - }; - - - /** - * @class IPv4Layer - * Represents an IPv4 protocol layer - */ - class IPv4Layer : public Layer, public IPLayer - { - public: - /** - * A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data (will be casted to @ref iphdr) - * @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 - */ - IPv4Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); - - /** - * A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data (will be casted to @ref iphdr) - * @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 - * @param[in] setTotalLenAsDataLen When setting this value to "true" or when using the other c'tor, the layer data length is calculated - * from iphdr#totalLength field. When setting to "false" the data length is set as the value of dataLen parameter. Please notice that - * if iphdr#totalLength is equal to zero (which can happen in TCP Segmentation Offloading), this flag is ignored and the layer data - * length is calculated by the actual data captured on the wire - */ - IPv4Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet, bool setTotalLenAsDataLen); - - - /** - * A constructor that allocates a new IPv4 header with empty fields - */ - IPv4Layer(); - - /** - * A constructor that allocates a new IPv4 header with source and destination IPv4 addresses - * @param[in] srcIP Source IPv4 address - * @param[in] dstIP Destination IPv4 address - */ - IPv4Layer(const IPv4Address& srcIP, const IPv4Address& dstIP); - - /** - * A copy constructor that copy the entire header from the other IPv4Layer (including IPv4 options) - */ - IPv4Layer(const IPv4Layer& other); - - /** - * An assignment operator that first delete all data from current layer and then copy the entire header from the other IPv4Layer (including IPv4 options) - */ - IPv4Layer& operator=(const IPv4Layer& other); - - /** - * Get a pointer to the IPv4 header. Notice this points directly to the data, so every change will change the actual packet data - * @return A pointer to the @ref iphdr - */ - iphdr* getIPv4Header() const { return (iphdr*)m_Data; } - - /** - * Get the source IP address in the form of IPAddress. This method is very similar to getSrcIPv4Address(), - * but adds a level of abstraction because IPAddress can be used for both IPv4 and IPv6 addresses - * @return An IPAddress containing the source address - */ - IPAddress getSrcIPAddress() const { return getSrcIPv4Address(); } - - /** - * Get the source IP address in the form of IPv4Address - * @return An IPv4Address containing the source address - */ - IPv4Address getSrcIPv4Address() const { return getIPv4Header()->ipSrc; } - - /** - * Set the source IP address - * @param[in] ipAddr The IP address to set - */ - void setSrcIPv4Address(const IPv4Address& ipAddr) { getIPv4Header()->ipSrc = ipAddr.toInt(); } - - /** - * Get the destination IP address in the form of IPAddress. This method is very similar to getDstIPv4Address(), - * but adds a level of abstraction because IPAddress can be used for both IPv4 and IPv6 addresses - * @return An IPAddress containing the destination address - */ - IPAddress getDstIPAddress() const { return getDstIPv4Address(); } - - /** - * Get the destination IP address in the form of IPv4Address - * @return An IPv4Address containing the destination address - */ - IPv4Address getDstIPv4Address() const { return getIPv4Header()->ipDst; } - - /** - * Set the dest IP address - * @param[in] ipAddr The IP address to set - */ - void setDstIPv4Address(const IPv4Address& ipAddr) { getIPv4Header()->ipDst = ipAddr.toInt(); } - - /** - * @return True if this packet is a fragment (in sense of IP fragmentation), false otherwise - */ - bool isFragment() const; - - /** - * @return True if this packet is a fragment (in sense of IP fragmentation) and is the first fragment - * (which usually contains the L4 header). Return false otherwise (not a fragment or not the first fragment) - */ - bool isFirstFragment() const; - - /** - * @return True if this packet is a fragment (in sense of IP fragmentation) and is the last fragment. - * Return false otherwise (not a fragment or not the last fragment) - */ - bool isLastFragment() const; - - /** - * @return A bitmask containing the fragmentation flags (e.g IP_DONT_FRAGMENT or IP_MORE_FRAGMENTS) - */ - uint8_t getFragmentFlags() const; - - /** - * @return The fragment offset in case this packet is a fragment, 0 otherwise - */ - uint16_t getFragmentOffset() const; - - /** - * Get an IPv4 option by type. - * @param[in] option IPv4 option type - * @return An IPv4Option object that contains the first option that matches this type, or logical NULL - * (IPv4Option#isNull() == true) if no such option found - */ - IPv4Option getOption(IPv4OptionTypes option) const; - - /** - * @return The first IPv4 option in the packet. If the current layer contains no options the returned value will contain - * a logical NULL (IPv4Option#isNull() == true) - */ - IPv4Option getFirstOption() const; - - /** - * Get the IPv4 option that comes after a given option. If the given option was the last one, the - * returned value will contain a logical NULL (IPv4Option#isNull() == true) - * @param[in] option An IPv4 option object that exists in the current layer - * @return A IPv4Option object that contains the IPv4 option data that comes next, or logical NULL if the given - * IPv4 option: (1) was the last one; or (2) contains a logical NULL; or (3) doesn't belong to this packet - */ - IPv4Option getNextOption(IPv4Option& option) const; - - /** - * @return The number of IPv4 options in this layer - */ - size_t getOptionCount() const; - - /** - * Add a new IPv4 option at the end of the layer (after the last IPv4 option) - * @param[in] optionBuilder An IPv4OptionBuilder object that contains the IPv4 option data to be added - * @return A IPv4Option object that contains the newly added IPv4 option data or logical NULL - * (IPv4Option#isNull() == true) if addition failed. In case of a failure a corresponding error message will be - * printed to log - */ - IPv4Option addOption(const IPv4OptionBuilder& optionBuilder); - - /** - * Add a new IPv4 option after an existing one - * @param[in] optionBuilder An IPv4OptionBuilder object that contains the requested IPv4 option data to be added - * @param[in] prevOptionType The IPv4 option which the newly added option should come after. This is an optional parameter which - * gets a default value of ::IPV4OPT_Unknown if omitted, which means the new option will be added as the first option in the layer - * @return A IPv4Option object containing the newly added IPv4 option data or logical NULL - * (IPv4Option#isNull() == true) if addition failed. In case of a failure a corresponding error message will be - * printed to log - */ - IPv4Option addOptionAfter(const IPv4OptionBuilder& optionBuilder, IPv4OptionTypes prevOptionType = IPV4OPT_Unknown); - - /** - * Remove an IPv4 option - * @param[in] option The option type to remove - * @return True if option was removed successfully or false if option type wasn't found or failed to shorten the layer. If an option appears twice in the layer, its first instance - * will be removed - */ - bool removeOption(IPv4OptionTypes option); - - /** - * Remove all IPv4 options from the layer - * @return True if options removed successfully or false if some error occurred (an appropriate error message will be printed to log) - */ - bool removeAllOptions(); - - - // implement abstract methods - - /** - * Currently identifies the following next layers: - * - UdpLayer - * - TcpLayer - * - IcmpLayer - * - IPv4Layer (IP-in-IP) - * - IPv6Layer (IP-in-IP) - * - GreLayer - * - IgmpLayer - * - AuthenticationHeaderLayer (IPSec) - * - ESPLayer (IPSec) - * - * Otherwise sets PayloadLayer - */ - void parseNextLayer(); - - /** - * @return Size of IPv4 header (including IPv4 options if exist) - */ - size_t getHeaderLen() const { return (size_t)((uint16_t)(getIPv4Header()->internetHeaderLength) * 4) + m_TempHeaderExtension; } - - /** - * Calculate the following fields: - * - iphdr#ipVersion = 4; - * - iphdr#totalLength = total packet length - * - iphdr#headerChecksum = calculated - * - iphdr#protocol = calculated if next layer is known: ::PACKETPP_IPPROTO_TCP for TCP, ::PACKETPP_IPPROTO_UDP for UDP, ::PACKETPP_IPPROTO_ICMP for ICMP - */ - void computeCalculateFields(); - - std::string toString() const; - - OsiModelLayer getOsiModelLayer() const { return OsiModelNetworkLayer; } - - /** - * A static method that validates the input data - * @param[in] data The pointer to the beginning of a byte stream of IP packet - * @param[in] dataLen The length of the byte stream - * @return True if the data is valid and can represent an IPv4 packet - */ - static inline bool isDataValid(const uint8_t* data, size_t dataLen); - - private: - int m_NumOfTrailingBytes; - int m_TempHeaderExtension; - TLVRecordReader m_OptionReader; - - void copyLayerData(const IPv4Layer& other); - uint8_t* getOptionsBasePtr() const { return m_Data + sizeof(iphdr); } - IPv4Option addOptionAt(const IPv4OptionBuilder& optionBuilder, int offset); - void adjustOptionsTrailer(size_t totalOptSize); - void initLayer(); - void initLayerInPacket(bool setTotalLenAsDataLen); - }; - - - // implementation of inline methods - - bool IPv4Layer::isDataValid(const uint8_t* data, size_t dataLen) - { - const iphdr* hdr = reinterpret_cast(data); - return dataLen >= sizeof(iphdr) && hdr->ipVersion == 4 && hdr->internetHeaderLength >= 5; - } +/** + * @struct IPv4TimestampOptionValue + * A struct representing a parsed value of the IPv4 timestamp option. This + * struct is used returned in IPv4OptionData#getTimestampOptionValue() method + */ +struct IPv4TimestampOptionValue { + /** + * An enum for IPv4 timestamp option types + */ + enum TimestampType { + /** Value containing only timestamps */ + TimestampOnly = 0, + /** Value containing both timestamps and IPv4 addresses */ + TimestampAndIP = 1, + /** The IPv4 addresses are prespecified */ + TimestampsForPrespecifiedIPs = 2, + /** Invalid or unknown value type */ + Unknown = 3 + }; + + /** The timestamp value type */ + TimestampType type; + + /** A list of timestamps parsed from the IPv4 timestamp option value */ + std::vector timestamps; + + /** A list of IPv4 addresses parsed from the IPv4 timestamp option value */ + std::vector ipAddresses; + + /** The default constructor */ + IPv4TimestampOptionValue() : type(IPv4TimestampOptionValue::Unknown) {} + + /** + * Clear the structure. Clean the timestamps and IP addresses vectors and set + * the type as IPv4TimestampOptionValue#Unknown + */ + void clear() { + type = IPv4TimestampOptionValue::Unknown; + timestamps.clear(); + ipAddresses.clear(); + } +}; + +/** + * @class IPv4Option + * A wrapper class for IPv4 options. This class does not create or modify IPv4 + * option records, but rather serves as a wrapper and provides useful methods + * for retrieving data from them + */ +class IPv4Option : public TLVRecord { + public: + /** + * A c'tor for this class that gets a pointer to the option raw data (byte + * array) + * @param[in] optionRawData A pointer to the IPv4 option raw data + */ + explicit IPv4Option(uint8_t* optionRawData) : TLVRecord(optionRawData) {} + + /** + * A d'tor for this class, currently does nothing + */ + ~IPv4Option() {} + + /** + * A method for parsing the IPv4 option value as a list of IPv4 addresses. + * This method is relevant only for certain types of IPv4 options which their + * value is a list of IPv4 addresses such as ::IPV4OPT_RecordRoute, + * ::IPV4OPT_StrictSourceRoute, ::IPV4OPT_LooseSourceRoute, etc. This method + * returns a vector of the IPv4 addresses. Blank IP addresses (meaning zeroed + * addresses - 0.0.0.0) will not be added to the returned list. If some error + * occurs during the parsing or the value is invalid an empty vector is + * returned + * @return A vector of IPv4 addresses parsed from the IPv4 option value + */ + std::vector getValueAsIpList() const { + std::vector res; + + if (m_Data == nullptr) + return res; + + size_t dataSize = getDataSize(); + if (dataSize < 2) + return res; + + uint8_t valueOffset = (uint8_t)(1); + + while ((size_t)valueOffset < dataSize) { + uint32_t curValue; + memcpy(&curValue, m_Data->recordValue + valueOffset, sizeof(uint32_t)); + if (curValue == 0) + break; + + res.push_back(IPv4Address(curValue)); + + valueOffset += (uint8_t)(4); + } + + return res; + } + + /** + * A method for parsing the IPv4 timestamp option value. This method is + * relevant only for IPv4 timestamp option. For other option types an empty + * result will be returned. The returned structure contains the timestamp + * value type (timestamp only, timestamp + IP addresses, etc.) as well as 2 + * vectors containing the list of timestamps and the list of IP addresses (if + * applicable for the timestamp value type). Blank timestamps or IP addresses + * (meaning zeroed values - timestamp=0 or IP address=0.0.0.0) will not be + * added to the lists. If some error occurs during the parsing or the value is + * invalid an empty result is returned + * @return A structured containing the IPv4 timestamp value + */ + IPv4TimestampOptionValue getTimestampOptionValue() const { + IPv4TimestampOptionValue res; + res.clear(); + + if (m_Data == nullptr) + return res; + + if (getIPv4OptionType() != IPV4OPT_Timestamp) + return res; + + size_t dataSize = getDataSize(); + if (dataSize < 2) + return res; + + res.type = (IPv4TimestampOptionValue::TimestampType)m_Data->recordValue[1]; + + uint8_t valueOffset = (uint8_t)(2); + bool readIPAddr = (res.type == IPv4TimestampOptionValue::TimestampAndIP); + + while ((size_t)valueOffset < dataSize) { + uint32_t curValue; + memcpy(&curValue, m_Data->recordValue + valueOffset, sizeof(uint32_t)); + if (curValue == 0) + break; + + if (readIPAddr) + res.ipAddresses.push_back(IPv4Address(curValue)); + else + res.timestamps.push_back(curValue); + + if (res.type == IPv4TimestampOptionValue::TimestampAndIP) + readIPAddr = !readIPAddr; + + valueOffset += (uint8_t)(4); + } + + return res; + } + + /** + * @return IPv4 option type casted as pcpp::IPv4OptionTypes enum + */ + IPv4OptionTypes getIPv4OptionType() const { + return getIPv4OptionType(m_Data); + } + + /** + * Check if a pointer can be assigned to the TLV record data + * @param[in] recordRawData A pointer to the TLV record raw data + * @param[in] tlvDataLen The size of the TLV record raw data + * @return True if data is valid and can be assigned + */ + static bool canAssign(const uint8_t* recordRawData, size_t tlvDataLen) { + auto data = (TLVRawData*)recordRawData; + if (data == nullptr) + return false; + + if (tlvDataLen < sizeof(TLVRawData::recordType)) + return false; + + if (getIPv4OptionType(data) == (uint8_t)IPV4OPT_EndOfOptionsList || + data->recordType == (uint8_t)IPV4OPT_NOP) + return true; + + return TLVRecord::canAssign(recordRawData, tlvDataLen); + } + + // implement abstract methods + + size_t getTotalSize() const { + if (m_Data == nullptr) + return 0; + + if (getIPv4OptionType() == (uint8_t)IPV4OPT_EndOfOptionsList || + m_Data->recordType == (uint8_t)IPV4OPT_NOP) + return sizeof(uint8_t); + + return (size_t)m_Data->recordLen; + } + + size_t getDataSize() const { + if (m_Data == nullptr) + return 0; + + if (getIPv4OptionType() == (uint8_t)IPV4OPT_EndOfOptionsList || + m_Data->recordType == (uint8_t)IPV4OPT_NOP) + return (size_t)0; + + return (size_t)m_Data->recordLen - (2 * sizeof(uint8_t)); + } + + private: + /** + * @return IPv4 option type casted as pcpp::IPv4OptionTypes enum + */ + static IPv4OptionTypes getIPv4OptionType(const TLVRawData* data) { + if (data == nullptr) + return IPV4OPT_Unknown; + + return (IPv4OptionTypes)data->recordType; + } +}; + +/** + * @class IPv4OptionBuilder + * A class for building IPv4 option records. This builder receives the IPv4 + * option parameters in its c'tor, builds the IPv4 option raw buffer and + * provides a build() method to get a IPv4Option object out of it + */ +class IPv4OptionBuilder : public TLVRecordBuilder { + private: + bool m_BuilderParamsValid; + + public: + /** + * A c'tor for building IPv4 options which their value is a byte array. The + * IPv4Option object can be later retrieved by calling build() + * @param[in] optionType IPv4 option type + * @param[in] optionValue A buffer containing the option value. This buffer is + * read-only and isn't modified in any way. For option types ::IPV4OPT_NOP and + * ::IPV4OPT_EndOfOptionsList this parameter is ignored (expected to be NULL) + * as these option types don't contain any data + * @param[in] optionValueLen Option value length in bytes + */ + IPv4OptionBuilder(IPv4OptionTypes optionType, const uint8_t* optionValue, + uint8_t optionValueLen) + : TLVRecordBuilder((uint8_t)optionType, optionValue, optionValueLen) { + m_BuilderParamsValid = true; + } + + /** + * A c'tor for building IPv4 options which have a 2-byte value. The IPv4Option + * object can be later retrieved by calling build() + * @param[in] optionType IPv4 option type + * @param[in] optionValue A 2-byte option value + */ + IPv4OptionBuilder(IPv4OptionTypes optionType, uint16_t optionValue) + : TLVRecordBuilder((uint8_t)optionType, optionValue) { + m_BuilderParamsValid = true; + } + + /** + * A c'tor for building IPv4 options which their value is a list of IPv4 + * addresses, for example: + * ::IPV4OPT_RecordRoute, ::IPV4OPT_StrictSourceRoute, + * ::IPV4OPT_LooseSourceRoute. The IPv4Option object can be later retrieved by + * calling build() + * @param[in] optionType IPv4 option type + * @param[in] ipList A vector of IPv4 addresses that will be used as the + * option value + */ + IPv4OptionBuilder(IPv4OptionTypes optionType, + const std::vector& ipList); + + /** + * A c'tor for building IPv4 timestamp option (::IPV4OPT_Timestamp). The + * IPv4Option object can be later retrieved by calling build() + * @param[in] timestampValue The timestamp value to build the IPv4 option with + */ + explicit IPv4OptionBuilder(const IPv4TimestampOptionValue& timestampValue); + + /** + * Build the IPv4Option object out of the parameters defined in the c'tor + * @return The IPv4Option object + */ + IPv4Option build() const; +}; + +/** + * @class IPv4Layer + * Represents an IPv4 protocol layer + */ +class IPv4Layer : public Layer, public IPLayer { + public: + /** + * A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data (will be casted to @ref iphdr) + * @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 + */ + IPv4Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); + + /** + * A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data (will be casted to @ref iphdr) + * @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 + * @param[in] setTotalLenAsDataLen When setting this value to "true" or when + * using the other c'tor, the layer data length is calculated from + * iphdr#totalLength field. When setting to "false" the data length is set as + * the value of dataLen parameter. Please notice that if iphdr#totalLength is + * equal to zero (which can happen in TCP Segmentation Offloading), this flag + * is ignored and the layer data length is calculated by the actual data + * captured on the wire + */ + IPv4Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet, + bool setTotalLenAsDataLen); + + /** + * A constructor that allocates a new IPv4 header with empty fields + */ + IPv4Layer(); + + /** + * A constructor that allocates a new IPv4 header with source and destination + * IPv4 addresses + * @param[in] srcIP Source IPv4 address + * @param[in] dstIP Destination IPv4 address + */ + IPv4Layer(const IPv4Address& srcIP, const IPv4Address& dstIP); + + /** + * A copy constructor that copy the entire header from the other IPv4Layer + * (including IPv4 options) + */ + IPv4Layer(const IPv4Layer& other); + + /** + * An assignment operator that first delete all data from current layer and + * then copy the entire header from the other IPv4Layer (including IPv4 + * options) + */ + IPv4Layer& operator=(const IPv4Layer& other); + + /** + * Get a pointer to the IPv4 header. Notice this points directly to the data, + * so every change will change the actual packet data + * @return A pointer to the @ref iphdr + */ + iphdr* getIPv4Header() const { return (iphdr*)m_Data; } + + /** + * Get the source IP address in the form of IPAddress. This method is very + * similar to getSrcIPv4Address(), but adds a level of abstraction because + * IPAddress can be used for both IPv4 and IPv6 addresses + * @return An IPAddress containing the source address + */ + IPAddress getSrcIPAddress() const { return getSrcIPv4Address(); } + + /** + * Get the source IP address in the form of IPv4Address + * @return An IPv4Address containing the source address + */ + IPv4Address getSrcIPv4Address() const { return getIPv4Header()->ipSrc; } + + /** + * Set the source IP address + * @param[in] ipAddr The IP address to set + */ + void setSrcIPv4Address(const IPv4Address& ipAddr) { + getIPv4Header()->ipSrc = ipAddr.toInt(); + } + + /** + * Get the destination IP address in the form of IPAddress. This method is + * very similar to getDstIPv4Address(), but adds a level of abstraction + * because IPAddress can be used for both IPv4 and IPv6 addresses + * @return An IPAddress containing the destination address + */ + IPAddress getDstIPAddress() const { return getDstIPv4Address(); } + + /** + * Get the destination IP address in the form of IPv4Address + * @return An IPv4Address containing the destination address + */ + IPv4Address getDstIPv4Address() const { return getIPv4Header()->ipDst; } + + /** + * Set the dest IP address + * @param[in] ipAddr The IP address to set + */ + void setDstIPv4Address(const IPv4Address& ipAddr) { + getIPv4Header()->ipDst = ipAddr.toInt(); + } + + /** + * @return True if this packet is a fragment (in sense of IP fragmentation), + * false otherwise + */ + bool isFragment() const; + + /** + * @return True if this packet is a fragment (in sense of IP fragmentation) + * and is the first fragment (which usually contains the L4 header). Return + * false otherwise (not a fragment or not the first fragment) + */ + bool isFirstFragment() const; + + /** + * @return True if this packet is a fragment (in sense of IP fragmentation) + * and is the last fragment. Return false otherwise (not a fragment or not the + * last fragment) + */ + bool isLastFragment() const; + + /** + * @return A bitmask containing the fragmentation flags (e.g IP_DONT_FRAGMENT + * or IP_MORE_FRAGMENTS) + */ + uint8_t getFragmentFlags() const; + + /** + * @return The fragment offset in case this packet is a fragment, 0 otherwise + */ + uint16_t getFragmentOffset() const; + + /** + * Get an IPv4 option by type. + * @param[in] option IPv4 option type + * @return An IPv4Option object that contains the first option that matches + * this type, or logical NULL (IPv4Option#isNull() == true) if no such option + * found + */ + IPv4Option getOption(IPv4OptionTypes option) const; + + /** + * @return The first IPv4 option in the packet. If the current layer contains + * no options the returned value will contain a logical NULL + * (IPv4Option#isNull() == true) + */ + IPv4Option getFirstOption() const; + + /** + * Get the IPv4 option that comes after a given option. If the given option + * was the last one, the returned value will contain a logical NULL + * (IPv4Option#isNull() == true) + * @param[in] option An IPv4 option object that exists in the current layer + * @return A IPv4Option object that contains the IPv4 option data that comes + * next, or logical NULL if the given IPv4 option: (1) was the last one; or + * (2) contains a logical NULL; or (3) doesn't belong to this packet + */ + IPv4Option getNextOption(IPv4Option& option) const; + + /** + * @return The number of IPv4 options in this layer + */ + size_t getOptionCount() const; + + /** + * Add a new IPv4 option at the end of the layer (after the last IPv4 option) + * @param[in] optionBuilder An IPv4OptionBuilder object that contains the IPv4 + * option data to be added + * @return A IPv4Option object that contains the newly added IPv4 option data + * or logical NULL (IPv4Option#isNull() == true) if addition failed. In case + * of a failure a corresponding error message will be printed to log + */ + IPv4Option addOption(const IPv4OptionBuilder& optionBuilder); + + /** + * Add a new IPv4 option after an existing one + * @param[in] optionBuilder An IPv4OptionBuilder object that contains the + * requested IPv4 option data to be added + * @param[in] prevOptionType The IPv4 option which the newly added option + * should come after. This is an optional parameter which gets a default value + * of ::IPV4OPT_Unknown if omitted, which means the new option will be added + * as the first option in the layer + * @return A IPv4Option object containing the newly added IPv4 option data or + * logical NULL (IPv4Option#isNull() == true) if addition failed. In case of a + * failure a corresponding error message will be printed to log + */ + IPv4Option addOptionAfter(const IPv4OptionBuilder& optionBuilder, + IPv4OptionTypes prevOptionType = IPV4OPT_Unknown); + + /** + * Remove an IPv4 option + * @param[in] option The option type to remove + * @return True if option was removed successfully or false if option type + * wasn't found or failed to shorten the layer. If an option appears twice in + * the layer, its first instance will be removed + */ + bool removeOption(IPv4OptionTypes option); + + /** + * Remove all IPv4 options from the layer + * @return True if options removed successfully or false if some error + * occurred (an appropriate error message will be printed to log) + */ + bool removeAllOptions(); + + // implement abstract methods + + /** + * Currently identifies the following next layers: + * - UdpLayer + * - TcpLayer + * - IcmpLayer + * - IPv4Layer (IP-in-IP) + * - IPv6Layer (IP-in-IP) + * - GreLayer + * - IgmpLayer + * - AuthenticationHeaderLayer (IPSec) + * - ESPLayer (IPSec) + * + * Otherwise sets PayloadLayer + */ + void parseNextLayer(); + + /** + * @return Size of IPv4 header (including IPv4 options if exist) + */ + size_t getHeaderLen() const { + return (size_t)((uint16_t)(getIPv4Header()->internetHeaderLength) * 4) + + m_TempHeaderExtension; + } + + /** + * Calculate the following fields: + * - iphdr#ipVersion = 4; + * - iphdr#totalLength = total packet length + * - iphdr#headerChecksum = calculated + * - iphdr#protocol = calculated if next layer is known: + * ::PACKETPP_IPPROTO_TCP for TCP, ::PACKETPP_IPPROTO_UDP for UDP, + * ::PACKETPP_IPPROTO_ICMP for ICMP + */ + void computeCalculateFields(); + + std::string toString() const; + + OsiModelLayer getOsiModelLayer() const { return OsiModelNetworkLayer; } + + /** + * A static method that validates the input data + * @param[in] data The pointer to the beginning of a byte stream of IP packet + * @param[in] dataLen The length of the byte stream + * @return True if the data is valid and can represent an IPv4 packet + */ + static inline bool isDataValid(const uint8_t* data, size_t dataLen); + + private: + int m_NumOfTrailingBytes; + int m_TempHeaderExtension; + TLVRecordReader m_OptionReader; + + void copyLayerData(const IPv4Layer& other); + uint8_t* getOptionsBasePtr() const { return m_Data + sizeof(iphdr); } + IPv4Option addOptionAt(const IPv4OptionBuilder& optionBuilder, int offset); + void adjustOptionsTrailer(size_t totalOptSize); + void initLayer(); + void initLayerInPacket(bool setTotalLenAsDataLen); +}; + +// implementation of inline methods + +bool IPv4Layer::isDataValid(const uint8_t* data, size_t dataLen) { + const iphdr* hdr = reinterpret_cast(data); + return dataLen >= sizeof(iphdr) && hdr->ipVersion == 4 && + hdr->internetHeaderLength >= 5; +} } // namespace pcpp diff --git a/Packet++/header/IPv6Extensions.h b/Packet++/header/IPv6Extensions.h index 6e48695c37..118477c42c 100644 --- a/Packet++/header/IPv6Extensions.h +++ b/Packet++/header/IPv6Extensions.h @@ -1,10 +1,10 @@ #ifndef PACKETPP_IPV6_EXTENSION #define PACKETPP_IPV6_EXTENSION -#include #include "IpAddress.h" #include "Layer.h" #include "TLVData.h" +#include /// @file @@ -12,549 +12,594 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - - /** - * @class IPv6Extension - * A base class for all supported IPv6 extensions. This class is abstract, meaning it cannot be instantiated or copied - * (has private c'tor and copy c'tor) - */ - class IPv6Extension - { - friend class IPv6Layer; - - public: - - /** - * An enum representing all supported IPv6 extension types - */ - enum IPv6ExtensionType - { - /** Hop-By-Hop extension type */ - IPv6HopByHop = 0, - /** Routing extension type */ - IPv6Routing = 43, - /** IPv6 fragmentation extension type */ - IPv6Fragmentation = 44, - /** Authentication Header extension type */ - IPv6AuthenticationHdr = 51, - /** Destination extension type */ - IPv6Destination = 60, - /** Unknown or unsupported extension type */ - IPv6ExtensionUnknown = 255 - }; - - /** - * @return The size of extension in bytes, meaning (for most extensions): 8 * ([headerLen field] + 1) - */ - virtual size_t getExtensionLen() const { return 8 * (getBaseHeader()->headerLen+1); } - - /** - * @return The type of the extension - */ - IPv6ExtensionType getExtensionType() const { return m_ExtType; } - - /** - * A destructor for this class - */ - virtual ~IPv6Extension(); - - /** - * @return A pointer to the next header or NULL if the extension is the last one - */ - IPv6Extension* getNextHeader() const { return m_NextHeader; } - - protected: - - struct ipv6_ext_base_header - { - uint8_t nextHeader; - uint8_t headerLen; - }; - - // protected c'tor - IPv6Extension(IDataContainer* dataContainer, size_t offset) : - m_NextHeader(NULL), m_ExtType(IPv6ExtensionUnknown), m_DataContainer(dataContainer), m_Offset(offset), m_ShadowData(NULL) {} - - // protected empty c'tor - IPv6Extension() : - m_NextHeader(NULL), m_ExtType(IPv6ExtensionUnknown), m_DataContainer(NULL), m_Offset(0), m_ShadowData(NULL) {} - - // protected assignment operator - IPv6Extension& operator=(const IPv6Extension& other); - - uint8_t* getDataPtr() const; - - void initShadowPtr(size_t size); - - ipv6_ext_base_header* getBaseHeader() const { return (ipv6_ext_base_header*)getDataPtr(); } - - void setNextHeader(IPv6Extension* nextHeader) { m_NextHeader = nextHeader; } - - IPv6Extension* m_NextHeader; - IPv6ExtensionType m_ExtType; - - private: - IDataContainer* m_DataContainer; - size_t m_Offset; - uint8_t* m_ShadowData; - - }; - - - - /** - * @class IPv6FragmentationHeader - * Represents an IPv6 fragmentation extension header and allows easy access to all fragmentation parameters - */ - class IPv6FragmentationHeader : public IPv6Extension - { - friend class IPv6Layer; - - public: - - /** - * @struct ipv6_frag_header - * A struct representing IPv6 fragmentation header - */ - struct ipv6_frag_header - { - /** Next header type */ - uint8_t nextHeader; - /** Fragmentation header size is fixed 8 bytes, so len is always zero */ - uint8_t headerLen; - /** Offset, in 8-octet units, relative to the start of the fragmentable part of the original packet - * plus 1-bit indicating if more fragments will follow */ - uint16_t fragOffsetAndFlags; - /** packet identification value. Needed for reassembly of the original packet */ - uint32_t id; - }; - - /** - * A c'tor for creating a new IPv6 fragmentation extension object not bounded to a packet. Useful for adding new extensions to an - * IPv6 layer with IPv6Layer#addExtension() - * @param[in] fragId Fragmentation ID - * @param[in] fragOffset Fragmentation offset - * @param[in] lastFragment Indicates whether this fragment is the last one - */ - IPv6FragmentationHeader(uint32_t fragId, uint16_t fragOffset, bool lastFragment); - - /** - * Get a pointer to the fragmentation header. Notice the returned pointer points directly to the data, so every change will modify - * the actual packet data - * @return A pointer to the @ref ipv6_frag_header - */ - ipv6_frag_header* getFragHeader() const { return (ipv6_frag_header*)getDataPtr(); } - - /** - * @return True if this is the first fragment (which usually contains the L4 header), false otherwise - */ - bool isFirstFragment() const; - - /** - * @return True if this is the last fragment, false otherwise - */ - bool isLastFragment() const; - - /** - * @return True if the "more fragments" bit is set, meaning more fragments are expected to follow this fragment - */ - bool isMoreFragments() const; - - /** - * @return The fragment offset - */ - uint16_t getFragmentOffset() const; - - private: - - IPv6FragmentationHeader(IDataContainer* dataContainer, size_t offset) : IPv6Extension(dataContainer, offset) - { - m_ExtType = IPv6Fragmentation; - } - - }; - - - /** - * An abstract base class for Hop-By-Hop and Destination IPv6 extensions which their structure contains Type-Length-Value (TLV) options. - * This class provides access to these options and their data as well as methods to create new options. Notice this class is abstract - * and cannot be instantiated - */ - class IPv6TLVOptionHeader : public IPv6Extension - { - friend class IPv6Layer; - - public: - - /** - * @class IPv6Option - * A class representing a Type-Length-Value (TLV) options that are used inside Hop-By-Hop and Destinations IPv6 - * extensions. This class does not create or modify IPv6 option records, but rather serves as a wrapper and - * provides useful methods for retrieving data from them - */ - class IPv6Option : public TLVRecord - { - public: - - static const uint8_t Pad0OptionType = 0; - static const uint8_t PadNOptionType = 1; - - /** - * A c'tor for this class that gets a pointer to the option raw data (byte array) - * @param[in] optionRawData A pointer to the attribute raw data - */ - explicit IPv6Option(uint8_t* optionRawData) : TLVRecord(optionRawData) { } - - /** - * A d'tor for this class, currently does nothing - */ - ~IPv6Option() { } - - /** - * Check if a pointer can be assigned to the TLV record data - * @param[in] recordRawData A pointer to the TLV record raw data - * @param[in] tlvDataLen The size of the TLV record raw data - * @return True if data is valid and can be assigned - */ - static bool canAssign(const uint8_t* recordRawData, size_t tlvDataLen) - { - auto data = (TLVRawData*)recordRawData; - if (data == nullptr) - return false; - - if (tlvDataLen < sizeof(TLVRawData::recordType)) - return false; - - if (data->recordType == Pad0OptionType) - return true; - - return TLVRecord::canAssign(recordRawData, tlvDataLen); - } - - // implement abstract methods - - size_t getTotalSize() const - { - if (m_Data == nullptr) - return 0; - - if (m_Data->recordType == Pad0OptionType) - return sizeof(uint8_t); - - return (size_t)(m_Data->recordLen + sizeof(uint16_t)); - } - - size_t getDataSize() const - { - if (m_Data == nullptr || m_Data->recordType == Pad0OptionType) - return 0; - - return (size_t)m_Data->recordLen; - } - }; - - - /** - * @class IPv6TLVOptionBuilder - * A class for building IPv6 Type-Length-Value (TLV) options. This builder receives the option parameters in its c'tor, - * builds the option raw buffer and provides a method to build a IPv6Option object out of it - */ - class IPv6TLVOptionBuilder : public TLVRecordBuilder - { - public: - - /** - * A c'tor for building IPv6 TLV options which their value is a byte array. The IPv6Option object can later - * be retrieved by calling build() - * @param[in] optType IPv6 option type - * @param[in] optValue A buffer containing the option value. This buffer is read-only and isn't modified in any way - * @param[in] optValueLen Option value length in bytes - */ - IPv6TLVOptionBuilder(uint8_t optType, const uint8_t* optValue, uint8_t optValueLen) : - TLVRecordBuilder(optType, optValue, optValueLen) { } - - /** - * A c'tor for building IPv6 TLV options which have a 1-byte value. The IPv6Option object can later be retrieved - * by calling build() - * @param[in] optType IPv6 option type - * @param[in] optValue A 1-byte option value - */ - IPv6TLVOptionBuilder(uint8_t optType, uint8_t optValue) : - TLVRecordBuilder(optType, optValue) { } - - /** - * A c'tor for building IPv6 TLV options which have a 2-byte value. The IPv6Option object can later be retrieved - * by calling build() - * @param[in] optType IPv6 option type - * @param[in] optValue A 2-byte option value - */ - IPv6TLVOptionBuilder(uint8_t optType, uint16_t optValue) : - TLVRecordBuilder(optType, optValue) { } - - /** - * A copy c'tor that creates an instance of this class out of another instance and copies all the data from it - * @param[in] other The instance to copy data from - */ - IPv6TLVOptionBuilder(const IPv6TLVOptionBuilder& other) : - TLVRecordBuilder(other) {} - - /** - * Assignment operator that copies all data from another instance of IPv6TLVOptionBuilder - * @param[in] other The instance to assign from - */ - IPv6TLVOptionBuilder& operator=(const IPv6TLVOptionBuilder& other) - { - TLVRecordBuilder::operator=(other); - return *this; - } - - /** - * Build the IPv6Option object out of the parameters defined in the c'tor - * @return The IPv6Option object - */ - IPv6Option build() const; - }; - - /** - * Retrieve an option by its type - * @param[in] optionType Option type - * @return An IPv6Option object that wraps the option data. If option isn't found a logical NULL is returned - * (IPv6Option#isNull() == true) - */ - IPv6Option getOption(uint8_t optionType) const; - - /** - * @return An IPv6Option that wraps the first option data or logical NULL (IPv6Option#isNull() == true) if no options exist - */ - IPv6Option getFirstOption() const; - - /** - * Returns a pointer to the option that comes after the option given as the parameter - * @param[in] option A pointer to an option instance - * @return An IPv6Option object that wraps the option data. In the following cases logical NULL (IPv6Option#isNull() == true) - * is returned: - * (1) input parameter is out-of-bounds for this extension or - * (2) the next option doesn't exist or - * (3) the input option is NULL - */ - IPv6Option getNextOption(IPv6Option& option) const; - - /** - * @returns The number of options this IPv6 extension contains - */ - size_t getOptionCount() const; - - protected: - - /** A private c'tor to keep this object from being constructed */ - explicit IPv6TLVOptionHeader(const std::vector& options); - - IPv6TLVOptionHeader(IDataContainer* dataContainer, size_t offset); - - private: - - TLVRecordReader m_OptionReader; - }; - - - - /** - * @class IPv6HopByHopHeader - * Represents IPv6 Hop-By-Hop extension header and allows easy access to all of its data including the TLV options stored - */ - class IPv6HopByHopHeader : public IPv6TLVOptionHeader - { - friend class IPv6Layer; - - public: - - /** - * A c'tor for creating a new IPv6 Hop-By-Hop extension object not bounded to a packet. Useful for adding new extensions to an - * IPv6 layer with IPv6Layer#addExtension() - * @param[in] options A vector of IPv6TLVOptionHeader#TLVOptionBuilder instances which define the options that will be stored in the - * extension data. Notice this vector is read-only and its content won't be modified - */ - explicit IPv6HopByHopHeader(const std::vector& options) : IPv6TLVOptionHeader(options) { m_ExtType = IPv6HopByHop; } - - private: - - IPv6HopByHopHeader(IDataContainer* dataContainer, size_t offset) : IPv6TLVOptionHeader(dataContainer, offset) { m_ExtType = IPv6HopByHop; } - }; - - - - /** - * @class IPv6DestinationHeader - * Represents IPv6 destination extension header and allows easy access to all of its data including the TLV options stored in it - */ - class IPv6DestinationHeader : public IPv6TLVOptionHeader - { - friend class IPv6Layer; - - public: - - /** - * A c'tor for creating a new IPv6 destination extension object not bounded to a packet. Useful for adding new extensions to an - * IPv6 layer with IPv6Layer#addExtension() - * @param[in] options A vector of IPv6TLVOptionHeader#TLVOptionBuilder instances which define the options that will be stored in the - * extension data. Notice this vector is read-only and its content won't be modified - */ - explicit IPv6DestinationHeader(const std::vector& options) : IPv6TLVOptionHeader(options) { m_ExtType = IPv6Destination; } - - private: - - IPv6DestinationHeader(IDataContainer* dataContainer, size_t offset) : IPv6TLVOptionHeader(dataContainer, offset) { m_ExtType = IPv6Destination; } - }; - - - - /** - * @class IPv6RoutingHeader - * Represents IPv6 routing extension header and allows easy access to all of its data - */ - class IPv6RoutingHeader : public IPv6Extension - { - friend class IPv6Layer; - - public: - - /** - * @struct ipv6_routing_header - * A struct representing the fixed part of the IPv6 routing extension header - */ - struct ipv6_routing_header - { - /** Next header type */ - uint8_t nextHeader; - /** The length of this header, in multiples of 8 octets, not including the first 8 octets */ - uint8_t headerLen; - /** A value representing the routing type */ - uint8_t routingType; - /** Number of nodes this packet still has to visit before reaching its final destination */ - uint8_t segmentsLeft; - }; - - /** - * A c'tor for creating a new IPv6 routing extension object not bounded to a packet. Useful for adding new extensions to an - * IPv6 layer with IPv6Layer#addExtension() - * @param[in] routingType Routing type value (will be written to ipv6_routing_header#routingType field) - * @param[in] segmentsLeft Segments left value (will be written to ipv6_routing_header#segmentsLeft field) - * @param[in] additionalRoutingData A pointer to a buffer containing the additional routing data for this extension. Notice this - * buffer is read-only and its content isn't modified - * @param[in] additionalRoutingDataLen The length of the additional routing data buffer - */ - IPv6RoutingHeader(uint8_t routingType, uint8_t segmentsLeft, const uint8_t* additionalRoutingData, size_t additionalRoutingDataLen); - - /** - * Get a pointer to the fixed part of the routing header. Notice the return pointer points directly to the data, so every change will modify - * the actual packet data - * @return A pointer to the @ref ipv6_routing_header - */ - ipv6_routing_header* getRoutingHeader() const { return (ipv6_routing_header*)getDataPtr(); } - - /** - * @return A pointer to the buffer containing the additional routing data for this extension. Notice that any change in this buffer - * will lead to a change in the extension data - */ - uint8_t* getRoutingAdditionalData() const; - - /** - * @return The length of the additional routing parameters buffer - */ - size_t getRoutingAdditionalDataLength() const; - - /** - * In many cases the additional routing data is actually IPv6 address(es). This method converts the raw buffer data into an IPv6 address - * @param[in] offset An offset in the additional routing buffer pointing to where the IPv6 address begins. In some cases there are - * multiple IPv6 addresses in the additional routing data buffer so this offset points to where the request IPv6 address begins. Also, - * even if there is only one IPv6 address in this buffer, sometimes it isn't written in the beginning of the buffer, so the offset points - * to where the IPv6 address begins. This is an optional parameter and the default offset is 0 - * @return The IPv6 address stored in the additional routing data buffer from the offset defined by the user. If offset is out-of-bounds - * of the extension of doesn't have 16 bytes (== the length of IPv6 address) until the end of the buffer - IPv6Address#Zero is returned - */ - IPv6Address getRoutingAdditionalDataAsIPv6Address(size_t offset = 0) const; - - private: - - IPv6RoutingHeader(IDataContainer* dataContainer, size_t offset) : IPv6Extension(dataContainer, offset) { m_ExtType = IPv6Routing; } - - }; - - - /** - * @class IPv6AuthenticationHeader - * Represents IPv6 authentication header extension (used in IPSec protocol) and allows easy access to all of its data - */ - class IPv6AuthenticationHeader : public IPv6Extension - { - friend class IPv6Layer; - - public: - - /** - * @struct ipv6_authentication_header - * A struct representing the fixed part of the IPv6 authentication header extension - */ - struct ipv6_authentication_header - { - /** Next header type */ - uint8_t nextHeader; - /** The length of this Authentication Header in 4-octet units, minus 2. For example, an AH value of 4 - * equals: [ 3×(32-bit fixed-length AH fields) + 3×(32-bit ICV fields) − 2 ] and thus an AH value of 4 means 24 octets */ - uint8_t headerLen; - /** Reserved bytes, all zeros */ - uint16_t reserved; - /** Arbitrary value which is used (together with the destination IP address) to identify the security association of the receiving party */ - uint32_t securityParametersIndex; - /** A monotonic strictly increasing sequence number (incremented by 1 for every packet sent) */ - uint32_t sequenceNumber; - }; - - /** - * A c'tor for creating a new IPv6 authentication header extension object not bounded to a packet. Useful for adding new extensions to an - * IPv6 layer with IPv6Layer#addExtension() - * @param[in] securityParametersIndex Security Parameters Index (SPI) value (will be written to ipv6_authentication_header#securityParametersIndex field) - * @param[in] sequenceNumber Sequence number value (will be written to ipv6_authentication_header#sequenceNumber field) - * @param[in] integrityCheckValue A pointer to a buffer containing the integrity check value (ICV) data for this extension. Notice this - * pointer is read-only and its content isn't modified in any way - * @param[in] integrityCheckValueLen The length of the integrity check value (ICV) buffer - */ - IPv6AuthenticationHeader(uint32_t securityParametersIndex, uint32_t sequenceNumber, const uint8_t* integrityCheckValue, size_t integrityCheckValueLen); - - /** - * Get a pointer to the fixed part of the authentication header. Notice the return pointer points directly to the data, so every change - * will modify the actual packet data - * @return A pointer to the @ref ipv6_authentication_header - */ - ipv6_authentication_header* getAuthHeader() const { return (ipv6_authentication_header*)getDataPtr(); } - - /** - * @return A pointer to the buffer containing the integrity check value (ICV) for this extension. Notice that any change in this buffer - * will lead to a change in the extension data - */ - uint8_t* getIntegrityCheckValue() const; - - /** - * @return The length of the integrity check value (ICV) buffer - */ - size_t getIntegrityCheckValueLength() const; - - // overridden methods - - /** - * In the authentication header the extension length is calculated in a different way than other extensions. The - * calculation is: [ 4 * (ipv6_authentication_header#headerLen + 2) ] - * @return The length of this extension - */ - size_t getExtensionLen() const { return 4 * (getBaseHeader()->headerLen+2); } - - private: - - IPv6AuthenticationHeader(IDataContainer* dataContainer, size_t offset) : IPv6Extension(dataContainer, offset) { m_ExtType = IPv6AuthenticationHdr; } - }; - -} +namespace pcpp { + +/** + * @class IPv6Extension + * A base class for all supported IPv6 extensions. This class is abstract, + * meaning it cannot be instantiated or copied (has private c'tor and copy + * c'tor) + */ +class IPv6Extension { + friend class IPv6Layer; + + public: + /** + * An enum representing all supported IPv6 extension types + */ + enum IPv6ExtensionType { + /** Hop-By-Hop extension type */ + IPv6HopByHop = 0, + /** Routing extension type */ + IPv6Routing = 43, + /** IPv6 fragmentation extension type */ + IPv6Fragmentation = 44, + /** Authentication Header extension type */ + IPv6AuthenticationHdr = 51, + /** Destination extension type */ + IPv6Destination = 60, + /** Unknown or unsupported extension type */ + IPv6ExtensionUnknown = 255 + }; + + /** + * @return The size of extension in bytes, meaning (for most extensions): 8 * + * ([headerLen field] + 1) + */ + virtual size_t getExtensionLen() const { + return 8 * (getBaseHeader()->headerLen + 1); + } + + /** + * @return The type of the extension + */ + IPv6ExtensionType getExtensionType() const { return m_ExtType; } + + /** + * A destructor for this class + */ + virtual ~IPv6Extension(); + + /** + * @return A pointer to the next header or NULL if the extension is the last + * one + */ + IPv6Extension* getNextHeader() const { return m_NextHeader; } + + protected: + struct ipv6_ext_base_header { + uint8_t nextHeader; + uint8_t headerLen; + }; + + // protected c'tor + IPv6Extension(IDataContainer* dataContainer, size_t offset) + : m_NextHeader(NULL), m_ExtType(IPv6ExtensionUnknown), + m_DataContainer(dataContainer), m_Offset(offset), m_ShadowData(NULL) {} + + // protected empty c'tor + IPv6Extension() + : m_NextHeader(NULL), m_ExtType(IPv6ExtensionUnknown), + m_DataContainer(NULL), m_Offset(0), m_ShadowData(NULL) {} + + // protected assignment operator + IPv6Extension& operator=(const IPv6Extension& other); + + uint8_t* getDataPtr() const; + + void initShadowPtr(size_t size); + + ipv6_ext_base_header* getBaseHeader() const { + return (ipv6_ext_base_header*)getDataPtr(); + } + + void setNextHeader(IPv6Extension* nextHeader) { m_NextHeader = nextHeader; } + + IPv6Extension* m_NextHeader; + IPv6ExtensionType m_ExtType; + + private: + IDataContainer* m_DataContainer; + size_t m_Offset; + uint8_t* m_ShadowData; +}; + +/** + * @class IPv6FragmentationHeader + * Represents an IPv6 fragmentation extension header and allows easy access to + * all fragmentation parameters + */ +class IPv6FragmentationHeader : public IPv6Extension { + friend class IPv6Layer; + + public: + /** + * @struct ipv6_frag_header + * A struct representing IPv6 fragmentation header + */ + struct ipv6_frag_header { + /** Next header type */ + uint8_t nextHeader; + /** Fragmentation header size is fixed 8 bytes, so len is always zero */ + uint8_t headerLen; + /** Offset, in 8-octet units, relative to the start of the fragmentable part + * of the original packet plus 1-bit indicating if more fragments will + * follow */ + uint16_t fragOffsetAndFlags; + /** packet identification value. Needed for reassembly of the original + * packet */ + uint32_t id; + }; + + /** + * A c'tor for creating a new IPv6 fragmentation extension object not bounded + * to a packet. Useful for adding new extensions to an IPv6 layer with + * IPv6Layer#addExtension() + * @param[in] fragId Fragmentation ID + * @param[in] fragOffset Fragmentation offset + * @param[in] lastFragment Indicates whether this fragment is the last one + */ + IPv6FragmentationHeader(uint32_t fragId, uint16_t fragOffset, + bool lastFragment); + + /** + * Get a pointer to the fragmentation header. Notice the returned pointer + * points directly to the data, so every change will modify the actual packet + * data + * @return A pointer to the @ref ipv6_frag_header + */ + ipv6_frag_header* getFragHeader() const { + return (ipv6_frag_header*)getDataPtr(); + } + + /** + * @return True if this is the first fragment (which usually contains the L4 + * header), false otherwise + */ + bool isFirstFragment() const; + + /** + * @return True if this is the last fragment, false otherwise + */ + bool isLastFragment() const; + + /** + * @return True if the "more fragments" bit is set, meaning more fragments are + * expected to follow this fragment + */ + bool isMoreFragments() const; + + /** + * @return The fragment offset + */ + uint16_t getFragmentOffset() const; + + private: + IPv6FragmentationHeader(IDataContainer* dataContainer, size_t offset) + : IPv6Extension(dataContainer, offset) { + m_ExtType = IPv6Fragmentation; + } +}; + +/** + * An abstract base class for Hop-By-Hop and Destination IPv6 extensions which + * their structure contains Type-Length-Value (TLV) options. This class provides + * access to these options and their data as well as methods to create new + * options. Notice this class is abstract and cannot be instantiated + */ +class IPv6TLVOptionHeader : public IPv6Extension { + friend class IPv6Layer; + + public: + /** + * @class IPv6Option + * A class representing a Type-Length-Value (TLV) options that are used inside + * Hop-By-Hop and Destinations IPv6 extensions. This class does not create or + * modify IPv6 option records, but rather serves as a wrapper and provides + * useful methods for retrieving data from them + */ + class IPv6Option : public TLVRecord { + public: + static const uint8_t Pad0OptionType = 0; + static const uint8_t PadNOptionType = 1; + + /** + * A c'tor for this class that gets a pointer to the option raw data (byte + * array) + * @param[in] optionRawData A pointer to the attribute raw data + */ + explicit IPv6Option(uint8_t* optionRawData) : TLVRecord(optionRawData) {} + + /** + * A d'tor for this class, currently does nothing + */ + ~IPv6Option() {} + + /** + * Check if a pointer can be assigned to the TLV record data + * @param[in] recordRawData A pointer to the TLV record raw data + * @param[in] tlvDataLen The size of the TLV record raw data + * @return True if data is valid and can be assigned + */ + static bool canAssign(const uint8_t* recordRawData, size_t tlvDataLen) { + auto data = (TLVRawData*)recordRawData; + if (data == nullptr) + return false; + + if (tlvDataLen < sizeof(TLVRawData::recordType)) + return false; + + if (data->recordType == Pad0OptionType) + return true; + + return TLVRecord::canAssign(recordRawData, tlvDataLen); + } + + // implement abstract methods + + size_t getTotalSize() const { + if (m_Data == nullptr) + return 0; + + if (m_Data->recordType == Pad0OptionType) + return sizeof(uint8_t); + + return (size_t)(m_Data->recordLen + sizeof(uint16_t)); + } + + size_t getDataSize() const { + if (m_Data == nullptr || m_Data->recordType == Pad0OptionType) + return 0; + + return (size_t)m_Data->recordLen; + } + }; + + /** + * @class IPv6TLVOptionBuilder + * A class for building IPv6 Type-Length-Value (TLV) options. This builder + * receives the option parameters in its c'tor, builds the option raw buffer + * and provides a method to build a IPv6Option object out of it + */ + class IPv6TLVOptionBuilder : public TLVRecordBuilder { + public: + /** + * A c'tor for building IPv6 TLV options which their value is a byte array. + * The IPv6Option object can later be retrieved by calling build() + * @param[in] optType IPv6 option type + * @param[in] optValue A buffer containing the option value. This buffer is + * read-only and isn't modified in any way + * @param[in] optValueLen Option value length in bytes + */ + IPv6TLVOptionBuilder(uint8_t optType, const uint8_t* optValue, + uint8_t optValueLen) + : TLVRecordBuilder(optType, optValue, optValueLen) {} + + /** + * A c'tor for building IPv6 TLV options which have a 1-byte value. The + * IPv6Option object can later be retrieved by calling build() + * @param[in] optType IPv6 option type + * @param[in] optValue A 1-byte option value + */ + IPv6TLVOptionBuilder(uint8_t optType, uint8_t optValue) + : TLVRecordBuilder(optType, optValue) {} + + /** + * A c'tor for building IPv6 TLV options which have a 2-byte value. The + * IPv6Option object can later be retrieved by calling build() + * @param[in] optType IPv6 option type + * @param[in] optValue A 2-byte option value + */ + IPv6TLVOptionBuilder(uint8_t optType, uint16_t optValue) + : TLVRecordBuilder(optType, optValue) {} + + /** + * A copy c'tor that creates an instance of this class out of another + * instance and copies all the data from it + * @param[in] other The instance to copy data from + */ + IPv6TLVOptionBuilder(const IPv6TLVOptionBuilder& other) + : TLVRecordBuilder(other) {} + + /** + * Assignment operator that copies all data from another instance of + * IPv6TLVOptionBuilder + * @param[in] other The instance to assign from + */ + IPv6TLVOptionBuilder& operator=(const IPv6TLVOptionBuilder& other) { + TLVRecordBuilder::operator=(other); + return *this; + } + + /** + * Build the IPv6Option object out of the parameters defined in the c'tor + * @return The IPv6Option object + */ + IPv6Option build() const; + }; + + /** + * Retrieve an option by its type + * @param[in] optionType Option type + * @return An IPv6Option object that wraps the option data. If option isn't + * found a logical NULL is returned (IPv6Option#isNull() == true) + */ + IPv6Option getOption(uint8_t optionType) const; + + /** + * @return An IPv6Option that wraps the first option data or logical NULL + * (IPv6Option#isNull() == true) if no options exist + */ + IPv6Option getFirstOption() const; + + /** + * Returns a pointer to the option that comes after the option given as the + * parameter + * @param[in] option A pointer to an option instance + * @return An IPv6Option object that wraps the option data. In the following + * cases logical NULL (IPv6Option#isNull() == true) is returned: (1) input + * parameter is out-of-bounds for this extension or (2) the next option + * doesn't exist or (3) the input option is NULL + */ + IPv6Option getNextOption(IPv6Option& option) const; + + /** + * @returns The number of options this IPv6 extension contains + */ + size_t getOptionCount() const; + + protected: + /** A private c'tor to keep this object from being constructed */ + explicit IPv6TLVOptionHeader( + const std::vector& options); + + IPv6TLVOptionHeader(IDataContainer* dataContainer, size_t offset); + + private: + TLVRecordReader m_OptionReader; +}; + +/** + * @class IPv6HopByHopHeader + * Represents IPv6 Hop-By-Hop extension header and allows easy access to all of + * its data including the TLV options stored + */ +class IPv6HopByHopHeader : public IPv6TLVOptionHeader { + friend class IPv6Layer; + + public: + /** + * A c'tor for creating a new IPv6 Hop-By-Hop extension object not bounded to + * a packet. Useful for adding new extensions to an IPv6 layer with + * IPv6Layer#addExtension() + * @param[in] options A vector of IPv6TLVOptionHeader#TLVOptionBuilder + * instances which define the options that will be stored in the extension + * data. Notice this vector is read-only and its content won't be modified + */ + explicit IPv6HopByHopHeader(const std::vector& options) + : IPv6TLVOptionHeader(options) { + m_ExtType = IPv6HopByHop; + } + + private: + IPv6HopByHopHeader(IDataContainer* dataContainer, size_t offset) + : IPv6TLVOptionHeader(dataContainer, offset) { + m_ExtType = IPv6HopByHop; + } +}; + +/** + * @class IPv6DestinationHeader + * Represents IPv6 destination extension header and allows easy access to all of + * its data including the TLV options stored in it + */ +class IPv6DestinationHeader : public IPv6TLVOptionHeader { + friend class IPv6Layer; + + public: + /** + * A c'tor for creating a new IPv6 destination extension object not bounded to + * a packet. Useful for adding new extensions to an IPv6 layer with + * IPv6Layer#addExtension() + * @param[in] options A vector of IPv6TLVOptionHeader#TLVOptionBuilder + * instances which define the options that will be stored in the extension + * data. Notice this vector is read-only and its content won't be modified + */ + explicit IPv6DestinationHeader( + const std::vector& options) + : IPv6TLVOptionHeader(options) { + m_ExtType = IPv6Destination; + } + + private: + IPv6DestinationHeader(IDataContainer* dataContainer, size_t offset) + : IPv6TLVOptionHeader(dataContainer, offset) { + m_ExtType = IPv6Destination; + } +}; + +/** + * @class IPv6RoutingHeader + * Represents IPv6 routing extension header and allows easy access to all of its + * data + */ +class IPv6RoutingHeader : public IPv6Extension { + friend class IPv6Layer; + + public: + /** + * @struct ipv6_routing_header + * A struct representing the fixed part of the IPv6 routing extension header + */ + struct ipv6_routing_header { + /** Next header type */ + uint8_t nextHeader; + /** The length of this header, in multiples of 8 octets, not including the + * first 8 octets */ + uint8_t headerLen; + /** A value representing the routing type */ + uint8_t routingType; + /** Number of nodes this packet still has to visit before reaching its final + * destination */ + uint8_t segmentsLeft; + }; + + /** + * A c'tor for creating a new IPv6 routing extension object not bounded to a + * packet. Useful for adding new extensions to an IPv6 layer with + * IPv6Layer#addExtension() + * @param[in] routingType Routing type value (will be written to + * ipv6_routing_header#routingType field) + * @param[in] segmentsLeft Segments left value (will be written to + * ipv6_routing_header#segmentsLeft field) + * @param[in] additionalRoutingData A pointer to a buffer containing the + * additional routing data for this extension. Notice this buffer is read-only + * and its content isn't modified + * @param[in] additionalRoutingDataLen The length of the additional routing + * data buffer + */ + IPv6RoutingHeader(uint8_t routingType, uint8_t segmentsLeft, + const uint8_t* additionalRoutingData, + size_t additionalRoutingDataLen); + + /** + * Get a pointer to the fixed part of the routing header. Notice the return + * pointer points directly to the data, so every change will modify the actual + * packet data + * @return A pointer to the @ref ipv6_routing_header + */ + ipv6_routing_header* getRoutingHeader() const { + return (ipv6_routing_header*)getDataPtr(); + } + + /** + * @return A pointer to the buffer containing the additional routing data for + * this extension. Notice that any change in this buffer will lead to a change + * in the extension data + */ + uint8_t* getRoutingAdditionalData() const; + + /** + * @return The length of the additional routing parameters buffer + */ + size_t getRoutingAdditionalDataLength() const; + + /** + * In many cases the additional routing data is actually IPv6 address(es). + * This method converts the raw buffer data into an IPv6 address + * @param[in] offset An offset in the additional routing buffer pointing to + * where the IPv6 address begins. In some cases there are multiple IPv6 + * addresses in the additional routing data buffer so this offset points to + * where the request IPv6 address begins. Also, even if there is only one IPv6 + * address in this buffer, sometimes it isn't written in the beginning of the + * buffer, so the offset points to where the IPv6 address begins. This is an + * optional parameter and the default offset is 0 + * @return The IPv6 address stored in the additional routing data buffer from + * the offset defined by the user. If offset is out-of-bounds of the extension + * of doesn't have 16 bytes (== the length of IPv6 address) until the end of + * the buffer - IPv6Address#Zero is returned + */ + IPv6Address getRoutingAdditionalDataAsIPv6Address(size_t offset = 0) const; + + private: + IPv6RoutingHeader(IDataContainer* dataContainer, size_t offset) + : IPv6Extension(dataContainer, offset) { + m_ExtType = IPv6Routing; + } +}; + +/** + * @class IPv6AuthenticationHeader + * Represents IPv6 authentication header extension (used in IPSec protocol) and + * allows easy access to all of its data + */ +class IPv6AuthenticationHeader : public IPv6Extension { + friend class IPv6Layer; + + public: + /** + * @struct ipv6_authentication_header + * A struct representing the fixed part of the IPv6 authentication header + * extension + */ + struct ipv6_authentication_header { + /** Next header type */ + uint8_t nextHeader; + /** The length of this Authentication Header in 4-octet units, minus 2. For + * example, an AH value of 4 equals: [ 3×(32-bit fixed-length AH fields) + + * 3×(32-bit ICV fields) − 2 ] and thus an AH value of 4 means 24 octets */ + uint8_t headerLen; + /** Reserved bytes, all zeros */ + uint16_t reserved; + /** Arbitrary value which is used (together with the destination IP address) + * to identify the security association of the receiving party */ + uint32_t securityParametersIndex; + /** A monotonic strictly increasing sequence number (incremented by 1 for + * every packet sent) */ + uint32_t sequenceNumber; + }; + + /** + * A c'tor for creating a new IPv6 authentication header extension object not + * bounded to a packet. Useful for adding new extensions to an IPv6 layer with + * IPv6Layer#addExtension() + * @param[in] securityParametersIndex Security Parameters Index (SPI) value + * (will be written to ipv6_authentication_header#securityParametersIndex + * field) + * @param[in] sequenceNumber Sequence number value (will be written to + * ipv6_authentication_header#sequenceNumber field) + * @param[in] integrityCheckValue A pointer to a buffer containing the + * integrity check value (ICV) data for this extension. Notice this pointer is + * read-only and its content isn't modified in any way + * @param[in] integrityCheckValueLen The length of the integrity check value + * (ICV) buffer + */ + IPv6AuthenticationHeader(uint32_t securityParametersIndex, + uint32_t sequenceNumber, + const uint8_t* integrityCheckValue, + size_t integrityCheckValueLen); + + /** + * Get a pointer to the fixed part of the authentication header. Notice the + * return pointer points directly to the data, so every change will modify the + * actual packet data + * @return A pointer to the @ref ipv6_authentication_header + */ + ipv6_authentication_header* getAuthHeader() const { + return (ipv6_authentication_header*)getDataPtr(); + } + + /** + * @return A pointer to the buffer containing the integrity check value (ICV) + * for this extension. Notice that any change in this buffer will lead to a + * change in the extension data + */ + uint8_t* getIntegrityCheckValue() const; + + /** + * @return The length of the integrity check value (ICV) buffer + */ + size_t getIntegrityCheckValueLength() const; + + // overridden methods + + /** + * In the authentication header the extension length is calculated in a + * different way than other extensions. The calculation is: [ 4 * + * (ipv6_authentication_header#headerLen + 2) ] + * @return The length of this extension + */ + size_t getExtensionLen() const { + return 4 * (getBaseHeader()->headerLen + 2); + } + + private: + IPv6AuthenticationHeader(IDataContainer* dataContainer, size_t offset) + : IPv6Extension(dataContainer, offset) { + m_ExtType = IPv6AuthenticationHdr; + } +}; + +} // namespace pcpp #endif // PACKETPP_IPV6_EXTENSION diff --git a/Packet++/header/IPv6Layer.h b/Packet++/header/IPv6Layer.h index 859b2a8451..fd526b2eae 100644 --- a/Packet++/header/IPv6Layer.h +++ b/Packet++/header/IPv6Layer.h @@ -1,10 +1,10 @@ #ifndef PACKETPP_IPV6_LAYER #define PACKETPP_IPV6_LAYER -#include "Layer.h" #include "IPLayer.h" #include "IPv6Extensions.h" #include "IpAddress.h" +#include "Layer.h" /// @file @@ -12,267 +12,278 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ +namespace pcpp { - /** - * @struct ip6_hdr - * Represents IPv6 protocol header - */ +/** + * @struct ip6_hdr + * Represents IPv6 protocol header + */ #pragma pack(push, 1) - struct ip6_hdr - { - #if (BYTE_ORDER == LITTLE_ENDIAN) - /** Traffic class */ - uint8_t trafficClass:4, - /** IP version number, has the value of 6 for IPv6 */ - ipVersion:4; - #else - /** IP version number, has the value of 6 for IPv6 */ - uint8_t ipVersion:4, - /** Traffic class */ - trafficClass:4; - #endif - /** Flow label */ - uint8_t flowLabel[3]; - /** The size of the payload in octets, including any extension headers */ - uint16_t payloadLength; - /** Specifies the type of the next header (protocol). Must be one of ::IPProtocolTypes */ - uint8_t nextHeader; - /** Replaces the time to live field of IPv4 */ - uint8_t hopLimit; - /** Source address */ - uint8_t ipSrc[16]; - /** Destination address */ - uint8_t ipDst[16]; - }; +struct ip6_hdr { +#if (BYTE_ORDER == LITTLE_ENDIAN) + /** Traffic class */ + uint8_t trafficClass : 4, + /** IP version number, has the value of 6 for IPv6 */ + ipVersion : 4; +#else + /** IP version number, has the value of 6 for IPv6 */ + uint8_t ipVersion : 4, + /** Traffic class */ + trafficClass : 4; +#endif + /** Flow label */ + uint8_t flowLabel[3]; + /** The size of the payload in octets, including any extension headers */ + uint16_t payloadLength; + /** Specifies the type of the next header (protocol). Must be one of + * ::IPProtocolTypes */ + uint8_t nextHeader; + /** Replaces the time to live field of IPv4 */ + uint8_t hopLimit; + /** Source address */ + uint8_t ipSrc[16]; + /** Destination address */ + uint8_t ipDst[16]; +}; #pragma pack(pop) - - /** - * @class IPv6Layer - * Represents an IPv6 protocol layer - */ - class IPv6Layer : public Layer, public IPLayer - { - public: - /** - * A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data (will be casted to @ref ip6_hdr) - * @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 - */ - IPv6Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); - - /** - * A constructor that allocates a new IPv6 header with empty fields - */ - IPv6Layer(); - - /** - * A constructor that allocates a new IPv6 header with source and destination IPv6 addresses - * @param[in] srcIP Source IPv6 address - * @param[in] dstIP Destination IPv6 address - */ - IPv6Layer(const IPv6Address& srcIP, const IPv6Address& dstIP); - - /** - * A copy constructor that copies the entire header from the other IPv6Layer (including IPv6 extensions) - */ - IPv6Layer(const IPv6Layer& other); - - /** - * A destructor for this layer - */ - ~IPv6Layer(); - - /** - * An assignment operator that first delete all data from current layer and then copy the entire header from the other IPv6Layer (including IPv6 extensions) - */ - IPv6Layer& operator=(const IPv6Layer& other); - - /** - * Get a pointer to the IPv6 header. Notice this points directly to the data, so every change will change the actual packet data - * @return A pointer to the @ref ip6_hdr - */ - ip6_hdr* getIPv6Header() const { return (ip6_hdr*)m_Data; } - - /** - * Get the source IP address in the form of IPAddress. This method is very similar to getSrcIPv6Address(), - * but adds a level of abstraction because IPAddress can be used for both IPv4 and IPv6 addresses - * @return An IPAddress containing the source address - */ - IPAddress getSrcIPAddress() const { return getSrcIPv6Address(); } - - /** - * Get the source IP address in the form of IPv6Address - * @return An IPv6Address containing the source address - */ - IPv6Address getSrcIPv6Address() const { return getIPv6Header()->ipSrc; } - - /** - * Set the source IP address - * @param[in] ipAddr The IP address to set - */ - void setSrcIPv6Address(const IPv6Address& ipAddr) { ipAddr.copyTo(getIPv6Header()->ipSrc); } - - - /** - * Set the dest IP address - * @param[in] ipAddr The IP address to set - */ - void setDstIPv6Address(const IPv6Address& ipAddr) { ipAddr.copyTo(getIPv6Header()->ipDst); } - - /** - * Get the destination IP address in the form of IPAddress. This method is very similar to getDstIPv6Address(), - * but adds a level of abstraction because IPAddress can be used for both IPv4 and IPv6 addresses - * @return An IPAddress containing the destination address - */ - IPAddress getDstIPAddress() const { return getDstIPv6Address(); } - - /** - * Get the destination IP address in the form of IPv6Address - * @return An IPv6Address containing the destination address - */ - IPv6Address getDstIPv6Address() const { return getIPv6Header()->ipDst; } - - /** - * @return Number of IPv6 extensions in this layer - */ - size_t getExtensionCount() const; - - /** - * A templated getter for an IPv6 extension of a type TIPv6Extension. TIPv6Extension has to be one of the supported IPv6 extensions, - * meaning a class that inherits IPv6Extension. If the requested extension type isn't found NULL is returned - * @return A pointer to the extension instance or NULL if the requested extension type isn't found - */ - template - TIPv6Extension* getExtensionOfType() const; - - /** - * Add a new extension of type TIPv6Extension to the layer. This is a templated method and TIPv6Extension has to be one of - * the supported IPv6 extensions, meaning a class that inherits IPv6Extension. If the extension is added successfully a pointer - * to the newly added extension object is returned, otherwise NULL is returned - * @param[in] extensionHeader The extension object to add. Notice the object passed here is read-only, meaning its data is copied - * but the object itself isn't modified - * @return If the extension is added successfully a pointer to the newly added extension object is returned. Otherwise NULL is - * returned - */ - template - TIPv6Extension* addExtension(const TIPv6Extension& extensionHeader); - - /** - * Remove all IPv6 extensions in this layer - */ - void removeAllExtensions(); - - /** - * @return True if this packet is an IPv6 fragment, meaning if it has an IPv6FragmentationHeader extension - */ - bool isFragment() const; - - /** - * The static method makes validation of input data - * @param[in] data The pointer to the beginning of byte stream of IP packet - * @param[in] dataLen The length of byte stream - * @return True if the data is valid and can represent the IPv6 packet - */ - static inline bool isDataValid(const uint8_t* data, size_t dataLen); - - - // implement abstract methods - - /** - * Currently identifies the following next layers: - * - UdpLayer - * - TcpLayer - * - IPv4Layer (IP-in-IP) - * - IPv6Layer (IP-in-IP) - * - GreLayer - * - AuthenticationHeaderLayer (IPSec) - * - ESPLayer (IPSec) - * - * Otherwise sets PayloadLayer - */ - void parseNextLayer(); - - /** - * @return Size of @ref ip6_hdr - */ - size_t getHeaderLen() const { return sizeof(ip6_hdr) + m_ExtensionsLen; } - - /** - * Calculate the following fields: - * - ip6_hdr#payloadLength = size of payload (all data minus header size) - * - ip6_hdr#ipVersion = 6 - * - ip6_hdr#nextHeader = calculated if next layer is known: ::PACKETPP_IPPROTO_TCP for TCP, ::PACKETPP_IPPROTO_UDP for UDP, ::PACKETPP_IPPROTO_ICMP for ICMP - */ - void computeCalculateFields(); - - std::string toString() const; - - OsiModelLayer getOsiModelLayer() const { return OsiModelNetworkLayer; } - - private: - void initLayer(); - void parseExtensions(); - void deleteExtensions(); - - IPv6Extension* m_FirstExtension; - IPv6Extension* m_LastExtension; - size_t m_ExtensionsLen; - }; - - - template - TIPv6Extension* IPv6Layer::getExtensionOfType() const - { - IPv6Extension* curExt = m_FirstExtension; - while (curExt != NULL && dynamic_cast(curExt) == NULL) - curExt = curExt->getNextHeader(); - - return (TIPv6Extension*)curExt; - } - - template - TIPv6Extension* IPv6Layer::addExtension(const TIPv6Extension& extensionHeader) - { - int offsetToAddHeader = (int)getHeaderLen(); - if (!extendLayer(offsetToAddHeader, extensionHeader.getExtensionLen())) - { - return NULL; - } - - TIPv6Extension* newHeader = new TIPv6Extension(this, (size_t)offsetToAddHeader); - (*newHeader) = extensionHeader; - - if (m_FirstExtension != NULL) - { - newHeader->getBaseHeader()->nextHeader = m_LastExtension->getBaseHeader()->nextHeader; - m_LastExtension->getBaseHeader()->nextHeader = newHeader->getExtensionType(); - m_LastExtension->setNextHeader(newHeader); - m_LastExtension = newHeader; - } - else - { - m_FirstExtension = newHeader; - m_LastExtension = newHeader; - newHeader->getBaseHeader()->nextHeader = getIPv6Header()->nextHeader; - getIPv6Header()->nextHeader = newHeader->getExtensionType(); - } - - m_ExtensionsLen += newHeader->getExtensionLen(); - - return newHeader; - } - - // implementation of inline methods - - bool IPv6Layer::isDataValid(const uint8_t* data, size_t dataLen) - { - return data && dataLen >= sizeof(ip6_hdr); - } +/** + * @class IPv6Layer + * Represents an IPv6 protocol layer + */ +class IPv6Layer : public Layer, public IPLayer { + public: + /** + * A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data (will be casted to @ref ip6_hdr) + * @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 + */ + IPv6Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); + + /** + * A constructor that allocates a new IPv6 header with empty fields + */ + IPv6Layer(); + + /** + * A constructor that allocates a new IPv6 header with source and destination + * IPv6 addresses + * @param[in] srcIP Source IPv6 address + * @param[in] dstIP Destination IPv6 address + */ + IPv6Layer(const IPv6Address& srcIP, const IPv6Address& dstIP); + + /** + * A copy constructor that copies the entire header from the other IPv6Layer + * (including IPv6 extensions) + */ + IPv6Layer(const IPv6Layer& other); + + /** + * A destructor for this layer + */ + ~IPv6Layer(); + + /** + * An assignment operator that first delete all data from current layer and + * then copy the entire header from the other IPv6Layer (including IPv6 + * extensions) + */ + IPv6Layer& operator=(const IPv6Layer& other); + + /** + * Get a pointer to the IPv6 header. Notice this points directly to the data, + * so every change will change the actual packet data + * @return A pointer to the @ref ip6_hdr + */ + ip6_hdr* getIPv6Header() const { return (ip6_hdr*)m_Data; } + + /** + * Get the source IP address in the form of IPAddress. This method is very + * similar to getSrcIPv6Address(), but adds a level of abstraction because + * IPAddress can be used for both IPv4 and IPv6 addresses + * @return An IPAddress containing the source address + */ + IPAddress getSrcIPAddress() const { return getSrcIPv6Address(); } + + /** + * Get the source IP address in the form of IPv6Address + * @return An IPv6Address containing the source address + */ + IPv6Address getSrcIPv6Address() const { return getIPv6Header()->ipSrc; } + + /** + * Set the source IP address + * @param[in] ipAddr The IP address to set + */ + void setSrcIPv6Address(const IPv6Address& ipAddr) { + ipAddr.copyTo(getIPv6Header()->ipSrc); + } + + /** + * Set the dest IP address + * @param[in] ipAddr The IP address to set + */ + void setDstIPv6Address(const IPv6Address& ipAddr) { + ipAddr.copyTo(getIPv6Header()->ipDst); + } + + /** + * Get the destination IP address in the form of IPAddress. This method is + * very similar to getDstIPv6Address(), but adds a level of abstraction + * because IPAddress can be used for both IPv4 and IPv6 addresses + * @return An IPAddress containing the destination address + */ + IPAddress getDstIPAddress() const { return getDstIPv6Address(); } + + /** + * Get the destination IP address in the form of IPv6Address + * @return An IPv6Address containing the destination address + */ + IPv6Address getDstIPv6Address() const { return getIPv6Header()->ipDst; } + + /** + * @return Number of IPv6 extensions in this layer + */ + size_t getExtensionCount() const; + + /** + * A templated getter for an IPv6 extension of a type TIPv6Extension. + * TIPv6Extension has to be one of the supported IPv6 extensions, meaning a + * class that inherits IPv6Extension. If the requested extension type isn't + * found NULL is returned + * @return A pointer to the extension instance or NULL if the requested + * extension type isn't found + */ + template + TIPv6Extension* getExtensionOfType() const; + + /** + * Add a new extension of type TIPv6Extension to the layer. This is a + * templated method and TIPv6Extension has to be one of the supported IPv6 + * extensions, meaning a class that inherits IPv6Extension. If the extension + * is added successfully a pointer to the newly added extension object is + * returned, otherwise NULL is returned + * @param[in] extensionHeader The extension object to add. Notice the object + * passed here is read-only, meaning its data is copied but the object itself + * isn't modified + * @return If the extension is added successfully a pointer to the newly added + * extension object is returned. Otherwise NULL is returned + */ + template + TIPv6Extension* addExtension(const TIPv6Extension& extensionHeader); + + /** + * Remove all IPv6 extensions in this layer + */ + void removeAllExtensions(); + + /** + * @return True if this packet is an IPv6 fragment, meaning if it has an + * IPv6FragmentationHeader extension + */ + bool isFragment() const; + + /** + * The static method makes validation of input data + * @param[in] data The pointer to the beginning of byte stream of IP packet + * @param[in] dataLen The length of byte stream + * @return True if the data is valid and can represent the IPv6 packet + */ + static inline bool isDataValid(const uint8_t* data, size_t dataLen); + + // implement abstract methods + + /** + * Currently identifies the following next layers: + * - UdpLayer + * - TcpLayer + * - IPv4Layer (IP-in-IP) + * - IPv6Layer (IP-in-IP) + * - GreLayer + * - AuthenticationHeaderLayer (IPSec) + * - ESPLayer (IPSec) + * + * Otherwise sets PayloadLayer + */ + void parseNextLayer(); + + /** + * @return Size of @ref ip6_hdr + */ + size_t getHeaderLen() const { return sizeof(ip6_hdr) + m_ExtensionsLen; } + + /** + * Calculate the following fields: + * - ip6_hdr#payloadLength = size of payload (all data minus header size) + * - ip6_hdr#ipVersion = 6 + * - ip6_hdr#nextHeader = calculated if next layer is known: + * ::PACKETPP_IPPROTO_TCP for TCP, ::PACKETPP_IPPROTO_UDP for UDP, + * ::PACKETPP_IPPROTO_ICMP for ICMP + */ + void computeCalculateFields(); + + std::string toString() const; + + OsiModelLayer getOsiModelLayer() const { return OsiModelNetworkLayer; } + + private: + void initLayer(); + void parseExtensions(); + void deleteExtensions(); + + IPv6Extension* m_FirstExtension; + IPv6Extension* m_LastExtension; + size_t m_ExtensionsLen; +}; + +template +TIPv6Extension* IPv6Layer::getExtensionOfType() const { + IPv6Extension* curExt = m_FirstExtension; + while (curExt != NULL && dynamic_cast(curExt) == NULL) + curExt = curExt->getNextHeader(); + + return (TIPv6Extension*)curExt; +} + +template +TIPv6Extension* IPv6Layer::addExtension(const TIPv6Extension& extensionHeader) { + int offsetToAddHeader = (int)getHeaderLen(); + if (!extendLayer(offsetToAddHeader, extensionHeader.getExtensionLen())) { + return NULL; + } + + TIPv6Extension* newHeader = + new TIPv6Extension(this, (size_t)offsetToAddHeader); + (*newHeader) = extensionHeader; + + if (m_FirstExtension != NULL) { + newHeader->getBaseHeader()->nextHeader = + m_LastExtension->getBaseHeader()->nextHeader; + m_LastExtension->getBaseHeader()->nextHeader = + newHeader->getExtensionType(); + m_LastExtension->setNextHeader(newHeader); + m_LastExtension = newHeader; + } else { + m_FirstExtension = newHeader; + m_LastExtension = newHeader; + newHeader->getBaseHeader()->nextHeader = getIPv6Header()->nextHeader; + getIPv6Header()->nextHeader = newHeader->getExtensionType(); + } + + m_ExtensionsLen += newHeader->getExtensionLen(); + + return newHeader; +} + +// implementation of inline methods + +bool IPv6Layer::isDataValid(const uint8_t* data, size_t dataLen) { + return data && dataLen >= sizeof(ip6_hdr); +} } // namespace pcpp diff --git a/Packet++/header/IcmpLayer.h b/Packet++/header/IcmpLayer.h index 4af06030d5..92dee2c176 100644 --- a/Packet++/header/IcmpLayer.h +++ b/Packet++/header/IcmpLayer.h @@ -1,8 +1,8 @@ #ifndef PACKETPP_ICMP_LAYER #define PACKETPP_ICMP_LAYER -#include "Layer.h" #include "IPv4Layer.h" +#include "Layer.h" #ifdef _MSC_VER #include #else @@ -10,745 +10,803 @@ #endif #include - /// @file /** * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ +namespace pcpp { - /** - * @struct icmphdr - * Represents ICMP basic protocol header (common for all ICMP message types) - */ +/** + * @struct icmphdr + * Represents ICMP basic protocol header (common for all ICMP message types) + */ #pragma pack(push, 1) - typedef struct icmphdr - { - /** message type */ - uint8_t type; - /** message code */ - uint8_t code; - /** message checksum */ - uint16_t checksum; - } icmphdr; +typedef struct icmphdr { + /** message type */ + uint8_t type; + /** message code */ + uint8_t code; + /** message checksum */ + uint16_t checksum; +} icmphdr; #pragma pack(pop) - /** - * An enum of all supported ICMP message types - */ - enum IcmpMessageType - { - /** ICMP echo (ping) reply message */ - ICMP_ECHO_REPLY = 0, - /** ICMP destination unreachable message */ - ICMP_DEST_UNREACHABLE = 3, - /** ICMP source quench message */ - ICMP_SOURCE_QUENCH = 4, - /** ICMP redirect message */ - ICMP_REDIRECT = 5, - /** ICMP echo (ping) request message */ - ICMP_ECHO_REQUEST = 8, - /** ICMP router advertisement message */ - ICMP_ROUTER_ADV = 9, - /** ICMP router soliciatation message */ - ICMP_ROUTER_SOL = 10, - /** ICMP time-to-live excceded message */ - ICMP_TIME_EXCEEDED = 11, - /** ICMP parameter problem message */ - ICMP_PARAM_PROBLEM = 12, - /** ICMP timestamp request message */ - ICMP_TIMESTAMP_REQUEST = 13, - /** ICMP timestamp reply message */ - ICMP_TIMESTAMP_REPLY = 14, - /** ICMP information request message */ - ICMP_INFO_REQUEST = 15, - /** ICMP information reply message */ - ICMP_INFO_REPLY = 16, - /** ICMP address mask request message */ - ICMP_ADDRESS_MASK_REQUEST = 17, - /** ICMP address mask reply message */ - ICMP_ADDRESS_MASK_REPLY = 18, - /** ICMP message type unsupported by PcapPlusPlus */ - ICMP_UNSUPPORTED = 255 - }; - - /** - * An enum for all possible codes for a destination unreachable message type - * Documentation is taken from Wikipedia: https://en.wikipedia.org/wiki/Internet_Control_Message_Protocol - */ - enum IcmpDestUnreachableCodes - { - /** Network unreachable error */ - IcmpNetworkUnreachable = 0, - /** Host unreachable error */ - IcmpHostUnreachable = 1, - /** Protocol unreachable error (the designated transport protocol is not supported) */ - IcmpProtocolUnreachable = 2, - /** Port unreachable error (the designated protocol is unable to inform the host of the incoming message) */ - IcmpPortUnreachable = 3, - /** The datagram is too big. Packet fragmentation is required but the 'don't fragment' (DF) flag is on */ - IcmpDatagramTooBig = 4, - /** Source route failed error */ - IcmpSourceRouteFailed = 5, - /** Destination network unknown error */ - IcmpDestinationNetworkUnknown = 6, - /** Destination host unknown error */ - IcmpDestinationHostUnknown = 7, - /** Source host isolated error */ - IcmpSourceHostIsolated = 8, - /** The destination network is administratively prohibited */ - IcmpDestinationNetworkProhibited = 9, - /** The destination host is administratively prohibited */ - IcmpDestinationHostProhibited = 10, - /** The network is unreachable for Type Of Service */ - IcmpNetworkUnreachableForTypeOfService = 11, - /** The host is unreachable for Type Of Service */ - IcmpHostUnreachableForTypeOfService = 12, - /** Communication administratively prohibited (administrative filtering prevents - * packet from being forwarded) - */ - IcmpCommunicationProhibited = 13, - /** Host precedence violation (indicates the requested precedence is not permitted for - * the combination of host or network and port) - */ - IcmpHostPrecedenceViolation = 14, - /** Precedence cutoff in effect (precedence of datagram is below the level set by - * the network administrators) - */ - IcmpPrecedenceCutoff = 15 - }; - - - /** - * @struct icmp_echo_hdr - * ICMP echo (ping) request/reply message structure - */ -#pragma pack(push, 1) - typedef struct icmp_echo_hdr : icmphdr - { - /** the echo (ping) request identifier */ - uint16_t id; - /** the echo (ping) request sequence number */ - uint16_t sequence; - /** a timestamp of when the message was sent */ - uint64_t timestamp; - } icmp_echo_hdr; -#pragma pack(pop) +/** + * An enum of all supported ICMP message types + */ +enum IcmpMessageType { + /** ICMP echo (ping) reply message */ + ICMP_ECHO_REPLY = 0, + /** ICMP destination unreachable message */ + ICMP_DEST_UNREACHABLE = 3, + /** ICMP source quench message */ + ICMP_SOURCE_QUENCH = 4, + /** ICMP redirect message */ + ICMP_REDIRECT = 5, + /** ICMP echo (ping) request message */ + ICMP_ECHO_REQUEST = 8, + /** ICMP router advertisement message */ + ICMP_ROUTER_ADV = 9, + /** ICMP router soliciatation message */ + ICMP_ROUTER_SOL = 10, + /** ICMP time-to-live excceded message */ + ICMP_TIME_EXCEEDED = 11, + /** ICMP parameter problem message */ + ICMP_PARAM_PROBLEM = 12, + /** ICMP timestamp request message */ + ICMP_TIMESTAMP_REQUEST = 13, + /** ICMP timestamp reply message */ + ICMP_TIMESTAMP_REPLY = 14, + /** ICMP information request message */ + ICMP_INFO_REQUEST = 15, + /** ICMP information reply message */ + ICMP_INFO_REPLY = 16, + /** ICMP address mask request message */ + ICMP_ADDRESS_MASK_REQUEST = 17, + /** ICMP address mask reply message */ + ICMP_ADDRESS_MASK_REPLY = 18, + /** ICMP message type unsupported by PcapPlusPlus */ + ICMP_UNSUPPORTED = 255 +}; +/** + * An enum for all possible codes for a destination unreachable message type + * Documentation is taken from Wikipedia: + * https://en.wikipedia.org/wiki/Internet_Control_Message_Protocol + */ +enum IcmpDestUnreachableCodes { + /** Network unreachable error */ + IcmpNetworkUnreachable = 0, + /** Host unreachable error */ + IcmpHostUnreachable = 1, + /** Protocol unreachable error (the designated transport protocol is not + supported) */ + IcmpProtocolUnreachable = 2, + /** Port unreachable error (the designated protocol is unable to inform the + host of the incoming message) */ + IcmpPortUnreachable = 3, + /** The datagram is too big. Packet fragmentation is required but the 'don't + fragment' (DF) flag is on */ + IcmpDatagramTooBig = 4, + /** Source route failed error */ + IcmpSourceRouteFailed = 5, + /** Destination network unknown error */ + IcmpDestinationNetworkUnknown = 6, + /** Destination host unknown error */ + IcmpDestinationHostUnknown = 7, + /** Source host isolated error */ + IcmpSourceHostIsolated = 8, + /** The destination network is administratively prohibited */ + IcmpDestinationNetworkProhibited = 9, + /** The destination host is administratively prohibited */ + IcmpDestinationHostProhibited = 10, + /** The network is unreachable for Type Of Service */ + IcmpNetworkUnreachableForTypeOfService = 11, + /** The host is unreachable for Type Of Service */ + IcmpHostUnreachableForTypeOfService = 12, + /** Communication administratively prohibited (administrative filtering + * prevents packet from being forwarded) + */ + IcmpCommunicationProhibited = 13, + /** Host precedence violation (indicates the requested precedence is not + * permitted for the combination of host or network and port) + */ + IcmpHostPrecedenceViolation = 14, + /** Precedence cutoff in effect (precedence of datagram is below the level set + * by the network administrators) + */ + IcmpPrecedenceCutoff = 15 +}; - /** - * @struct icmp_echo_request - * ICMP echo (ping) request/reply message structure - */ - typedef struct icmp_echo_request - { - /** a pointer to the header data */ - icmp_echo_hdr* header; - /** most echo requests/replies contain some payload data. This is the data length */ - size_t dataLength; - /** most echo requests/replies contain some payload data. This is a pointer to this data */ - uint8_t* data; - } icmp_echo_request; - - - /** - * @typedef icmp_echo_reply - * ICMP echo (ping) reply message structure, same as icmp_echo_request - */ - typedef icmp_echo_request icmp_echo_reply; - - - /** - * @struct icmp_timestamp_request - * ICMP timestamp request message structure - */ +/** + * @struct icmp_echo_hdr + * ICMP echo (ping) request/reply message structure + */ #pragma pack(push, 1) - typedef struct icmp_timestamp_request : icmphdr - { - /** the timestamp request identifier */ - uint16_t id; - /** the timestamp request sequence number */ - uint16_t sequence; - /** the time (in milliseconds since midnight) the sender last touched the packet */ - uint32_t originateTimestamp; - /** relevant for timestamp reply only - the time the echoer first touched it on receipt */ - uint32_t receiveTimestamp; - /** relevant for timestamp reply only - the time the echoer last touched the message on sending it */ - uint32_t transmitTimestamp; - } icmp_timestamp_request; +typedef struct icmp_echo_hdr : icmphdr { + /** the echo (ping) request identifier */ + uint16_t id; + /** the echo (ping) request sequence number */ + uint16_t sequence; + /** a timestamp of when the message was sent */ + uint64_t timestamp; +} icmp_echo_hdr; #pragma pack(pop) +/** + * @struct icmp_echo_request + * ICMP echo (ping) request/reply message structure + */ +typedef struct icmp_echo_request { + /** a pointer to the header data */ + icmp_echo_hdr* header; + /** most echo requests/replies contain some payload data. This is the data + * length */ + size_t dataLength; + /** most echo requests/replies contain some payload data. This is a pointer to + * this data */ + uint8_t* data; +} icmp_echo_request; - /** - * @typedef icmp_timestamp_reply - * ICMP timestamp reply message structure, same as icmp_timestamp_request - */ - typedef icmp_timestamp_request icmp_timestamp_reply; - +/** + * @typedef icmp_echo_reply + * ICMP echo (ping) reply message structure, same as icmp_echo_request + */ +typedef icmp_echo_request icmp_echo_reply; - /** - * @struct icmp_destination_unreachable - * ICMP destination unreachable message structure - */ +/** + * @struct icmp_timestamp_request + * ICMP timestamp request message structure + */ #pragma pack(push, 1) - typedef struct icmp_destination_unreachable : icmphdr - { - /** unused 2 bytes */ - uint16_t unused; - /** contains the MTU of the next-hop network if a code 4 error occurs */ - uint16_t nextHopMTU; - } icmp_destination_unreachable; +typedef struct icmp_timestamp_request : icmphdr { + /** the timestamp request identifier */ + uint16_t id; + /** the timestamp request sequence number */ + uint16_t sequence; + /** the time (in milliseconds since midnight) the sender last touched the + * packet */ + uint32_t originateTimestamp; + /** relevant for timestamp reply only - the time the echoer first touched it + * on receipt */ + uint32_t receiveTimestamp; + /** relevant for timestamp reply only - the time the echoer last touched the + * message on sending it */ + uint32_t transmitTimestamp; +} icmp_timestamp_request; #pragma pack(pop) +/** + * @typedef icmp_timestamp_reply + * ICMP timestamp reply message structure, same as icmp_timestamp_request + */ +typedef icmp_timestamp_request icmp_timestamp_reply; - /** - * @struct icmp_time_exceeded - * ICMP time-to-live exceeded message structure - */ +/** + * @struct icmp_destination_unreachable + * ICMP destination unreachable message structure + */ #pragma pack(push, 1) - typedef struct icmp_time_exceeded : icmphdr - { - /** unused 4 bytes */ - uint32_t unused; - } icmp_time_exceeded; +typedef struct icmp_destination_unreachable : icmphdr { + /** unused 2 bytes */ + uint16_t unused; + /** contains the MTU of the next-hop network if a code 4 error occurs */ + uint16_t nextHopMTU; +} icmp_destination_unreachable; #pragma pack(pop) - - /** - * @typedef icmp_source_quench - * ICMP source quence message structure, same as icmp_time_exceeded - */ - typedef icmp_time_exceeded icmp_source_quench; - - - /** - * @struct icmp_param_problem - * ICMP parameter problem message structure - */ +/** + * @struct icmp_time_exceeded + * ICMP time-to-live exceeded message structure + */ #pragma pack(push, 1) - typedef struct icmp_param_problem : icmphdr - { - /** in the case of an invalid IP header (Code 0), this field indicates the byte offset of the error in the header */ - uint8_t pointer; - /** unused 1 byte */ - uint8_t unused1; - /** unused 2 bytes */ - uint16_t unused2; - } icmp_param_problem; +typedef struct icmp_time_exceeded : icmphdr { + /** unused 4 bytes */ + uint32_t unused; +} icmp_time_exceeded; #pragma pack(pop) +/** + * @typedef icmp_source_quench + * ICMP source quence message structure, same as icmp_time_exceeded + */ +typedef icmp_time_exceeded icmp_source_quench; - /** - * @typedef icmp_router_solicitation - * ICMP router solicitation message structure, same as icmphdr - */ - typedef icmphdr icmp_router_solicitation; - - /** - * @struct icmp_redirect - * ICMP redirect message structure - */ +/** + * @struct icmp_param_problem + * ICMP parameter problem message structure + */ #pragma pack(push, 1) - typedef struct icmp_redirect : icmphdr - { - /** an IPv4 address of the gateway to which the redirection should be sent */ - uint32_t gatewayAddress; - } icmp_redirect; +typedef struct icmp_param_problem : icmphdr { + /** in the case of an invalid IP header (Code 0), this field indicates the + * byte offset of the error in the header */ + uint8_t pointer; + /** unused 1 byte */ + uint8_t unused1; + /** unused 2 bytes */ + uint16_t unused2; +} icmp_param_problem; #pragma pack(pop) +/** + * @typedef icmp_router_solicitation + * ICMP router solicitation message structure, same as icmphdr + */ +typedef icmphdr icmp_router_solicitation; - /** - * @struct icmp_router_address_structure - * Router address structure, relevant for ICMP router advertisement message type (icmp_router_advertisement) - */ +/** + * @struct icmp_redirect + * ICMP redirect message structure + */ #pragma pack(push, 1) - struct icmp_router_address_structure - { - /** the IPv4 address of the advertised router */ - uint32_t routerAddress; - /** The preferability of the router address as a default router address, relative to other router addresses - * on the same subnet. This is a twos-complement value where higher values indicate that the route is - * more preferable */ - uint32_t preferenceLevel; - - /** - * Set router address structure from a given IPv4 address and preference level - * @param[in] addr IPv4 address to set - * @param[in] preference Preference level to set - */ - void setRouterAddress(IPv4Address addr, uint32_t preference); - - /** - * @return The IPv4 address extracted from icmp_router_address_structure#routerAddress field - */ - IPv4Address getAddress() const { return routerAddress; } - }; +typedef struct icmp_redirect : icmphdr { + /** an IPv4 address of the gateway to which the redirection should be sent */ + uint32_t gatewayAddress; +} icmp_redirect; #pragma pack(pop) - - /** - * @struct icmp_router_advertisement_hdr - * ICMP router advertisement message structure - */ +/** + * @struct icmp_router_address_structure + * Router address structure, relevant for ICMP router advertisement message type + * (icmp_router_advertisement) + */ #pragma pack(push, 1) - typedef struct icmp_router_advertisement_hdr : icmphdr - { - /** the number of router advertisements in this message. Each advertisement contains one router address/preference level pair */ - uint8_t advertisementCount; - /** the number of 32-bit words of information for each router address entry in the list. The value is normally set to 2 - * (router address + preference level) */ - uint8_t addressEntrySize; - /** the maximum number of seconds that the router addresses in this list may be considered valid */ - uint16_t lifetime; - } icmp_router_advertisement_hdr; +struct icmp_router_address_structure { + /** the IPv4 address of the advertised router */ + uint32_t routerAddress; + /** The preferability of the router address as a default router address, + * relative to other router addresses on the same subnet. This is a + * twos-complement value where higher values indicate that the route is more + * preferable */ + uint32_t preferenceLevel; + + /** + * Set router address structure from a given IPv4 address and preference level + * @param[in] addr IPv4 address to set + * @param[in] preference Preference level to set + */ + void setRouterAddress(IPv4Address addr, uint32_t preference); + + /** + * @return The IPv4 address extracted from + * icmp_router_address_structure#routerAddress field + */ + IPv4Address getAddress() const { return routerAddress; } +}; #pragma pack(pop) - - /** - * @struct icmp_router_advertisement - * ICMP router advertisement message structure - */ - struct icmp_router_advertisement - { - /** a pointer to the header data on the packet */ - icmp_router_advertisement_hdr* header; - - /** - * Extract router advertisement at a given index - * @param[in] index The index of the router advertisement - * @return A pointer to the router advertisement on the packet or null if index is out of range (less than zero or - * greater than the number of router advertisement records on this message, determined by advertisementCount field) - */ - icmp_router_address_structure* getRouterAddress(int index) const; - }; - - - /** - * @struct icmp_address_mask_request - * ICMP address mask request message structure - */ +/** + * @struct icmp_router_advertisement_hdr + * ICMP router advertisement message structure + */ #pragma pack(push, 1) - typedef struct icmp_address_mask_request : icmphdr - { - /** the address mask request identifier */ - uint16_t id; - /** the address mask request sequence */ - uint16_t sequence; - /** the subnet mask of the requesting host */ - uint32_t addressMask; - } icmp_address_mask_request; +typedef struct icmp_router_advertisement_hdr : icmphdr { + /** the number of router advertisements in this message. Each advertisement + * contains one router address/preference level pair */ + uint8_t advertisementCount; + /** the number of 32-bit words of information for each router address entry in + * the list. The value is normally set to 2 (router address + preference + * level) */ + uint8_t addressEntrySize; + /** the maximum number of seconds that the router addresses in this list may + * be considered valid */ + uint16_t lifetime; +} icmp_router_advertisement_hdr; #pragma pack(pop) +/** + * @struct icmp_router_advertisement + * ICMP router advertisement message structure + */ +struct icmp_router_advertisement { + /** a pointer to the header data on the packet */ + icmp_router_advertisement_hdr* header; + + /** + * Extract router advertisement at a given index + * @param[in] index The index of the router advertisement + * @return A pointer to the router advertisement on the packet or null if + * index is out of range (less than zero or greater than the number of router + * advertisement records on this message, determined by advertisementCount + * field) + */ + icmp_router_address_structure* getRouterAddress(int index) const; +}; - /** - * @typedef icmp_address_mask_reply - * ICMP address mask reply message structure, same as icmp_address_mask_request - */ - typedef icmp_address_mask_request icmp_address_mask_reply; +/** + * @struct icmp_address_mask_request + * ICMP address mask request message structure + */ +#pragma pack(push, 1) +typedef struct icmp_address_mask_request : icmphdr { + /** the address mask request identifier */ + uint16_t id; + /** the address mask request sequence */ + uint16_t sequence; + /** the subnet mask of the requesting host */ + uint32_t addressMask; +} icmp_address_mask_request; +#pragma pack(pop) +/** + * @typedef icmp_address_mask_reply + * ICMP address mask reply message structure, same as icmp_address_mask_request + */ +typedef icmp_address_mask_request icmp_address_mask_reply; - /** - * @struct icmp_info_request - * ICMP information request message structure - */ +/** + * @struct icmp_info_request + * ICMP information request message structure + */ #pragma pack(push, 1) - typedef struct icmp_info_request : icmphdr - { - /** the information request identifier */ - uint16_t id; - /** the information request sequence */ - uint16_t sequence; - } icmp_info_request; +typedef struct icmp_info_request : icmphdr { + /** the information request identifier */ + uint16_t id; + /** the information request sequence */ + uint16_t sequence; +} icmp_info_request; #pragma pack(pop) +/** + * @typedef icmp_info_reply + * ICMP information reply message structure, same as icmp_info_request + */ +typedef icmp_info_request icmp_info_reply; - /** - * @typedef icmp_info_reply - * ICMP information reply message structure, same as icmp_info_request - */ - typedef icmp_info_request icmp_info_reply; - - - /** - * @class IcmpLayer - * Represents an ICMP protocol layer (for IPv4 only) - */ - class IcmpLayer : public Layer - { - private: - icmp_echo_request m_EchoData; - mutable icmp_router_advertisement m_RouterAdvData; - - bool cleanIcmpLayer(); - - bool setEchoData(IcmpMessageType echoType, uint16_t id, uint16_t sequence, uint64_t timestamp, const uint8_t* data, size_t dataLen); - - bool setIpAndL4Layers(IPv4Layer* ipLayer, Layer* l4Layer); - - public: - /** - * A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data (will be casted to @ref arphdr) - * @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 - */ - // cppcheck-suppress uninitMemberVar - IcmpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) { m_Protocol = ICMP; } - - /** - * An empty constructor that creates a new layer with an empty ICMP header without setting the ICMP type or ICMP data. - * Call the set*Data() methods to set ICMP type and data - */ - IcmpLayer(); - - virtual ~IcmpLayer() {} - - /** - * Get a pointer to the basic ICMP header. Notice this points directly to the data, so every change will change the actual packet data - * @return A pointer to the @ref icmphdr - */ - icmphdr* getIcmpHeader() const { return (icmphdr*)m_Data; } - - /** - * @return The ICMP message type - */ - IcmpMessageType getMessageType() const; - - /** - * @param[in] type Type to check - * @return True if the layer if of the given type, false otherwise - */ - bool isMessageOfType(IcmpMessageType type) const { return getMessageType() == type; } - - /** - * @return ICMP echo (ping) request data. If the layer isn't of type ICMP echo request NULL is returned - */ - icmp_echo_request* getEchoRequestData(); - - /** - * Set echo (ping) request message data - * @param[in] id Echo (ping) request identifier - * @param[in] sequence Echo (ping) request sequence - * @param[in] timestamp Echo (ping) request timestamp - * @param[in] data A pointer to echo (ping) request payload to set - * @param[in] dataLen The length of the echo (ping) request payload - * @return A pointer to the echo (ping) request data that have been set or NULL if something went wrong - * (an appropriate error log is printed in such cases) - */ - icmp_echo_request* setEchoRequestData(uint16_t id, uint16_t sequence, uint64_t timestamp, const uint8_t* data, size_t dataLen); - - /** - * @return ICMP echo reply data. If the layer isn't of type ICMP echo reply NULL is returned - */ - icmp_echo_reply* getEchoReplyData(); - - /** - * Set echo (ping) reply message data - * @param[in] id Echo (ping) reply identifier - * @param[in] sequence Echo (ping) reply sequence - * @param[in] timestamp Echo (ping) reply timestamp - * @param[in] data A pointer to echo (ping) reply payload to set - * @param[in] dataLen The length of the echo (ping) reply payload - * @return A pointer to the echo (ping) reply data that have been set or NULL if something went wrong - * (an appropriate error log is printed in such cases) - */ - icmp_echo_reply* setEchoReplyData(uint16_t id, uint16_t sequence, uint64_t timestamp, const uint8_t* data, size_t dataLen); - - /** - * @return ICMP timestamp request data. If the layer isn't of type ICMP timestamp request NULL is returned - */ - icmp_timestamp_request* getTimestampRequestData(); - - /** - * Set timestamp request message data - * @param[in] id Timestamp request identifier - * @param[in] sequence Timestamp request sequence - * @param[in] originateTimestamp Time (in milliseconds since midnight) the sender last touched the packet - * @return A pointer to the timestamp request data that have been set or NULL if something went wrong - * (an appropriate error log is printed in such cases) - */ - icmp_timestamp_request* setTimestampRequestData(uint16_t id, uint16_t sequence, timeval originateTimestamp); - - /** - * @return ICMP timestamp reply data. If the layer isn't of type ICMP timestamp reply NULL is returned - */ - icmp_timestamp_reply* getTimestampReplyData(); - - /** - * Set timestamp reply message data - * @param[in] id Timestamp reply identifier - * @param[in] sequence Timestamp reply sequence - * @param[in] originateTimestamp Time (in milliseconds since midnight) the sender last touched the packet - * @param[in] receiveTimestamp The time the echoer first touched it on receipt - * @param[in] transmitTimestamp The time the echoer last touched the message on sending it - * @return A pointer to the timestamp reply data that have been set or NULL if something went wrong - * (an appropriate error log is printed in such cases) - */ - icmp_timestamp_reply* setTimestampReplyData(uint16_t id, uint16_t sequence, - timeval originateTimestamp, timeval receiveTimestamp, timeval transmitTimestamp); - - /** - * @return ICMP destination unreachable data. If the layer isn't of type ICMP destination unreachable NULL is returned. - * The IP and L4 (ICMP/TCP/UDP) headers of the destination unreachable data are parsed as separate layers and can be - * retrieved via this->getNextLayer() - */ - icmp_destination_unreachable* getDestUnreachableData(); - - /** - * Set destination unreachable message data. This method only works if IcmpLayer is already part of a packet (not - * a standalone layer). The reason is the Internet and L4 headers given as parameters are added as separate layers - * and need a packet to be added to - * @param[in] code Destination unreachable code - * @param[in] nextHopMTU The MTU of the next-hop network if a code 4 error occurs - * @param[in] ipHeader The Internet header of the original data. This layer is added as a separate layer on the packet - * @param[in] l4Header The L4 header of the original data. This layer is added as a separate layer on the packet - * @return A pointer to the destination unreachable data that have been set or NULL if something went wrong - * (an appropriate error log is printed in such cases) - */ - icmp_destination_unreachable* setDestUnreachableData(IcmpDestUnreachableCodes code, uint16_t nextHopMTU, IPv4Layer* ipHeader, Layer* l4Header); - - /** - * @return ICMP source quench data. If the layer isn't of type ICMP source quench NULL is returned. - * The IP and L4 (ICMP/TCP/UDP) headers of the source quench data are parsed as separate layers and can be - * retrieved via this->getNextLayer() - */ - icmp_source_quench* getSourceQuenchdata(); - - /** - * Set source quench message data. This method only works if IcmpLayer is already part of a packet (not - * a standalone layer). The reason is the Internet and L4 headers given as parameters are added as separate layers - * and need a packet to be added to - * @param[in] ipHeader The Internet header of the original data. This layer is added as a separate layer on the packet - * @param[in] l4Header The L4 header of the original data. This layer is added as a separate layer on the packet - * @return A pointer to the source quench data that have been set or NULL if something went wrong - * (an appropriate error log is printed in such cases) - */ - icmp_source_quench* setSourceQuenchdata(IPv4Layer* ipHeader, Layer* l4Header); - - /** - * @return ICMP redirect data. If the layer isn't of type ICMP redirect NULL is returned. - * The IP and L4 (ICMP/TCP/UDP) headers of the redirect data are parsed as separate layers and can be - * retrieved via this->getNextLayer() - */ - icmp_redirect* getRedirectData(); - - /** - * Set redirect message data. This method only works if IcmpLayer is already part of a packet (not - * a standalone layer). The reason is the Internet and L4 headers given as parameters are added as separate layers - * and need a packet to be added to - * @param[in] code The redirect message code. Only values between 0 and 3 are legal, the rest will cause the method to fail - * @param[in] gatewayAddress An IPv4 address of the gateway to which the redirection should be sent - * @param[in] ipHeader The Internet header of the original data. This layer is added as a separate layer on the packet - * @param[in] l4Header The L4 header of the original data. This layer is added as a separate layer on the packet - * @return A pointer to the redirect data that have been set or NULL if something went wrong - * (an appropriate error log is printed in such cases) - */ - icmp_redirect* setRedirectData(uint8_t code, IPv4Address gatewayAddress, IPv4Layer* ipHeader, Layer* l4Header); - - /** - * @return ICMP router advertisement data. If the layer isn't of type ICMP router advertisement NULL is returned - */ - icmp_router_advertisement* getRouterAdvertisementData() const; - - /** - * Set router advertisement message data - * @param[in] code The router advertisement message code. Only codes 0 or 16 are legal, the rest will fail the method - * @param[in] lifetimeInSeconds The maximum number of seconds that the router addresses in this list may be considered valid - * @param[in] routerAddresses A vector of router advertisements to set - * @return A pointer to the router advertisement data that have been set or NULL if something went wrong - * (an appropriate error log is printed in such cases) - */ - icmp_router_advertisement* setRouterAdvertisementData(uint8_t code, uint16_t lifetimeInSeconds, const std::vector& routerAddresses); - - /** - * @return ICMP router solicitation data. If the layer isn't of type ICMP router solicitation NULL is returned - */ - icmp_router_solicitation* getRouterSolicitationData(); - - /** - * Set router solicitation message data. This message accepts no parameters as there are no parameters to this - * type of message (code is always zero) - * @return A pointer to the router solicitation data that have been set or NULL if something went wrong - * (an appropriate error log is printed in such cases) - */ - icmp_router_solicitation* setRouterSolicitationData(); - - /** - * @return ICMP time-to-live exceeded data. If the layer isn't of type ICMP time-to-live exceeded NULL is returned. - * The IP and L4 (ICMP/TCP/UDP) headers of the time exceeded data are parsed as separate layers and can be - * retrieved via this->getNextLayer() - */ - icmp_time_exceeded* getTimeExceededData(); - - /** - * Set time-to-live exceeded message data. This method only works if IcmpLayer is already part of a packet (not - * a standalone layer). The reason is the Internet and L4 headers given as parameters are added as separate layers - * and need a packet to be added to - * @param[in] code Time-to-live exceeded message code. Only codes 0 or 1 are legal, the rest will fail the method - * @param[in] ipHeader The Internet header of the original data. This layer is added as a separate layer on the packet - * @param[in] l4Header The L4 header of the original data. This layer is added as a separate layer on the packet - * @return A pointer to the time-to-live exceeded data that have been set or NULL if something went wrong - * (an appropriate error log is printed in such cases) - */ - icmp_time_exceeded* setTimeExceededData(uint8_t code, IPv4Layer* ipHeader, Layer* l4Header); - - /** - * @return ICMP parameter problem data. If the layer isn't of type ICMP parameter problem NULL is returned - */ - icmp_param_problem* getParamProblemData(); - - /** - * Set parameter problem message data. This method only works if IcmpLayer is already part of a packet (not - * a standalone layer). The reason is the Internet and L4 headers given as parameters are added as separate layers - * and need a packet to be added to - * @param[in] code Parameter problem message code. Only code between 0 and 2 are legal, the rest will fail the method - * @param[in] errorOctetPointer In the case of an invalid IP header (Code 0), indicate the byte offset of the error in the header - * @param[in] ipHeader The Internet header of the original data. This layer is added as a separate layer on the packet - * @param[in] l4Header The L4 header of the original data. This layer is added as a separate layer on the packet - * @return A pointer to the parameter problem data that have been set or NULL if something went wrong - * (an appropriate error log is printed in such cases) - */ - icmp_param_problem* setParamProblemData(uint8_t code, uint8_t errorOctetPointer, IPv4Layer* ipHeader, Layer* l4Header); - - /** - * @return ICMP address mask request data. If the layer isn't of type ICMP address mask request NULL is returned - */ - icmp_address_mask_request* getAddressMaskRequestData(); - - /** - * Set address mask request message data - * @param[in] id Address mask request identifier - * @param[in] sequence Address mask request sequence - * @param[in] mask The subnet mask of the requesting host - * @return A pointer to the address mask request data that have been set or NULL if something went wrong - * (an appropriate error log is printed in such cases) - */ - icmp_address_mask_request* setAddressMaskRequestData(uint16_t id, uint16_t sequence, IPv4Address mask); - - /** - * @return ICMP address mask reply data. If the layer isn't of type ICMP address mask reply NULL is returned - */ - icmp_address_mask_reply* getAddressMaskReplyData(); - - /** - * Set address mask reply message data - * @param[in] id Address mask reply identifier - * @param[in] sequence Address mask reply sequence - * @param[in] mask The subnet mask of the requesting host - * @return A pointer to the address mask reply data that have been set or NULL if something went wrong - * (an appropriate error log is printed in such cases) - */ - icmp_address_mask_reply* setAddressMaskReplyData(uint16_t id, uint16_t sequence, IPv4Address mask); - - /** - * @return ICMP address information request data. If the layer isn't of type ICMP information request NULL is returned - */ - icmp_info_request* getInfoRequestData(); - - /** - * Set information request message data - * @param[in] id Information request identifier - * @param[in] sequence Information request sequence - * @return A pointer to the information request data that have been set or NULL if something went wrong - * (an appropriate error log is printed in such cases) - */ - icmp_info_request* setInfoRequestData(uint16_t id, uint16_t sequence); - - /** - * @return ICMP address information reply data. If the layer isn't of type ICMP information reply NULL is returned - */ - icmp_info_reply* getInfoReplyData(); - - /** - * Set information reply message data - * @param[in] id Information reply identifier - * @param[in] sequence Information reply sequence - * @return A pointer to the information reply data that have been set or NULL if something went wrong - * (an appropriate error log is printed in such cases) - */ - icmp_info_reply* setInfoReplyData(uint16_t id, uint16_t sequence); - - /** - * The static method makes validation of input data - * @param[in] data The pointer to the beginning of byte stream of ICMP packet - * @param[in] dataLen The length of byte stream - * @return True if the data is valid and can represent an ICMP packet - */ - static inline bool isDataValid(const uint8_t* data, size_t dataLen); - - // implement abstract methods - - /** - * ICMP messages of types: ICMP_DEST_UNREACHABLE, ICMP_SOURCE_QUENCH, ICMP_TIME_EXCEEDED, ICMP_REDIRECT, ICMP_PARAM_PROBLEM - * have data that contains IPv4 header and some L4 header (TCP/UDP/ICMP). This method parses these headers as separate - * layers on top of the ICMP layer - */ - void parseNextLayer(); - - /** - * @return The ICMP header length. This length varies according to the ICMP message type. This length doesn't include - * IPv4 and L4 headers in case ICMP message type are: ICMP_DEST_UNREACHABLE, ICMP_SOURCE_QUENCH, ICMP_TIME_EXCEEDED, - * ICMP_REDIRECT, ICMP_PARAM_PROBLEM - */ - size_t getHeaderLen() const; - - /** - * Calculate ICMP checksum field - */ - void computeCalculateFields(); - - std::string toString() const; - - OsiModelLayer getOsiModelLayer() const { return OsiModelNetworkLayer; } - }; - - // implementation of inline methods - - bool IcmpLayer::isDataValid(const uint8_t* data, size_t dataLen) - { - if (dataLen < sizeof(icmphdr)) - return false; - - uint8_t type = data[0]; - - // ICMP_ECHO_REQUEST, ICMP_ECHO_REPLY, ICMP_ROUTER_SOL, ICMP_INFO_REQUEST, ICMP_INFO_REPLY - if (type == 8 || type == 0 || type == 10 || type == 15 || type == 16) - return true; - - // ICMP_TIMESTAMP_REQUEST, ICMP_TIMESTAMP_REPLY - if (type == 13 || type == 14) - return dataLen >= sizeof(icmp_timestamp_request); - - // ICMP_ADDRESS_MASK_REPLY, ICMP_ADDRESS_MASK_REQUEST - if (type == 17 || type == 18) - return dataLen >= sizeof(icmp_address_mask_request); - - // ICMP_DEST_UNREACHABLE - if (type == 3) - return dataLen >= sizeof(icmp_destination_unreachable); - - // ICMP_REDIRECT - if (type == 5) - return dataLen >= sizeof(icmp_redirect); - - // ICMP_TIME_EXCEEDED, ICMP_SOURCE_QUENCH - if (type == 4 || type == 11) - return dataLen >= sizeof(icmp_time_exceeded); - - // ICMP_PARAM_PROBLEM - if (type == 12) - return dataLen >= sizeof(icmp_param_problem); - - // ICMP_ROUTER_ADV - if (type == 9) - return dataLen >= sizeof(icmp_router_advertisement_hdr); - - return false; - } +/** + * @class IcmpLayer + * Represents an ICMP protocol layer (for IPv4 only) + */ +class IcmpLayer : public Layer { + private: + icmp_echo_request m_EchoData; + mutable icmp_router_advertisement m_RouterAdvData; + + bool cleanIcmpLayer(); + + bool setEchoData(IcmpMessageType echoType, uint16_t id, uint16_t sequence, + uint64_t timestamp, const uint8_t* data, size_t dataLen); + + bool setIpAndL4Layers(IPv4Layer* ipLayer, Layer* l4Layer); + + public: + /** + * A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data (will be casted to @ref arphdr) + * @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 + */ + // cppcheck-suppress uninitMemberVar + IcmpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : Layer(data, dataLen, prevLayer, packet) { + m_Protocol = ICMP; + } + + /** + * An empty constructor that creates a new layer with an empty ICMP header + * without setting the ICMP type or ICMP data. Call the set*Data() methods to + * set ICMP type and data + */ + IcmpLayer(); + + virtual ~IcmpLayer() {} + + /** + * Get a pointer to the basic ICMP header. Notice this points directly to the + * data, so every change will change the actual packet data + * @return A pointer to the @ref icmphdr + */ + icmphdr* getIcmpHeader() const { return (icmphdr*)m_Data; } + + /** + * @return The ICMP message type + */ + IcmpMessageType getMessageType() const; + + /** + * @param[in] type Type to check + * @return True if the layer if of the given type, false otherwise + */ + bool isMessageOfType(IcmpMessageType type) const { + return getMessageType() == type; + } + + /** + * @return ICMP echo (ping) request data. If the layer isn't of type ICMP echo + * request NULL is returned + */ + icmp_echo_request* getEchoRequestData(); + + /** + * Set echo (ping) request message data + * @param[in] id Echo (ping) request identifier + * @param[in] sequence Echo (ping) request sequence + * @param[in] timestamp Echo (ping) request timestamp + * @param[in] data A pointer to echo (ping) request payload to set + * @param[in] dataLen The length of the echo (ping) request payload + * @return A pointer to the echo (ping) request data that have been set or + * NULL if something went wrong (an appropriate error log is printed in such + * cases) + */ + icmp_echo_request* setEchoRequestData(uint16_t id, uint16_t sequence, + uint64_t timestamp, const uint8_t* data, + size_t dataLen); + + /** + * @return ICMP echo reply data. If the layer isn't of type ICMP echo reply + * NULL is returned + */ + icmp_echo_reply* getEchoReplyData(); + + /** + * Set echo (ping) reply message data + * @param[in] id Echo (ping) reply identifier + * @param[in] sequence Echo (ping) reply sequence + * @param[in] timestamp Echo (ping) reply timestamp + * @param[in] data A pointer to echo (ping) reply payload to set + * @param[in] dataLen The length of the echo (ping) reply payload + * @return A pointer to the echo (ping) reply data that have been set or NULL + * if something went wrong (an appropriate error log is printed in such cases) + */ + icmp_echo_reply* setEchoReplyData(uint16_t id, uint16_t sequence, + uint64_t timestamp, const uint8_t* data, + size_t dataLen); + + /** + * @return ICMP timestamp request data. If the layer isn't of type ICMP + * timestamp request NULL is returned + */ + icmp_timestamp_request* getTimestampRequestData(); + + /** + * Set timestamp request message data + * @param[in] id Timestamp request identifier + * @param[in] sequence Timestamp request sequence + * @param[in] originateTimestamp Time (in milliseconds since midnight) the + * sender last touched the packet + * @return A pointer to the timestamp request data that have been set or NULL + * if something went wrong (an appropriate error log is printed in such cases) + */ + icmp_timestamp_request* setTimestampRequestData(uint16_t id, + uint16_t sequence, + timeval originateTimestamp); + + /** + * @return ICMP timestamp reply data. If the layer isn't of type ICMP + * timestamp reply NULL is returned + */ + icmp_timestamp_reply* getTimestampReplyData(); + + /** + * Set timestamp reply message data + * @param[in] id Timestamp reply identifier + * @param[in] sequence Timestamp reply sequence + * @param[in] originateTimestamp Time (in milliseconds since midnight) the + * sender last touched the packet + * @param[in] receiveTimestamp The time the echoer first touched it on receipt + * @param[in] transmitTimestamp The time the echoer last touched the message + * on sending it + * @return A pointer to the timestamp reply data that have been set or NULL if + * something went wrong (an appropriate error log is printed in such cases) + */ + icmp_timestamp_reply* setTimestampReplyData(uint16_t id, uint16_t sequence, + timeval originateTimestamp, + timeval receiveTimestamp, + timeval transmitTimestamp); + + /** + * @return ICMP destination unreachable data. If the layer isn't of type ICMP + * destination unreachable NULL is returned. The IP and L4 (ICMP/TCP/UDP) + * headers of the destination unreachable data are parsed as separate layers + * and can be retrieved via this->getNextLayer() + */ + icmp_destination_unreachable* getDestUnreachableData(); + + /** + * Set destination unreachable message data. This method only works if + * IcmpLayer is already part of a packet (not a standalone layer). The reason + * is the Internet and L4 headers given as parameters are added as separate + * layers and need a packet to be added to + * @param[in] code Destination unreachable code + * @param[in] nextHopMTU The MTU of the next-hop network if a code 4 error + * occurs + * @param[in] ipHeader The Internet header of the original data. This layer is + * added as a separate layer on the packet + * @param[in] l4Header The L4 header of the original data. This layer is added + * as a separate layer on the packet + * @return A pointer to the destination unreachable data that have been set or + * NULL if something went wrong (an appropriate error log is printed in such + * cases) + */ + icmp_destination_unreachable* + setDestUnreachableData(IcmpDestUnreachableCodes code, uint16_t nextHopMTU, + IPv4Layer* ipHeader, Layer* l4Header); + + /** + * @return ICMP source quench data. If the layer isn't of type ICMP source + * quench NULL is returned. The IP and L4 (ICMP/TCP/UDP) headers of the source + * quench data are parsed as separate layers and can be retrieved via + * this->getNextLayer() + */ + icmp_source_quench* getSourceQuenchdata(); + + /** + * Set source quench message data. This method only works if IcmpLayer is + * already part of a packet (not a standalone layer). The reason is the + * Internet and L4 headers given as parameters are added as separate layers + * and need a packet to be added to + * @param[in] ipHeader The Internet header of the original data. This layer is + * added as a separate layer on the packet + * @param[in] l4Header The L4 header of the original data. This layer is added + * as a separate layer on the packet + * @return A pointer to the source quench data that have been set or NULL if + * something went wrong (an appropriate error log is printed in such cases) + */ + icmp_source_quench* setSourceQuenchdata(IPv4Layer* ipHeader, Layer* l4Header); + + /** + * @return ICMP redirect data. If the layer isn't of type ICMP redirect NULL + * is returned. The IP and L4 (ICMP/TCP/UDP) headers of the redirect data are + * parsed as separate layers and can be retrieved via this->getNextLayer() + */ + icmp_redirect* getRedirectData(); + + /** + * Set redirect message data. This method only works if IcmpLayer is already + * part of a packet (not a standalone layer). The reason is the Internet and + * L4 headers given as parameters are added as separate layers and need a + * packet to be added to + * @param[in] code The redirect message code. Only values between 0 and 3 are + * legal, the rest will cause the method to fail + * @param[in] gatewayAddress An IPv4 address of the gateway to which the + * redirection should be sent + * @param[in] ipHeader The Internet header of the original data. This layer is + * added as a separate layer on the packet + * @param[in] l4Header The L4 header of the original data. This layer is added + * as a separate layer on the packet + * @return A pointer to the redirect data that have been set or NULL if + * something went wrong (an appropriate error log is printed in such cases) + */ + icmp_redirect* setRedirectData(uint8_t code, IPv4Address gatewayAddress, + IPv4Layer* ipHeader, Layer* l4Header); + + /** + * @return ICMP router advertisement data. If the layer isn't of type ICMP + * router advertisement NULL is returned + */ + icmp_router_advertisement* getRouterAdvertisementData() const; + + /** + * Set router advertisement message data + * @param[in] code The router advertisement message code. Only codes 0 or 16 + * are legal, the rest will fail the method + * @param[in] lifetimeInSeconds The maximum number of seconds that the router + * addresses in this list may be considered valid + * @param[in] routerAddresses A vector of router advertisements to set + * @return A pointer to the router advertisement data that have been set or + * NULL if something went wrong (an appropriate error log is printed in such + * cases) + */ + icmp_router_advertisement* setRouterAdvertisementData( + uint8_t code, uint16_t lifetimeInSeconds, + const std::vector& routerAddresses); + + /** + * @return ICMP router solicitation data. If the layer isn't of type ICMP + * router solicitation NULL is returned + */ + icmp_router_solicitation* getRouterSolicitationData(); + + /** + * Set router solicitation message data. This message accepts no parameters as + * there are no parameters to this type of message (code is always zero) + * @return A pointer to the router solicitation data that have been set or + * NULL if something went wrong (an appropriate error log is printed in such + * cases) + */ + icmp_router_solicitation* setRouterSolicitationData(); + + /** + * @return ICMP time-to-live exceeded data. If the layer isn't of type ICMP + * time-to-live exceeded NULL is returned. The IP and L4 (ICMP/TCP/UDP) + * headers of the time exceeded data are parsed as separate layers and can be + * retrieved via this->getNextLayer() + */ + icmp_time_exceeded* getTimeExceededData(); + + /** + * Set time-to-live exceeded message data. This method only works if IcmpLayer + * is already part of a packet (not a standalone layer). The reason is the + * Internet and L4 headers given as parameters are added as separate layers + * and need a packet to be added to + * @param[in] code Time-to-live exceeded message code. Only codes 0 or 1 are + * legal, the rest will fail the method + * @param[in] ipHeader The Internet header of the original data. This layer is + * added as a separate layer on the packet + * @param[in] l4Header The L4 header of the original data. This layer is added + * as a separate layer on the packet + * @return A pointer to the time-to-live exceeded data that have been set or + * NULL if something went wrong (an appropriate error log is printed in such + * cases) + */ + icmp_time_exceeded* setTimeExceededData(uint8_t code, IPv4Layer* ipHeader, + Layer* l4Header); + + /** + * @return ICMP parameter problem data. If the layer isn't of type ICMP + * parameter problem NULL is returned + */ + icmp_param_problem* getParamProblemData(); + + /** + * Set parameter problem message data. This method only works if IcmpLayer is + * already part of a packet (not a standalone layer). The reason is the + * Internet and L4 headers given as parameters are added as separate layers + * and need a packet to be added to + * @param[in] code Parameter problem message code. Only code between 0 and 2 + * are legal, the rest will fail the method + * @param[in] errorOctetPointer In the case of an invalid IP header (Code 0), + * indicate the byte offset of the error in the header + * @param[in] ipHeader The Internet header of the original data. This layer is + * added as a separate layer on the packet + * @param[in] l4Header The L4 header of the original data. This layer is added + * as a separate layer on the packet + * @return A pointer to the parameter problem data that have been set or NULL + * if something went wrong (an appropriate error log is printed in such cases) + */ + icmp_param_problem* setParamProblemData(uint8_t code, + uint8_t errorOctetPointer, + IPv4Layer* ipHeader, Layer* l4Header); + + /** + * @return ICMP address mask request data. If the layer isn't of type ICMP + * address mask request NULL is returned + */ + icmp_address_mask_request* getAddressMaskRequestData(); + + /** + * Set address mask request message data + * @param[in] id Address mask request identifier + * @param[in] sequence Address mask request sequence + * @param[in] mask The subnet mask of the requesting host + * @return A pointer to the address mask request data that have been set or + * NULL if something went wrong (an appropriate error log is printed in such + * cases) + */ + icmp_address_mask_request* + setAddressMaskRequestData(uint16_t id, uint16_t sequence, IPv4Address mask); + + /** + * @return ICMP address mask reply data. If the layer isn't of type ICMP + * address mask reply NULL is returned + */ + icmp_address_mask_reply* getAddressMaskReplyData(); + + /** + * Set address mask reply message data + * @param[in] id Address mask reply identifier + * @param[in] sequence Address mask reply sequence + * @param[in] mask The subnet mask of the requesting host + * @return A pointer to the address mask reply data that have been set or NULL + * if something went wrong (an appropriate error log is printed in such cases) + */ + icmp_address_mask_reply* + setAddressMaskReplyData(uint16_t id, uint16_t sequence, IPv4Address mask); + + /** + * @return ICMP address information request data. If the layer isn't of type + * ICMP information request NULL is returned + */ + icmp_info_request* getInfoRequestData(); + + /** + * Set information request message data + * @param[in] id Information request identifier + * @param[in] sequence Information request sequence + * @return A pointer to the information request data that have been set or + * NULL if something went wrong (an appropriate error log is printed in such + * cases) + */ + icmp_info_request* setInfoRequestData(uint16_t id, uint16_t sequence); + + /** + * @return ICMP address information reply data. If the layer isn't of type + * ICMP information reply NULL is returned + */ + icmp_info_reply* getInfoReplyData(); + + /** + * Set information reply message data + * @param[in] id Information reply identifier + * @param[in] sequence Information reply sequence + * @return A pointer to the information reply data that have been set or NULL + * if something went wrong (an appropriate error log is printed in such cases) + */ + icmp_info_reply* setInfoReplyData(uint16_t id, uint16_t sequence); + + /** + * The static method makes validation of input data + * @param[in] data The pointer to the beginning of byte stream of ICMP packet + * @param[in] dataLen The length of byte stream + * @return True if the data is valid and can represent an ICMP packet + */ + static inline bool isDataValid(const uint8_t* data, size_t dataLen); + + // implement abstract methods + + /** + * ICMP messages of types: ICMP_DEST_UNREACHABLE, ICMP_SOURCE_QUENCH, + * ICMP_TIME_EXCEEDED, ICMP_REDIRECT, ICMP_PARAM_PROBLEM have data that + * contains IPv4 header and some L4 header (TCP/UDP/ICMP). This method parses + * these headers as separate layers on top of the ICMP layer + */ + void parseNextLayer(); + + /** + * @return The ICMP header length. This length varies according to the ICMP + * message type. This length doesn't include IPv4 and L4 headers in case ICMP + * message type are: ICMP_DEST_UNREACHABLE, ICMP_SOURCE_QUENCH, + * ICMP_TIME_EXCEEDED, ICMP_REDIRECT, ICMP_PARAM_PROBLEM + */ + size_t getHeaderLen() const; + + /** + * Calculate ICMP checksum field + */ + void computeCalculateFields(); + + std::string toString() const; + + OsiModelLayer getOsiModelLayer() const { return OsiModelNetworkLayer; } +}; + +// implementation of inline methods + +bool IcmpLayer::isDataValid(const uint8_t* data, size_t dataLen) { + if (dataLen < sizeof(icmphdr)) + return false; + + uint8_t type = data[0]; + + // ICMP_ECHO_REQUEST, ICMP_ECHO_REPLY, ICMP_ROUTER_SOL, ICMP_INFO_REQUEST, + // ICMP_INFO_REPLY + if (type == 8 || type == 0 || type == 10 || type == 15 || type == 16) + return true; + + // ICMP_TIMESTAMP_REQUEST, ICMP_TIMESTAMP_REPLY + if (type == 13 || type == 14) + return dataLen >= sizeof(icmp_timestamp_request); + + // ICMP_ADDRESS_MASK_REPLY, ICMP_ADDRESS_MASK_REQUEST + if (type == 17 || type == 18) + return dataLen >= sizeof(icmp_address_mask_request); + + // ICMP_DEST_UNREACHABLE + if (type == 3) + return dataLen >= sizeof(icmp_destination_unreachable); + + // ICMP_REDIRECT + if (type == 5) + return dataLen >= sizeof(icmp_redirect); + + // ICMP_TIME_EXCEEDED, ICMP_SOURCE_QUENCH + if (type == 4 || type == 11) + return dataLen >= sizeof(icmp_time_exceeded); + + // ICMP_PARAM_PROBLEM + if (type == 12) + return dataLen >= sizeof(icmp_param_problem); + + // ICMP_ROUTER_ADV + if (type == 9) + return dataLen >= sizeof(icmp_router_advertisement_hdr); + + return false; +} } // namespace pcpp diff --git a/Packet++/header/IcmpV6Layer.h b/Packet++/header/IcmpV6Layer.h index f686fdc01c..91d65dd68d 100644 --- a/Packet++/header/IcmpV6Layer.h +++ b/Packet++/header/IcmpV6Layer.h @@ -9,90 +9,88 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ +namespace pcpp { /** * An enum representing the available ICMPv6 message types */ -enum class ICMPv6MessageType : int -{ - /** Unknown ICMPv6 message */ - ICMPv6_UNKNOWN_MESSAGE = 0, - /** Destination Unreachable Message */ - ICMPv6_DESTINATION_UNREACHABLE = 1, - /** Packet Too Big Message */ - ICMPv6_PACKET_TOO_BIG = 2, - /** Time Exceeded Message */ - ICMPv6_TIME_EXCEEDED = 3, - /** Parameter Problem Message */ - ICMPv6_PARAMETER_PROBLEM = 4, - /** Private Experimentation Message */ - ICMPv6_PRIVATE_EXPERIMENTATION1 = 100, - /** Private Experimentation Message */ - ICMPv6_PRIVATE_EXPERIMENTATION2 = 101, - /** Reserved for expansion of ICMPv6 error messages */ - ICMPv6_RESERVED_EXPANSION_ERROR = 127, - /** Echo Request Message */ - ICMPv6_ECHO_REQUEST = 128, - /** Echo Reply Message */ - ICMPv6_ECHO_REPLY = 129, - /** Multicast Listener Query Message */ - ICMPv6_MULTICAST_LISTENER_QUERY = 130, - /** Multicast Listener Report Message */ - ICMPv6_MULTICAST_LISTENER_REPORT = 131, - /** Multicast Listener Done Message */ - ICMPv6_MULTICAST_LISTENER_DONE = 132, - /** Router Solicitation Message */ - ICMPv6_ROUTER_SOLICITATION = 133, - /** Router Advertisement Message */ - ICMPv6_ROUTER_ADVERTISEMENT = 134, - /** Neighbor Solicitation Message */ - ICMPv6_NEIGHBOR_SOLICITATION = 135, - /** Neighbor Advertisement Message */ - ICMPv6_NEIGHBOR_ADVERTISEMENT = 136, - /** Redirect Message */ - ICMPv6_REDIRECT_MESSAGE = 137, - /** Router Renumbering Message */ - ICMPv6_ROUTER_RENUMBERING = 138, - /** Node Information Query Message */ - ICMPv6_ICMP_NODE_INFORMATION_QUERY = 139, - /** Node Information Reply Message*/ - ICMPv6_ICMP_NODE_INFORMATION_RESPONSE = 140, - /** Inverse Neighbor Discovery Solicitation Message */ - ICMPv6_INVERSE_NEIGHBOR_DISCOVERY_SOLICITATION_MESSAGE = 141, - /** Inverse Neighbor Discovery Advertisement Message */ - ICMPv6_INVERSE_NEIGHBOR_DISCOVERY_ADVERTISEMENT_MESSAGE = 142, - /** Multicast Listener Report Message */ - ICMPv6_MULTICAST_LISTENER_DISCOVERY_REPORTS = 143, - /** Home Agent Address Discovery Request Message */ - ICMPv6_HOME_AGENT_ADDRESS_DISCOVERY_REQUEST_MESSAGE = 144, - /** Home Agent Address Discovery Reply Message */ - ICMPv6_HOME_AGENT_ADDRESS_DISCOVERY_REPLY_MESSAGE = 145, - /** Mobile Prefix Solicitation Message */ - ICMPv6_MOBILE_PREFIX_SOLICITATION = 146, - /** Mobile Prefix Advertisement Message */ - ICMPv6_MOBILE_PREFIX_ADVERTISEMENT = 147, - /** Certification Path Solicitation Message */ - ICMPv6_CERTIFICATION_PATH_SOLICITATION = 148, - /** Certification Path Advertisement Message */ - ICMPv6_CERTIFICATION_PATH_ADVERTISEMENT = 149, - /** ICMP Experimental Mobility Subtype Format and Registry Message */ - ICMPv6_EXPERIMENTAL_MOBILITY = 150, - /** Multicast Router Advertisement Message */ - ICMPv6_MULTICAST_ROUTER_ADVERTISEMENT = 151, - /** Multicast Router Solicitation Message */ - ICMPv6_MULTICAST_ROUTER_SOLICITATION = 152, - /** Multicast Router Termination Message*/ - ICMPv6_MULTICAST_ROUTER_TERMINATION = 153, - /** RPL Control Message */ - ICMPv6_RPL_CONTROL_MESSAGE = 155, - /** Private Experimentation Message */ - ICMPv6_PRIVATE_EXPERIMENTATION3 = 200, - /** Private Experimentation Message */ - ICMPv6_PRIVATE_EXPERIMENTATION4 = 201, - /** Reserved for expansion of ICMPv6 informational messages */ - ICMPv6_RESERVED_EXPANSION_INFORMATIONAL = 255 +enum class ICMPv6MessageType : int { + /** Unknown ICMPv6 message */ + ICMPv6_UNKNOWN_MESSAGE = 0, + /** Destination Unreachable Message */ + ICMPv6_DESTINATION_UNREACHABLE = 1, + /** Packet Too Big Message */ + ICMPv6_PACKET_TOO_BIG = 2, + /** Time Exceeded Message */ + ICMPv6_TIME_EXCEEDED = 3, + /** Parameter Problem Message */ + ICMPv6_PARAMETER_PROBLEM = 4, + /** Private Experimentation Message */ + ICMPv6_PRIVATE_EXPERIMENTATION1 = 100, + /** Private Experimentation Message */ + ICMPv6_PRIVATE_EXPERIMENTATION2 = 101, + /** Reserved for expansion of ICMPv6 error messages */ + ICMPv6_RESERVED_EXPANSION_ERROR = 127, + /** Echo Request Message */ + ICMPv6_ECHO_REQUEST = 128, + /** Echo Reply Message */ + ICMPv6_ECHO_REPLY = 129, + /** Multicast Listener Query Message */ + ICMPv6_MULTICAST_LISTENER_QUERY = 130, + /** Multicast Listener Report Message */ + ICMPv6_MULTICAST_LISTENER_REPORT = 131, + /** Multicast Listener Done Message */ + ICMPv6_MULTICAST_LISTENER_DONE = 132, + /** Router Solicitation Message */ + ICMPv6_ROUTER_SOLICITATION = 133, + /** Router Advertisement Message */ + ICMPv6_ROUTER_ADVERTISEMENT = 134, + /** Neighbor Solicitation Message */ + ICMPv6_NEIGHBOR_SOLICITATION = 135, + /** Neighbor Advertisement Message */ + ICMPv6_NEIGHBOR_ADVERTISEMENT = 136, + /** Redirect Message */ + ICMPv6_REDIRECT_MESSAGE = 137, + /** Router Renumbering Message */ + ICMPv6_ROUTER_RENUMBERING = 138, + /** Node Information Query Message */ + ICMPv6_ICMP_NODE_INFORMATION_QUERY = 139, + /** Node Information Reply Message*/ + ICMPv6_ICMP_NODE_INFORMATION_RESPONSE = 140, + /** Inverse Neighbor Discovery Solicitation Message */ + ICMPv6_INVERSE_NEIGHBOR_DISCOVERY_SOLICITATION_MESSAGE = 141, + /** Inverse Neighbor Discovery Advertisement Message */ + ICMPv6_INVERSE_NEIGHBOR_DISCOVERY_ADVERTISEMENT_MESSAGE = 142, + /** Multicast Listener Report Message */ + ICMPv6_MULTICAST_LISTENER_DISCOVERY_REPORTS = 143, + /** Home Agent Address Discovery Request Message */ + ICMPv6_HOME_AGENT_ADDRESS_DISCOVERY_REQUEST_MESSAGE = 144, + /** Home Agent Address Discovery Reply Message */ + ICMPv6_HOME_AGENT_ADDRESS_DISCOVERY_REPLY_MESSAGE = 145, + /** Mobile Prefix Solicitation Message */ + ICMPv6_MOBILE_PREFIX_SOLICITATION = 146, + /** Mobile Prefix Advertisement Message */ + ICMPv6_MOBILE_PREFIX_ADVERTISEMENT = 147, + /** Certification Path Solicitation Message */ + ICMPv6_CERTIFICATION_PATH_SOLICITATION = 148, + /** Certification Path Advertisement Message */ + ICMPv6_CERTIFICATION_PATH_ADVERTISEMENT = 149, + /** ICMP Experimental Mobility Subtype Format and Registry Message */ + ICMPv6_EXPERIMENTAL_MOBILITY = 150, + /** Multicast Router Advertisement Message */ + ICMPv6_MULTICAST_ROUTER_ADVERTISEMENT = 151, + /** Multicast Router Solicitation Message */ + ICMPv6_MULTICAST_ROUTER_SOLICITATION = 152, + /** Multicast Router Termination Message*/ + ICMPv6_MULTICAST_ROUTER_TERMINATION = 153, + /** RPL Control Message */ + ICMPv6_RPL_CONTROL_MESSAGE = 155, + /** Private Experimentation Message */ + ICMPv6_PRIVATE_EXPERIMENTATION3 = 200, + /** Private Experimentation Message */ + ICMPv6_PRIVATE_EXPERIMENTATION4 = 201, + /** Reserved for expansion of ICMPv6 informational messages */ + ICMPv6_RESERVED_EXPANSION_INFORMATIONAL = 255 }; /** @@ -100,15 +98,17 @@ enum class ICMPv6MessageType : int * Represents an ICMPv6 protocol header */ #pragma pack(push, 1) -struct icmpv6hdr -{ - /** Type of the message. Values in the range from 0 to 127 (high-order bit is 0) indicate an error message, - while values in the range from 128 to 255 (high-order bit is 1) indicate an information message. */ - uint8_t type; - /** The code field value depends on the message type and provides an additional level of message granularity */ - uint8_t code; - /** The checksum field provides a minimal level of integrity verification for the ICMP message */ - uint16_t checksum; +struct icmpv6hdr { + /** Type of the message. Values in the range from 0 to 127 (high-order bit is + 0) indicate an error message, while values in the range from 128 to 255 + (high-order bit is 1) indicate an information message. */ + uint8_t type; + /** The code field value depends on the message type and provides an + * additional level of message granularity */ + uint8_t code; + /** The checksum field provides a minimal level of integrity verification for + * the ICMP message */ + uint16_t checksum; }; #pragma pack(pop) @@ -117,165 +117,173 @@ struct icmpv6hdr * ICMP echo request/reply message structure */ #pragma pack(push, 1) -typedef struct icmpv6_echo_hdr : icmpv6hdr -{ - /** the echo request identifier */ - uint16_t id; - /** the echo request sequence number */ - uint16_t sequence; +typedef struct icmpv6_echo_hdr : icmpv6hdr { + /** the echo request identifier */ + uint16_t id; + /** the echo request sequence number */ + uint16_t sequence; } icmpv6_echo_hdr; #pragma pack(pop) /** * @class IcmpV6Layer - * Base class for ICMPv6 protocol layers which provides common logic for ICMPv6 messages. + * Base class for ICMPv6 protocol layers which provides common logic for ICMPv6 + * messages. */ -class IcmpV6Layer : public Layer -{ -public: - /** - * A constructor that creates the layer from an existing packet raw data - * @param data A pointer to the raw data - * @param dataLen Size of the data in bytes - * @param prevLayer A pointer to the previous layer - * @param packet A pointer to the Packet instance where layer will be stored in - */ - IcmpV6Layer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) - : Layer(data, dataLen, prevLayer, packet) { m_Protocol = ICMPv6; } - - /** - * A constructor that allocates a new ICMPv6 layer with type, code and data - * @param[in] msgType Message type of the ICMPv6 layer - * @param[in] code Code field of the ICMPv6 layer - * @param[in] data A pointer to the payload to set - * @param[in] dataLen The length of the payload - */ - IcmpV6Layer(ICMPv6MessageType msgType, uint8_t code, const uint8_t *data, size_t dataLen); - - virtual ~IcmpV6Layer() {} - - /** - * A static method that creates an ICMPv6 layer from 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 - * @return Layer* A newly allocated ICMPv6 layer - */ - static Layer *parseIcmpV6Layer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet); - - /** - * @param[in] type Type to check - * @return True if the layer if of the given type, false otherwise - */ - bool isMessageOfType(ICMPv6MessageType type) const { return getMessageType() == type; } - - /** - * @return Get the ICMPv6 Message Type - */ - ICMPv6MessageType getMessageType() const; - - /** - * @return Get the code header field - */ - uint8_t getCode() const; - - /** - * @return Get the checksum header field in host representation - */ - uint16_t getChecksum() const; - - /** - * Does nothing for this layer. ICMPv6 is the last layer. - */ - void parseNextLayer() {} - - /** - * @return The size of the ICMPv6 message - */ - size_t getHeaderLen() const { return m_DataLen; } - - /** - * Calculate ICMPv6 checksum field - */ - void computeCalculateFields(); - - OsiModelLayer getOsiModelLayer() const { return OsiModelNetworkLayer; } - - std::string toString() const; - -protected: - IcmpV6Layer() = default; - -private: - void calculateChecksum(); - icmpv6hdr *getIcmpv6Header() const { return (icmpv6hdr *)m_Data; } +class IcmpV6Layer : public Layer { + public: + /** + * A constructor that creates the layer from an existing packet raw data + * @param data A pointer to the raw data + * @param dataLen Size of the data in bytes + * @param prevLayer A pointer to the previous layer + * @param packet A pointer to the Packet instance where layer will be stored + * in + */ + IcmpV6Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : Layer(data, dataLen, prevLayer, packet) { + m_Protocol = ICMPv6; + } + + /** + * A constructor that allocates a new ICMPv6 layer with type, code and data + * @param[in] msgType Message type of the ICMPv6 layer + * @param[in] code Code field of the ICMPv6 layer + * @param[in] data A pointer to the payload to set + * @param[in] dataLen The length of the payload + */ + IcmpV6Layer(ICMPv6MessageType msgType, uint8_t code, const uint8_t* data, + size_t dataLen); + + virtual ~IcmpV6Layer() {} + + /** + * A static method that creates an ICMPv6 layer from 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 + * @return Layer* A newly allocated ICMPv6 layer + */ + static Layer* parseIcmpV6Layer(uint8_t* data, size_t dataLen, + Layer* prevLayer, Packet* packet); + + /** + * @param[in] type Type to check + * @return True if the layer if of the given type, false otherwise + */ + bool isMessageOfType(ICMPv6MessageType type) const { + return getMessageType() == type; + } + + /** + * @return Get the ICMPv6 Message Type + */ + ICMPv6MessageType getMessageType() const; + + /** + * @return Get the code header field + */ + uint8_t getCode() const; + + /** + * @return Get the checksum header field in host representation + */ + uint16_t getChecksum() const; + + /** + * Does nothing for this layer. ICMPv6 is the last layer. + */ + void parseNextLayer() {} + + /** + * @return The size of the ICMPv6 message + */ + size_t getHeaderLen() const { return m_DataLen; } + + /** + * Calculate ICMPv6 checksum field + */ + void computeCalculateFields(); + + OsiModelLayer getOsiModelLayer() const { return OsiModelNetworkLayer; } + + std::string toString() const; + + protected: + IcmpV6Layer() = default; + + private: + void calculateChecksum(); + icmpv6hdr* getIcmpv6Header() const { return (icmpv6hdr*)m_Data; } }; /** * @class ICMPv6EchoLayer * Represents an ICMPv6 echo request/reply protocol layer */ -class ICMPv6EchoLayer : public IcmpV6Layer -{ -public: - /** - * An enum representing ICMPv6 echo message types - */ - enum ICMPv6EchoType - { - /** Echo Request Type */ - REQUEST, - /** Echo Reply Type */ - REPLY - }; - - /** - * 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 - */ - ICMPv6EchoLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) - : IcmpV6Layer(data, dataLen, prevLayer, packet) {} - - /** - * A constructor for a new echo request/reply layer - * @param[in] echoType The type of the echo message - * @param[in] id Echo request identifier - * @param[in] sequence Echo request sequence number - * @param[in] data A pointer to echo request payload to set - * @param[in] dataLen The length of the echo request payload - */ - ICMPv6EchoLayer(ICMPv6EchoType echoType, uint16_t id, uint16_t sequence, const uint8_t *data, size_t dataLen); - - virtual ~ICMPv6EchoLayer() {} - - /** - * @return Identifier in host representation - */ - uint16_t getIdentifier() const; - - /** - * @return Sequence number in host representation - */ - uint16_t getSequenceNr() const; - - /** - * @return Size of the data in bytes - */ - size_t getEchoDataLen() const { return m_DataLen - sizeof(icmpv6_echo_hdr); } - - /** - * @return Pointer to the beginning of the data - */ - uint8_t *getEchoDataPtr() const { return m_Data + sizeof(icmpv6_echo_hdr); } - - std::string toString() const; - -private: - icmpv6_echo_hdr *getEchoHeader() const { return (icmpv6_echo_hdr *)m_Data; } +class ICMPv6EchoLayer : public IcmpV6Layer { + public: + /** + * An enum representing ICMPv6 echo message types + */ + enum ICMPv6EchoType { + /** Echo Request Type */ + REQUEST, + /** Echo Reply Type */ + REPLY + }; + + /** + * 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 + */ + ICMPv6EchoLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet) + : IcmpV6Layer(data, dataLen, prevLayer, packet) {} + + /** + * A constructor for a new echo request/reply layer + * @param[in] echoType The type of the echo message + * @param[in] id Echo request identifier + * @param[in] sequence Echo request sequence number + * @param[in] data A pointer to echo request payload to set + * @param[in] dataLen The length of the echo request payload + */ + ICMPv6EchoLayer(ICMPv6EchoType echoType, uint16_t id, uint16_t sequence, + const uint8_t* data, size_t dataLen); + + virtual ~ICMPv6EchoLayer() {} + + /** + * @return Identifier in host representation + */ + uint16_t getIdentifier() const; + + /** + * @return Sequence number in host representation + */ + uint16_t getSequenceNr() const; + + /** + * @return Size of the data in bytes + */ + size_t getEchoDataLen() const { return m_DataLen - sizeof(icmpv6_echo_hdr); } + + /** + * @return Pointer to the beginning of the data + */ + uint8_t* getEchoDataPtr() const { return m_Data + sizeof(icmpv6_echo_hdr); } + + std::string toString() const; + + private: + icmpv6_echo_hdr* getEchoHeader() const { return (icmpv6_echo_hdr*)m_Data; } }; } // namespace pcpp diff --git a/Packet++/header/IgmpLayer.h b/Packet++/header/IgmpLayer.h index e5041e69e0..c2ff21099e 100644 --- a/Packet++/header/IgmpLayer.h +++ b/Packet++/header/IgmpLayer.h @@ -1,8 +1,8 @@ #ifndef PACKETPP_IGMP_LAYER #define PACKETPP_IGMP_LAYER -#include "Layer.h" #include "IpAddress.h" +#include "Layer.h" #include /// @file @@ -11,504 +11,564 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ +namespace pcpp { /** * @struct igmp_header * IGMPv1 and IGMPv2 basic protocol header */ -struct igmp_header -{ - /** Indicates the message type. The enum for message type is pcpp::IgmpType */ - uint8_t type; - /** Specifies the time limit for the corresponding report. The field has a resolution of 100 milliseconds */ - uint8_t maxResponseTime; - /** This is the 16-bit one's complement of the one's complement sum of the entire IGMP message */ - uint16_t checksum; - /** This is the multicast address being queried when sending a Group-Specific or Group-and-Source-Specific Query */ - uint32_t groupAddress; +struct igmp_header { + /** Indicates the message type. The enum for message type is pcpp::IgmpType */ + uint8_t type; + /** Specifies the time limit for the corresponding report. The field has a + * resolution of 100 milliseconds */ + uint8_t maxResponseTime; + /** This is the 16-bit one's complement of the one's complement sum of the + * entire IGMP message */ + uint16_t checksum; + /** This is the multicast address being queried when sending a Group-Specific + * or Group-and-Source-Specific Query */ + uint32_t groupAddress; }; - /** * @struct igmpv3_query_header * IGMPv3 membership query basic header */ -struct igmpv3_query_header -{ - /** IGMP message type. Should always have value of membership query (::IgmpType_MembershipQuery) */ - uint8_t type; - /** This field specifies the maximum time (in 1/10 second) allowed before sending a responding report */ - uint8_t maxResponseTime; - /** This is the 16-bit one's complement of the one's complement sum of the entire IGMP message */ - uint16_t checksum; - /** This is the multicast address being queried when sending a Group-Specific or Group-and-Source-Specific Query */ - uint32_t groupAddress; - /** Suppress Router-side Processing Flag + Querier's Robustness Variable */ - uint8_t s_qrv; - /** Querier's Query Interval Code */ - uint8_t qqic; - /** This field specifies the number of source addresses present in the Query */ - uint16_t numOfSources; +struct igmpv3_query_header { + /** IGMP message type. Should always have value of membership query + * (::IgmpType_MembershipQuery) */ + uint8_t type; + /** This field specifies the maximum time (in 1/10 second) allowed before + * sending a responding report */ + uint8_t maxResponseTime; + /** This is the 16-bit one's complement of the one's complement sum of the + * entire IGMP message */ + uint16_t checksum; + /** This is the multicast address being queried when sending a Group-Specific + * or Group-and-Source-Specific Query */ + uint32_t groupAddress; + /** Suppress Router-side Processing Flag + Querier's Robustness Variable */ + uint8_t s_qrv; + /** Querier's Query Interval Code */ + uint8_t qqic; + /** This field specifies the number of source addresses present in the Query + */ + uint16_t numOfSources; }; - /** * @struct igmpv3_report_header * IGMPv3 membership report basic header */ -struct igmpv3_report_header -{ - /** IGMP message type. Should always have value of IGMPv3 membership report (::IgmpType_MembershipReportV3) */ - uint8_t type; - /** Unused byte */ - uint8_t reserved1; - /** This is the 16-bit one's complement of the one's complement sum of the entire IGMP message */ - uint16_t checksum; - /** Unused bytes */ - uint16_t reserved2; - /** This field specifies the number of group records present in the Report */ - uint16_t numOfGroupRecords; +struct igmpv3_report_header { + /** IGMP message type. Should always have value of IGMPv3 membership report + * (::IgmpType_MembershipReportV3) */ + uint8_t type; + /** Unused byte */ + uint8_t reserved1; + /** This is the 16-bit one's complement of the one's complement sum of the + * entire IGMP message */ + uint16_t checksum; + /** Unused bytes */ + uint16_t reserved2; + /** This field specifies the number of group records present in the Report */ + uint16_t numOfGroupRecords; }; - /** * @struct igmpv3_group_record - * A block of fields containing information pertaining to the sender's membership in a single multicast group on the interface - * from which the Report is sent. Relevant only for IGMPv3 membership report messages + * A block of fields containing information pertaining to the sender's + * membership in a single multicast group on the interface from which the Report + * is sent. Relevant only for IGMPv3 membership report messages */ -struct igmpv3_group_record -{ - /** Group record type */ - uint8_t recordType; - /** Contains the length of the Auxiliary Data field in this Group Record. A value other than 0 isn't supported */ - uint8_t auxDataLen; - /** Specifies how many source addresses are present in this Group Record */ - uint16_t numOfSources; - /** Contains the IP multicast address to which this Group Record pertains */ - uint32_t multicastAddress; - /** A vector of n IP unicast addresses, where n is the value in this record's Number of Sources field */ - uint8_t sourceAddresses[]; - - /** - * @return The multicast address in igmpv3_group_record#multicastAddress as IPv4Address instance - */ - IPv4Address getMulticastAddress() const { return multicastAddress; } - - /** - * @return The number of source addresses in this group record - */ - uint16_t getSourceAddressCount() const; - - /** - * Get the source address at a certain index - * @param[in] index The index of the source address in the group record - * @return The source address in the requested index. If index is negative or higher than the number of source addresses in this - * group record the value if IPv4Address#Zero is returned - */ - IPv4Address getSourceAddressAtIndex(int index) const; - - /** - * @return The total size in bytes of the group record - */ - size_t getRecordLen() const; +struct igmpv3_group_record { + /** Group record type */ + uint8_t recordType; + /** Contains the length of the Auxiliary Data field in this Group Record. A + * value other than 0 isn't supported */ + uint8_t auxDataLen; + /** Specifies how many source addresses are present in this Group Record */ + uint16_t numOfSources; + /** Contains the IP multicast address to which this Group Record pertains */ + uint32_t multicastAddress; + /** A vector of n IP unicast addresses, where n is the value in this record's + * Number of Sources field */ + uint8_t sourceAddresses[]; + + /** + * @return The multicast address in igmpv3_group_record#multicastAddress as + * IPv4Address instance + */ + IPv4Address getMulticastAddress() const { return multicastAddress; } + + /** + * @return The number of source addresses in this group record + */ + uint16_t getSourceAddressCount() const; + + /** + * Get the source address at a certain index + * @param[in] index The index of the source address in the group record + * @return The source address in the requested index. If index is negative or + * higher than the number of source addresses in this group record the value + * if IPv4Address#Zero is returned + */ + IPv4Address getSourceAddressAtIndex(int index) const; + + /** + * @return The total size in bytes of the group record + */ + size_t getRecordLen() const; }; - /** * IGMP message types */ -enum IgmpType -{ - /** Unknown message type */ - IgmpType_Unknown = 0, - /** IGMP Membership Query */ - IgmpType_MembershipQuery = 0x11, - /** IGMPv1 Membership Report */ - IgmpType_MembershipReportV1 = 0x12, - /** DVMRP */ - IgmpType_DVMRP = 0x13, - /** PIM version 1 */ - IgmpType_P1Mv1 = 0x14, - /** Cisco Trace Messages */ - IgmpType_CiscoTrace = 0x15, - /** IGMPv2 Membership Report */ - IgmpType_MembershipReportV2 = 0x16, - /** IGMPv2 Leave Group */ - IgmpType_LeaveGroup = 0x17, - /** Multicast Traceroute Response */ - IgmpType_MulticastTracerouteResponse = 0x1e, - /** Multicast Traceroute */ - IgmpType_MulticastTraceroute = 0x1f, - /** IGMPv3 Membership Report */ - IgmpType_MembershipReportV3 = 0x22, - /** MRD, Multicast Router Advertisement */ - IgmpType_MulticastRouterAdvertisement = 0x30, - /** MRD, Multicast Router Solicitation */ - IgmpType_MulticastRouterSolicitation = 0x31, - /** MRD, Multicast Router Termination */ - IgmpType_MulticastRouterTermination = 0x32, +enum IgmpType { + /** Unknown message type */ + IgmpType_Unknown = 0, + /** IGMP Membership Query */ + IgmpType_MembershipQuery = 0x11, + /** IGMPv1 Membership Report */ + IgmpType_MembershipReportV1 = 0x12, + /** DVMRP */ + IgmpType_DVMRP = 0x13, + /** PIM version 1 */ + IgmpType_P1Mv1 = 0x14, + /** Cisco Trace Messages */ + IgmpType_CiscoTrace = 0x15, + /** IGMPv2 Membership Report */ + IgmpType_MembershipReportV2 = 0x16, + /** IGMPv2 Leave Group */ + IgmpType_LeaveGroup = 0x17, + /** Multicast Traceroute Response */ + IgmpType_MulticastTracerouteResponse = 0x1e, + /** Multicast Traceroute */ + IgmpType_MulticastTraceroute = 0x1f, + /** IGMPv3 Membership Report */ + IgmpType_MembershipReportV3 = 0x22, + /** MRD, Multicast Router Advertisement */ + IgmpType_MulticastRouterAdvertisement = 0x30, + /** MRD, Multicast Router Solicitation */ + IgmpType_MulticastRouterSolicitation = 0x31, + /** MRD, Multicast Router Termination */ + IgmpType_MulticastRouterTermination = 0x32, }; - /** * @class IgmpLayer - * A base class for all IGMP (Internet Group Management Protocol) protocol classes. This is an abstract class and cannot be instantiated, - * only its child classes can be instantiated. The inherited classes represent the different versions of the protocol: - * IGMPv1, IGMPv2 and IGMPv3 + * A base class for all IGMP (Internet Group Management Protocol) protocol + * classes. This is an abstract class and cannot be instantiated, only its child + * classes can be instantiated. The inherited classes represent the different + * versions of the protocol: IGMPv1, IGMPv2 and IGMPv3 */ -class IgmpLayer : public Layer -{ -protected: - - IgmpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet, ProtocolType igmpVer) : Layer(data, dataLen, prevLayer, packet) { m_Protocol = igmpVer; } - - IgmpLayer(IgmpType type, const IPv4Address& groupAddr, uint8_t maxResponseTime, ProtocolType igmpVer); - - uint16_t calculateChecksum(); - - size_t getHeaderSizeByVerAndType(ProtocolType igmpVer, IgmpType igmpType) const; -public: - - virtual ~IgmpLayer() {} - - /** - * Get a pointer to the raw IGMPv1/IGMPv2 header. Notice this points directly to the data, so every change will change the actual packet data - * @return A pointer to the @ref igmp_header - */ - igmp_header* getIgmpHeader() const { return (igmp_header*)m_Data; } - - /** - * @return The IPv4 multicast address stored igmp_header#groupAddress - */ - IPv4Address getGroupAddress() const { return getIgmpHeader()->groupAddress; } - - /** - * Set the IPv4 multicast address - * @param[in] groupAddr The IPv4 address to set - */ - void setGroupAddress(const IPv4Address& groupAddr); - - /** - * @return IGMP type set in igmp_header#type as ::IgmpType enum. Notice that if igmp_header#type contains a value - * that doesn't appear in the ::IgmpType enum, ::IgmpType_Unknown will be returned - */ - IgmpType getType() const; - - /** - * Set IGMP type (will be written to igmp_header#type field) - * @param[in] type The type to set - */ - void setType(IgmpType type); - - /** - * A static method that gets raw IGMP data (byte stream) and returns the IGMP version of this IGMP message - * @param[in] data The IGMP raw data (byte stream) - * @param[in] dataLen Raw data length - * @param[out] isQuery Return true if IGMP message type is ::IgmpType_MembershipQuery and false otherwise - * @return One of the values ::IGMPv1, ::IGMPv2, ::IGMPv3 according to detected IGMP version or ::UnknownProtocol if couldn't detect - * IGMP version - */ - static ProtocolType getIGMPVerFromData(uint8_t* data, size_t dataLen, bool& isQuery); - - - // implement abstract methods - - /** - * Does nothing for this layer (IGMP layer is always last) - */ - void parseNextLayer() {} - - /** - * @return Size of IGMP header = 8B - */ - size_t getHeaderLen() const { return sizeof(igmp_header); } - - std::string toString() const; - - OsiModelLayer getOsiModelLayer() const { return OsiModelNetworkLayer; } +class IgmpLayer : public Layer { + protected: + IgmpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet, + ProtocolType igmpVer) + : Layer(data, dataLen, prevLayer, packet) { + m_Protocol = igmpVer; + } + + IgmpLayer(IgmpType type, const IPv4Address& groupAddr, + uint8_t maxResponseTime, ProtocolType igmpVer); + + uint16_t calculateChecksum(); + + size_t getHeaderSizeByVerAndType(ProtocolType igmpVer, + IgmpType igmpType) const; + + public: + virtual ~IgmpLayer() {} + + /** + * Get a pointer to the raw IGMPv1/IGMPv2 header. Notice this points directly + * to the data, so every change will change the actual packet data + * @return A pointer to the @ref igmp_header + */ + igmp_header* getIgmpHeader() const { return (igmp_header*)m_Data; } + + /** + * @return The IPv4 multicast address stored igmp_header#groupAddress + */ + IPv4Address getGroupAddress() const { return getIgmpHeader()->groupAddress; } + + /** + * Set the IPv4 multicast address + * @param[in] groupAddr The IPv4 address to set + */ + void setGroupAddress(const IPv4Address& groupAddr); + + /** + * @return IGMP type set in igmp_header#type as ::IgmpType enum. Notice that + * if igmp_header#type contains a value that doesn't appear in the ::IgmpType + * enum, ::IgmpType_Unknown will be returned + */ + IgmpType getType() const; + + /** + * Set IGMP type (will be written to igmp_header#type field) + * @param[in] type The type to set + */ + void setType(IgmpType type); + + /** + * A static method that gets raw IGMP data (byte stream) and returns the IGMP + * version of this IGMP message + * @param[in] data The IGMP raw data (byte stream) + * @param[in] dataLen Raw data length + * @param[out] isQuery Return true if IGMP message type is + * ::IgmpType_MembershipQuery and false otherwise + * @return One of the values ::IGMPv1, ::IGMPv2, ::IGMPv3 according to + * detected IGMP version or ::UnknownProtocol if couldn't detect IGMP version + */ + static ProtocolType getIGMPVerFromData(uint8_t* data, size_t dataLen, + bool& isQuery); + + // implement abstract methods + + /** + * Does nothing for this layer (IGMP layer is always last) + */ + void parseNextLayer() {} + + /** + * @return Size of IGMP header = 8B + */ + size_t getHeaderLen() const { return sizeof(igmp_header); } + + std::string toString() const; + + OsiModelLayer getOsiModelLayer() const { return OsiModelNetworkLayer; } }; - /** * @class IgmpV1Layer - * Represents IGMPv1 (Internet Group Management Protocol ver 1) layer. This class represents all the different messages of IGMPv1 + * Represents IGMPv1 (Internet Group Management Protocol ver 1) layer. This + * class represents all the different messages of IGMPv1 */ -class IgmpV1Layer : public IgmpLayer -{ -public: - /** 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 - */ - IgmpV1Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) - : IgmpLayer(data, dataLen, prevLayer, packet, IGMPv1) {} - - /** - * A constructor that allocates a new IGMPv1 header - * @param[in] type The message type to set - * @param[in] groupAddr The multicast address to set. This is an optional parameter and has a default value of IPv4Address#Zero - * if not provided - */ - explicit IgmpV1Layer(IgmpType type, const IPv4Address& groupAddr = IPv4Address()) - : IgmpLayer(type, groupAddr, 0, IGMPv1) {} - - /** - * A destructor for this layer (does nothing) - */ - ~IgmpV1Layer() {} - - - // implement abstract methods - - /** - * Calculate the IGMP checksum and set igmp_header#maxResponseTime to 0 (this field is unused in IGMPv1) - */ - void computeCalculateFields(); - +class IgmpV1Layer : public IgmpLayer { + public: + /** 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 + */ + IgmpV1Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : IgmpLayer(data, dataLen, prevLayer, packet, IGMPv1) {} + + /** + * A constructor that allocates a new IGMPv1 header + * @param[in] type The message type to set + * @param[in] groupAddr The multicast address to set. This is an optional + * parameter and has a default value of IPv4Address#Zero if not provided + */ + explicit IgmpV1Layer(IgmpType type, + const IPv4Address& groupAddr = IPv4Address()) + : IgmpLayer(type, groupAddr, 0, IGMPv1) {} + + /** + * A destructor for this layer (does nothing) + */ + ~IgmpV1Layer() {} + + // implement abstract methods + + /** + * Calculate the IGMP checksum and set igmp_header#maxResponseTime to 0 (this + * field is unused in IGMPv1) + */ + void computeCalculateFields(); }; - /** * @class IgmpV2Layer - * Represents IGMPv2 (Internet Group Management Protocol ver 2) layer. This class represents all the different messages of IGMPv2 + * Represents IGMPv2 (Internet Group Management Protocol ver 2) layer. This + * class represents all the different messages of IGMPv2 */ -class IgmpV2Layer : public IgmpLayer -{ -public: - /** 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 - */ - IgmpV2Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) - : IgmpLayer(data, dataLen, prevLayer, packet, IGMPv2) {} - - /** - * A constructor that allocates a new IGMPv2 header - * @param[in] type The message type to set - * @param[in] groupAddr The multicast address to set. This is an optional parameter and has a default value of unspecified/zero IPv4 address - * @param[in] maxResponseTime The max response time to set. This is an optional parameter and has a default value of 0 if not provided - */ - explicit IgmpV2Layer(IgmpType type, const IPv4Address& groupAddr = IPv4Address(), uint8_t maxResponseTime = 0) - : IgmpLayer(type, groupAddr, maxResponseTime, IGMPv2) {} - - /** - * A destructor for this layer (does nothing) - */ - ~IgmpV2Layer() {} - - - // implement abstract methods - - /** - * Calculate the IGMP checksum - */ - void computeCalculateFields(); +class IgmpV2Layer : public IgmpLayer { + public: + /** 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 + */ + IgmpV2Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : IgmpLayer(data, dataLen, prevLayer, packet, IGMPv2) {} + + /** + * A constructor that allocates a new IGMPv2 header + * @param[in] type The message type to set + * @param[in] groupAddr The multicast address to set. This is an optional + * parameter and has a default value of unspecified/zero IPv4 address + * @param[in] maxResponseTime The max response time to set. This is an + * optional parameter and has a default value of 0 if not provided + */ + explicit IgmpV2Layer(IgmpType type, + const IPv4Address& groupAddr = IPv4Address(), + uint8_t maxResponseTime = 0) + : IgmpLayer(type, groupAddr, maxResponseTime, IGMPv2) {} + + /** + * A destructor for this layer (does nothing) + */ + ~IgmpV2Layer() {} + + // implement abstract methods + + /** + * Calculate the IGMP checksum + */ + void computeCalculateFields(); }; - /** * @class IgmpV3QueryLayer - * Represents an IGMPv3 (Internet Group Management Protocol ver 3) membership query message + * Represents an IGMPv3 (Internet Group Management Protocol ver 3) membership + * query message */ -class IgmpV3QueryLayer : public IgmpLayer -{ -public: - - /** 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 - */ - IgmpV3QueryLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); - - /** - * A constructor that allocates a new IGMPv3 membership query - * @param[in] multicastAddr The multicast address to set. This is an optional parameter and has a default value of unspecified/zero IPv4 address - * if not provided - * @param[in] maxResponseTime The max response time to set. This is an optional parameter and has a default value of 0 if not provided - * @param[in] s_qrv A 1-byte value representing the value in Suppress Router-side Processing Flag + Querier's Robustness Variable - * (igmpv3_query_header#s_qrv field). This is an optional parameter and has a default value of 0 if not provided - */ - explicit IgmpV3QueryLayer(const IPv4Address& multicastAddr = IPv4Address(), uint8_t maxResponseTime = 0, uint8_t s_qrv = 0); - - /** - * Get a pointer to the raw IGMPv3 membership query header. Notice this points directly to the data, so every change will change the - * actual packet data - * @return A pointer to the @ref igmpv3_query_header - */ - igmpv3_query_header* getIgmpV3QueryHeader() const { return (igmpv3_query_header*)m_Data; } - - /** - * @return The number of source addresses in this message (as extracted from the igmpv3_query_header#numOfSources field) - */ - uint16_t getSourceAddressCount() const; - - /** - * Get the IPV4 source address in a certain index - * @param[in] index The requested index of the source address - * @return The IPv4 source address, or IPv4Address#Zero if index is out of bounds (of the message or of the layer) - */ - IPv4Address getSourceAddressAtIndex(int index) const; - - /** - * Add a new source address at the end of the source address list. The igmpv3_query_header#numOfSources field will be incremented accordingly - * @param[in] addr The IPv4 source address to add - * @return True if source address was added successfully or false otherwise. If false is returned an appropriate error message - * will be printed to log - */ - bool addSourceAddress(const IPv4Address& addr); - - /** - * Add a new source address at a certain index of the source address list. The igmpv3_query_header#numOfSources field will be incremented accordingly - * @param[in] addr The IPv4 source address to add - * @param[in] index The index to add the new source address at - * @return True if source address was added successfully or false otherwise. If false is returned an appropriate error message - * will be printed to log - */ - bool addSourceAddressAtIndex(const IPv4Address& addr, int index); - - /** - * Remove a source address at a certain index. The igmpv3_query_header#numOfSources field will be decremented accordingly - * @param[in] index The index of the source address to be removed - * @return True if source address was removed successfully or false otherwise. If false is returned an appropriate error message - * will be printed to log - */ - bool removeSourceAddressAtIndex(int index); - - /** - * Remove all source addresses in the message. The igmpv3_query_header#numOfSources field will be set to 0 - * @return True if all source addresses were cleared successfully or false otherwise. If false is returned an appropriate error message - * will be printed to log - */ - bool removeAllSourceAddresses(); - - // implement abstract methods - - /** - * Calculate the IGMP checksum - */ - void computeCalculateFields(); - - /** - * @return The message size in bytes which include the size of the basic header + the size of the source address list - */ - size_t getHeaderLen() const; +class IgmpV3QueryLayer : public IgmpLayer { + public: + /** 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 + */ + IgmpV3QueryLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet); + + /** + * A constructor that allocates a new IGMPv3 membership query + * @param[in] multicastAddr The multicast address to set. This is an optional + * parameter and has a default value of unspecified/zero IPv4 address if not + * provided + * @param[in] maxResponseTime The max response time to set. This is an + * optional parameter and has a default value of 0 if not provided + * @param[in] s_qrv A 1-byte value representing the value in Suppress + * Router-side Processing Flag + Querier's Robustness Variable + * (igmpv3_query_header#s_qrv field). This is an optional parameter and has a + * default value of 0 if not provided + */ + explicit IgmpV3QueryLayer(const IPv4Address& multicastAddr = IPv4Address(), + uint8_t maxResponseTime = 0, uint8_t s_qrv = 0); + + /** + * Get a pointer to the raw IGMPv3 membership query header. Notice this points + * directly to the data, so every change will change the actual packet data + * @return A pointer to the @ref igmpv3_query_header + */ + igmpv3_query_header* getIgmpV3QueryHeader() const { + return (igmpv3_query_header*)m_Data; + } + + /** + * @return The number of source addresses in this message (as extracted from + * the igmpv3_query_header#numOfSources field) + */ + uint16_t getSourceAddressCount() const; + + /** + * Get the IPV4 source address in a certain index + * @param[in] index The requested index of the source address + * @return The IPv4 source address, or IPv4Address#Zero if index is out of + * bounds (of the message or of the layer) + */ + IPv4Address getSourceAddressAtIndex(int index) const; + + /** + * Add a new source address at the end of the source address list. The + * igmpv3_query_header#numOfSources field will be incremented accordingly + * @param[in] addr The IPv4 source address to add + * @return True if source address was added successfully or false otherwise. + * If false is returned an appropriate error message will be printed to log + */ + bool addSourceAddress(const IPv4Address& addr); + + /** + * Add a new source address at a certain index of the source address list. The + * igmpv3_query_header#numOfSources field will be incremented accordingly + * @param[in] addr The IPv4 source address to add + * @param[in] index The index to add the new source address at + * @return True if source address was added successfully or false otherwise. + * If false is returned an appropriate error message will be printed to log + */ + bool addSourceAddressAtIndex(const IPv4Address& addr, int index); + + /** + * Remove a source address at a certain index. The + * igmpv3_query_header#numOfSources field will be decremented accordingly + * @param[in] index The index of the source address to be removed + * @return True if source address was removed successfully or false otherwise. + * If false is returned an appropriate error message will be printed to log + */ + bool removeSourceAddressAtIndex(int index); + + /** + * Remove all source addresses in the message. The + * igmpv3_query_header#numOfSources field will be set to 0 + * @return True if all source addresses were cleared successfully or false + * otherwise. If false is returned an appropriate error message will be + * printed to log + */ + bool removeAllSourceAddresses(); + + // implement abstract methods + + /** + * Calculate the IGMP checksum + */ + void computeCalculateFields(); + + /** + * @return The message size in bytes which include the size of the basic + * header + the size of the source address list + */ + size_t getHeaderLen() const; }; - /** * @class IgmpV3ReportLayer - * Represents an IGMPv3 (Internet Group Management Protocol ver 3) membership report message + * Represents an IGMPv3 (Internet Group Management Protocol ver 3) membership + * report message */ -class IgmpV3ReportLayer : public IgmpLayer -{ -private: - igmpv3_group_record* addGroupRecordAt(uint8_t recordType, const IPv4Address& multicastAddress, const std::vector& sourceAddresses, int offset); - -public: - - /** 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 - */ - IgmpV3ReportLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) - : IgmpLayer(data, dataLen, prevLayer, packet, IGMPv3) {} - - /** - * A constructor that allocates a new IGMPv3 membership report with 0 group addresses - */ - IgmpV3ReportLayer() : IgmpLayer(IgmpType_MembershipReportV3, IPv4Address(), 0, IGMPv3) {} - - /** - * Get a pointer to the raw IGMPv3 membership report header. Notice this points directly to the data, so every change will change the - * actual packet data - * @return A pointer to the @ref igmpv3_report_header - */ - igmpv3_report_header* getReportHeader() const { return (igmpv3_report_header*)m_Data; } - - /** - * @return The number of group records in this message (as extracted from the igmpv3_report_header#numOfGroupRecords field) - */ - uint16_t getGroupRecordCount() const; - - /** - * @return A pointer to the first group record or NULL if no group records exist. Notice the return value is a pointer to the real data, - * so changes in the return value will affect the packet data - */ - igmpv3_group_record* getFirstGroupRecord() const; - - /** - * Get the group record that comes next to a given group record. If "groupRecord" is NULL then NULL will be returned. - * If "groupRecord" is the last group record or if it is out of layer bounds NULL will be returned also. Notice the return value is a - * pointer to the real data casted to igmpv3_group_record type (as opposed to a copy of the option data). So changes in the return - * value will affect the packet data - * @param[in] groupRecord The group record to start searching from - * @return The next group record or NULL if "groupRecord" is NULL, last or out of layer bounds - */ - igmpv3_group_record* getNextGroupRecord(igmpv3_group_record* groupRecord) const; - - /** - * Add a new group record at a the end of the group record list. The igmpv3_report_header#numOfGroupRecords field will be - * incremented accordingly - * @param[in] recordType The type of the new group record - * @param[in] multicastAddress The multicast address of the new group record - * @param[in] sourceAddresses A vector containing all the source addresses of the new group record - * @return The method constructs a new group record, adds it to the end of the group record list of IGMPv3 report message and - * returns a pointer to the new message. If something went wrong in creating or adding the new group record a NULL value is returned - * and an appropriate error message is printed to log - */ - igmpv3_group_record* addGroupRecord(uint8_t recordType, const IPv4Address& multicastAddress, const std::vector& sourceAddresses); - - /** - * Add a new group record at a certain index of the group record list. The igmpv3_report_header#numOfGroupRecords field will be - * incremented accordingly - * @param[in] recordType The type of the new group record - * @param[in] multicastAddress The multicast address of the new group record - * @param[in] sourceAddresses A vector containing all the source addresses of the new group record - * @param[in] index The index to add the new group address at - * @return The method constructs a new group record, adds it to the IGMPv3 report message and returns a pointer to the new message. - * If something went wrong in creating or adding the new group record a NULL value is returned and an appropriate error message is - * printed to log - */ - igmpv3_group_record* addGroupRecordAtIndex(uint8_t recordType, const IPv4Address& multicastAddress, const std::vector& sourceAddresses, int index); - - /** - * Remove a group record at a certain index. The igmpv3_report_header#numOfGroupRecords field will be decremented accordingly - * @param[in] index The index of the group record to be removed - * @return True if group record was removed successfully or false otherwise. If false is returned an appropriate error message - * will be printed to log - */ - bool removeGroupRecordAtIndex(int index); - - /** - * Remove all group records in the message. The igmpv3_report_header#numOfGroupRecords field will be set to 0 - * @return True if all group records were cleared successfully or false otherwise. If false is returned an appropriate error message - * will be printed to log - */ - bool removeAllGroupRecords(); - - // implement abstract methods - - /** - * Calculate the IGMP checksum - */ - void computeCalculateFields(); - - /** - * @return The message size in bytes which include the size of the basic header + the size of the group record list - */ - size_t getHeaderLen() const { return m_DataLen; } +class IgmpV3ReportLayer : public IgmpLayer { + private: + igmpv3_group_record* + addGroupRecordAt(uint8_t recordType, const IPv4Address& multicastAddress, + const std::vector& sourceAddresses, int offset); + + public: + /** 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 + */ + IgmpV3ReportLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet) + : IgmpLayer(data, dataLen, prevLayer, packet, IGMPv3) {} + + /** + * A constructor that allocates a new IGMPv3 membership report with 0 group + * addresses + */ + IgmpV3ReportLayer() + : IgmpLayer(IgmpType_MembershipReportV3, IPv4Address(), 0, IGMPv3) {} + + /** + * Get a pointer to the raw IGMPv3 membership report header. Notice this + * points directly to the data, so every change will change the actual packet + * data + * @return A pointer to the @ref igmpv3_report_header + */ + igmpv3_report_header* getReportHeader() const { + return (igmpv3_report_header*)m_Data; + } + + /** + * @return The number of group records in this message (as extracted from the + * igmpv3_report_header#numOfGroupRecords field) + */ + uint16_t getGroupRecordCount() const; + + /** + * @return A pointer to the first group record or NULL if no group records + * exist. Notice the return value is a pointer to the real data, so changes in + * the return value will affect the packet data + */ + igmpv3_group_record* getFirstGroupRecord() const; + + /** + * Get the group record that comes next to a given group record. If + * "groupRecord" is NULL then NULL will be returned. If "groupRecord" is the + * last group record or if it is out of layer bounds NULL will be returned + * also. Notice the return value is a pointer to the real data casted to + * igmpv3_group_record type (as opposed to a copy of the option data). So + * changes in the return value will affect the packet data + * @param[in] groupRecord The group record to start searching from + * @return The next group record or NULL if "groupRecord" is NULL, last or out + * of layer bounds + */ + igmpv3_group_record* + getNextGroupRecord(igmpv3_group_record* groupRecord) const; + + /** + * Add a new group record at a the end of the group record list. The + * igmpv3_report_header#numOfGroupRecords field will be incremented + * accordingly + * @param[in] recordType The type of the new group record + * @param[in] multicastAddress The multicast address of the new group record + * @param[in] sourceAddresses A vector containing all the source addresses of + * the new group record + * @return The method constructs a new group record, adds it to the end of the + * group record list of IGMPv3 report message and returns a pointer to the new + * message. If something went wrong in creating or adding the new group record + * a NULL value is returned and an appropriate error message is printed to log + */ + igmpv3_group_record* + addGroupRecord(uint8_t recordType, const IPv4Address& multicastAddress, + const std::vector& sourceAddresses); + + /** + * Add a new group record at a certain index of the group record list. The + * igmpv3_report_header#numOfGroupRecords field will be incremented + * accordingly + * @param[in] recordType The type of the new group record + * @param[in] multicastAddress The multicast address of the new group record + * @param[in] sourceAddresses A vector containing all the source addresses of + * the new group record + * @param[in] index The index to add the new group address at + * @return The method constructs a new group record, adds it to the IGMPv3 + * report message and returns a pointer to the new message. If something went + * wrong in creating or adding the new group record a NULL value is returned + * and an appropriate error message is printed to log + */ + igmpv3_group_record* + addGroupRecordAtIndex(uint8_t recordType, const IPv4Address& multicastAddress, + const std::vector& sourceAddresses, + int index); + + /** + * Remove a group record at a certain index. The + * igmpv3_report_header#numOfGroupRecords field will be decremented + * accordingly + * @param[in] index The index of the group record to be removed + * @return True if group record was removed successfully or false otherwise. + * If false is returned an appropriate error message will be printed to log + */ + bool removeGroupRecordAtIndex(int index); + + /** + * Remove all group records in the message. The + * igmpv3_report_header#numOfGroupRecords field will be set to 0 + * @return True if all group records were cleared successfully or false + * otherwise. If false is returned an appropriate error message will be + * printed to log + */ + bool removeAllGroupRecords(); + + // implement abstract methods + + /** + * Calculate the IGMP checksum + */ + void computeCalculateFields(); + + /** + * @return The message size in bytes which include the size of the basic + * header + the size of the group record list + */ + size_t getHeaderLen() const { return m_DataLen; } }; -} +} // namespace pcpp #endif // PACKETPP_IGMP_LAYER diff --git a/Packet++/header/LLCLayer.h b/Packet++/header/LLCLayer.h index 2f7b872248..7b1f1117f6 100644 --- a/Packet++/header/LLCLayer.h +++ b/Packet++/header/LLCLayer.h @@ -9,86 +9,87 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - /** - * @struct llc_header - * Logical Link Control (LLC) header - */ - #pragma pack(push, 1) - struct llc_header - { - /// Destination Service Access Point - uint8_t dsap, - /// Source Service Access Point - ssap, - /// Control Field - control; - }; - #pragma pack(pop) - - /** - * @class LLCLayer - * Represents Logical Link Control layer messages - */ - class LLCLayer : public Layer - { - public: - - /** - * A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data (will be casted to llc_header) - * @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 - */ - LLCLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) { m_Protocol = LLC; } - - /** - * A constructor that creates the LLC layer from provided values - * @param[in] dsap Destination Service Access Point - * @param[in] ssap Source Service Access Point - * @param[in] control Control Field - */ - LLCLayer(uint8_t dsap, uint8_t ssap, uint8_t control); - - /** - * Get a pointer to Logical Link Control (LLC) layer header - * @return Pointer to LLC header - */ - inline llc_header *getLlcHeader() const { return (llc_header*)m_Data; }; - - // overridden methods - - /// Parses the next layer. Currently only STP supported as next layer - void parseNextLayer(); - - /// Does nothing for this layer - void computeCalculateFields() {} - - /** - * @return Get the size of the LLC header - */ - size_t getHeaderLen() const { return sizeof(llc_header); } - - /** - * @return Returns the protocol info as readable string - */ - std::string toString() const; - - /** - * @return The OSI layer level of LLC (Data Link Layer). - */ - OsiModelLayer getOsiModelLayer() const { return OsiModelDataLinkLayer; } +namespace pcpp { +/** + * @struct llc_header + * Logical Link Control (LLC) header + */ +#pragma pack(push, 1) +struct llc_header { + /// Destination Service Access Point + uint8_t dsap, + /// Source Service Access Point + ssap, + /// Control Field + control; +}; +#pragma pack(pop) - /** - * A static method that validates the input data - * @param[in] data The pointer to the beginning of a byte stream of an LLC packet - * @param[in] dataLen The length of the byte stream - * @return True if the data is valid and can represent an LLC packet - */ - static bool isDataValid(const uint8_t *data, size_t dataLen); - }; +/** + * @class LLCLayer + * Represents Logical Link Control layer messages + */ +class LLCLayer : public Layer { + public: + /** + * A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data (will be casted to llc_header) + * @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 + */ + LLCLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : Layer(data, dataLen, prevLayer, packet) { + m_Protocol = LLC; + } + + /** + * A constructor that creates the LLC layer from provided values + * @param[in] dsap Destination Service Access Point + * @param[in] ssap Source Service Access Point + * @param[in] control Control Field + */ + LLCLayer(uint8_t dsap, uint8_t ssap, uint8_t control); + + /** + * Get a pointer to Logical Link Control (LLC) layer header + * @return Pointer to LLC header + */ + inline llc_header* getLlcHeader() const { return (llc_header*)m_Data; }; + + // overridden methods + + /// Parses the next layer. Currently only STP supported as next layer + void parseNextLayer(); + + /// Does nothing for this layer + void computeCalculateFields() {} + + /** + * @return Get the size of the LLC header + */ + size_t getHeaderLen() const { return sizeof(llc_header); } + + /** + * @return Returns the protocol info as readable string + */ + std::string toString() const; + + /** + * @return The OSI layer level of LLC (Data Link Layer). + */ + OsiModelLayer getOsiModelLayer() const { return OsiModelDataLinkLayer; } + + /** + * A static method that validates the input data + * @param[in] data The pointer to the beginning of a byte stream of an LLC + * packet + * @param[in] dataLen The length of the byte stream + * @return True if the data is valid and can represent an LLC packet + */ + static bool isDataValid(const uint8_t* data, size_t dataLen); +}; } // namespace pcpp diff --git a/Packet++/header/Layer.h b/Packet++/header/Layer.h index 967ae36a45..da36080781 100644 --- a/Packet++/header/Layer.h +++ b/Packet++/header/Layer.h @@ -1,9 +1,9 @@ #ifndef PACKETPP_LAYER #define PACKETPP_LAYER +#include "ProtocolType.h" #include #include -#include "ProtocolType.h" #include /// @file @@ -12,184 +12,206 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - - /** - * @class IDataContainer - * An interface (virtual abstract class) that indicates an object that holds a pointer to a buffer data. The Layer class is an example - * of such object, hence it inherits this interface - */ - class IDataContainer - { - public: - /** - * Get a pointer to the data - * @param[in] offset Get a pointer in a certain offset. Default is 0 - get a pointer to start of data - * @return A pointer to the data - */ - virtual uint8_t* getDataPtr(size_t offset = 0) const = 0; - - virtual ~IDataContainer() {} - }; - - class Packet; - - /** - * @class Layer - * Layer is the base class for all protocol layers. Each protocol supported in PcapPlusPlus has a class that inherits Layer. - * The protocol layer class expose all properties and methods relevant for viewing and editing protocol fields. - * For example: a pointer to a structured header (e.g tcphdr, iphdr, etc.), protocol header size, payload size, compute - * fields that can be automatically computed, print protocol data to string, etc. - * Each protocol instance is obviously part of a protocol stack (which construct a packet). This protocol stack is represented - * in PcapPlusPlus in a linked list, and each layer is an element in this list. That's why each layer has properties to the next and previous - * layer in the protocol stack - * The Layer class, as a base class, is abstract and the user can't create an instance of it (it has a private constructor) - * Each layer holds a pointer to the relevant place in the packet. The layer sees all the data from this pointer forward until the - * end of the packet. Here is an example packet showing this concept: - * - @verbatim - ==================================================== - |Eth |IPv4 |TCP |Packet | - |Header |Header |Header |Payload | - ==================================================== - - |--------------------------------------------------| - EthLayer data - |---------------------------------------| - IPv4Layer data - |---------------------------| - TcpLayer data - |----------------| - PayloadLayer data - @endverbatim - * - */ - class Layer : public IDataContainer - { - friend class Packet; - public: - /** - * A destructor for this class. Frees the data if it was allocated by the layer constructor (see isAllocatedToPacket() for more info) - */ - virtual ~Layer(); - - /** - * @return A pointer to the next layer in the protocol stack or NULL if the layer is the last one - */ - Layer* getNextLayer() const { return m_NextLayer; } - - /** - * @return A pointer to the previous layer in the protocol stack or NULL if the layer is the first one - */ - Layer* getPrevLayer() const { return m_PrevLayer; } - - /** - * @return The protocol enum - */ - ProtocolType getProtocol() const { return m_Protocol; } - - /** - * @return A pointer to the layer raw data. In most cases it'll be a pointer to the first byte of the header - */ - uint8_t* getData() const { return m_Data; } - - /** - * @return The length in bytes of the data from the first byte of the header until the end of the packet - */ - size_t getDataLen() const { return m_DataLen; } - - /** - * @return A pointer for the layer payload, meaning the first byte after the header - */ - uint8_t* getLayerPayload() const { return m_Data + getHeaderLen(); } - - /** - * @return The size in bytes of the payload - */ - size_t getLayerPayloadSize() const { return m_DataLen - getHeaderLen(); } - - /** - * Raw data in layers can come from one of sources: - * 1. from an existing packet - this is the case when parsing packets received from files or the network. In this case the data was - * already allocated by someone else, and layer only holds the pointer to the relevant place inside this data - * 2. when creating packets, data is allocated when layer is created. In this case the layer is responsible for freeing it as well - * - * @return Returns true if the data was allocated by an external source (a packet) or false if it was allocated by the layer itself - */ - bool isAllocatedToPacket() const { return m_Packet != NULL; } - - /** - * Copy the raw data of this layer to another array - * @param[out] toArr The destination byte array - */ - void copyData(uint8_t* toArr) const; - - // implement abstract methods - - uint8_t* getDataPtr(size_t offset = 0) const { return (uint8_t*)(m_Data + offset); } - - - // abstract methods - - /** - * Each layer is responsible for parsing the next layer - */ - virtual void parseNextLayer() = 0; - - /** - * @return The header length in bytes - */ - virtual size_t getHeaderLen() const = 0; - - /** - * Each layer can compute field values automatically using this method. This is an abstract method - */ - virtual void computeCalculateFields() = 0; - - /** - * @return A string representation of the layer most important data (should look like the layer description in Wireshark) - */ - virtual std::string toString() const = 0; - - /** - * @return The OSI Model layer this protocol belongs to - */ - virtual OsiModelLayer getOsiModelLayer() const = 0; - - protected: - uint8_t* m_Data; - size_t m_DataLen; - Packet* m_Packet; - ProtocolType m_Protocol; - Layer* m_NextLayer; - Layer* m_PrevLayer; - bool m_IsAllocatedInPacket; - - Layer() : m_Data(NULL), m_DataLen(0), m_Packet(NULL), m_Protocol(UnknownProtocol), m_NextLayer(NULL), m_PrevLayer(NULL), m_IsAllocatedInPacket(false) { } - - Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : - m_Data(data), m_DataLen(dataLen), - m_Packet(packet), m_Protocol(UnknownProtocol), - m_NextLayer(NULL), m_PrevLayer(prevLayer), m_IsAllocatedInPacket(false) {} - - // Copy c'tor - Layer(const Layer& other); - Layer& operator=(const Layer& other); - - void setNextLayer(Layer* nextLayer) { m_NextLayer = nextLayer; } - void setPrevLayer(Layer* prevLayer) { m_PrevLayer = prevLayer; } - - virtual bool extendLayer(int offsetInLayer, size_t numOfBytesToExtend); - virtual bool shortenLayer(int offsetInLayer, size_t numOfBytesToShorten); - }; +namespace pcpp { + +/** + * @class IDataContainer + * An interface (virtual abstract class) that indicates an object that holds a + * pointer to a buffer data. The Layer class is an example of such object, hence + * it inherits this interface + */ +class IDataContainer { + public: + /** + * Get a pointer to the data + * @param[in] offset Get a pointer in a certain offset. Default is 0 - get a + * pointer to start of data + * @return A pointer to the data + */ + virtual uint8_t* getDataPtr(size_t offset = 0) const = 0; + + virtual ~IDataContainer() {} +}; + +class Packet; + +/** + * @class Layer + * Layer is the base class for all protocol layers. Each protocol supported in + PcapPlusPlus has a class that inherits Layer. + * The protocol layer class expose all properties and methods relevant for + viewing and editing protocol fields. + * For example: a pointer to a structured header (e.g tcphdr, iphdr, etc.), + protocol header size, payload size, compute + * fields that can be automatically computed, print protocol data to string, + etc. + * Each protocol instance is obviously part of a protocol stack (which construct + a packet). This protocol stack is represented + * in PcapPlusPlus in a linked list, and each layer is an element in this list. + That's why each layer has properties to the next and previous + * layer in the protocol stack + * The Layer class, as a base class, is abstract and the user can't create an + instance of it (it has a private constructor) + * Each layer holds a pointer to the relevant place in the packet. The layer + sees all the data from this pointer forward until the + * end of the packet. Here is an example packet showing this concept: + * + @verbatim + ==================================================== + |Eth |IPv4 |TCP |Packet | + |Header |Header |Header |Payload | + ==================================================== + + |--------------------------------------------------| + EthLayer data + |---------------------------------------| + IPv4Layer data + |---------------------------| + TcpLayer data + |----------------| + PayloadLayer data + @endverbatim + * +*/ +class Layer : public IDataContainer { + friend class Packet; + + public: + /** + * A destructor for this class. Frees the data if it was allocated by the + * layer constructor (see isAllocatedToPacket() for more info) + */ + virtual ~Layer(); + + /** + * @return A pointer to the next layer in the protocol stack or NULL if the + * layer is the last one + */ + Layer* getNextLayer() const { return m_NextLayer; } + + /** + * @return A pointer to the previous layer in the protocol stack or NULL if + * the layer is the first one + */ + Layer* getPrevLayer() const { return m_PrevLayer; } + + /** + * @return The protocol enum + */ + ProtocolType getProtocol() const { return m_Protocol; } + + /** + * @return A pointer to the layer raw data. In most cases it'll be a pointer + * to the first byte of the header + */ + uint8_t* getData() const { return m_Data; } + + /** + * @return The length in bytes of the data from the first byte of the header + * until the end of the packet + */ + size_t getDataLen() const { return m_DataLen; } + + /** + * @return A pointer for the layer payload, meaning the first byte after the + * header + */ + uint8_t* getLayerPayload() const { return m_Data + getHeaderLen(); } + + /** + * @return The size in bytes of the payload + */ + size_t getLayerPayloadSize() const { return m_DataLen - getHeaderLen(); } + + /** + * Raw data in layers can come from one of sources: + * 1. from an existing packet - this is the case when parsing packets received + * from files or the network. In this case the data was already allocated by + * someone else, and layer only holds the pointer to the relevant place inside + * this data + * 2. when creating packets, data is allocated when layer is created. In this + * case the layer is responsible for freeing it as well + * + * @return Returns true if the data was allocated by an external source (a + * packet) or false if it was allocated by the layer itself + */ + bool isAllocatedToPacket() const { return m_Packet != NULL; } + + /** + * Copy the raw data of this layer to another array + * @param[out] toArr The destination byte array + */ + void copyData(uint8_t* toArr) const; + + // implement abstract methods + + uint8_t* getDataPtr(size_t offset = 0) const { + return (uint8_t*)(m_Data + offset); + } + + // abstract methods + + /** + * Each layer is responsible for parsing the next layer + */ + virtual void parseNextLayer() = 0; + + /** + * @return The header length in bytes + */ + virtual size_t getHeaderLen() const = 0; + + /** + * Each layer can compute field values automatically using this method. This + * is an abstract method + */ + virtual void computeCalculateFields() = 0; + + /** + * @return A string representation of the layer most important data (should + * look like the layer description in Wireshark) + */ + virtual std::string toString() const = 0; + + /** + * @return The OSI Model layer this protocol belongs to + */ + virtual OsiModelLayer getOsiModelLayer() const = 0; + + protected: + uint8_t* m_Data; + size_t m_DataLen; + Packet* m_Packet; + ProtocolType m_Protocol; + Layer* m_NextLayer; + Layer* m_PrevLayer; + bool m_IsAllocatedInPacket; + + Layer() + : m_Data(NULL), m_DataLen(0), m_Packet(NULL), m_Protocol(UnknownProtocol), + m_NextLayer(NULL), m_PrevLayer(NULL), m_IsAllocatedInPacket(false) {} + + Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : m_Data(data), m_DataLen(dataLen), m_Packet(packet), + m_Protocol(UnknownProtocol), m_NextLayer(NULL), m_PrevLayer(prevLayer), + m_IsAllocatedInPacket(false) {} + + // Copy c'tor + Layer(const Layer& other); + Layer& operator=(const Layer& other); + + void setNextLayer(Layer* nextLayer) { m_NextLayer = nextLayer; } + void setPrevLayer(Layer* prevLayer) { m_PrevLayer = prevLayer; } + + virtual bool extendLayer(int offsetInLayer, size_t numOfBytesToExtend); + virtual bool shortenLayer(int offsetInLayer, size_t numOfBytesToShorten); +}; } // namespace pcpp -inline std::ostream& operator<<(std::ostream& os, const pcpp::Layer &layer) -{ - os << layer.toString(); - return os; +inline std::ostream& operator<<(std::ostream& os, const pcpp::Layer& layer) { + os << layer.toString(); + return os; } #endif /* PACKETPP_LAYER */ diff --git a/Packet++/header/MplsLayer.h b/Packet++/header/MplsLayer.h index 004598bf0b..27800b1cbb 100644 --- a/Packet++/header/MplsLayer.h +++ b/Packet++/header/MplsLayer.h @@ -9,117 +9,125 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - - /** - * @class MplsLayer - * Represents a MPLS (Multi-Protocol Label Switching) layer - */ - class MplsLayer : public Layer - { - private: - - #pragma pack(push, 1) - struct mpls_header - { - uint16_t hiLabel; - uint8_t misc; - uint8_t ttl; - }; - #pragma pack(pop) - - mpls_header* getMplsHeader() const { return (mpls_header*)m_Data; } - - public: - /** 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 - */ - MplsLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) { m_Protocol = MPLS; } - - /** - * A constructor that allocates a new MPLS header - * @param[in] mplsLabel MPLS label - * @param[in] ttl Time-to-leave value - * @param[in] experimentalUseValue Experimental use value - * @param[in] bottomOfStack Bottom-of-stack value which indicate whether the next layer will also be a MPLS label or not - */ - MplsLayer(uint32_t mplsLabel, uint8_t ttl, uint8_t experimentalUseValue, bool bottomOfStack); - - virtual ~MplsLayer() {} - - /** - * @return TTL value of the MPLS header - */ - uint8_t getTTL() const { return getMplsHeader()->ttl; } - - /** - * Set the TTL value - * @param[in] ttl The TTL value to set - */ - void setTTL(uint8_t ttl) { getMplsHeader()->ttl = ttl; } - - /** - * Get an indication whether the next layer is also be a MPLS label or not - * @return True if it's the last MPLS layer, false otherwise - */ - bool isBottomOfStack() const; - - /** - * Set the bottom-of-stack bit in the MPLS label - * @param[in] val Set or unset the bit - */ - void setBottomOfStack(bool val); - - /** - * @return The exp value (3 bits) of the MPLS label - */ - uint8_t getExperimentalUseValue() const; - - /** - * Set the exp value (3 bits) of the MPLS label - * @param[in] val The exp value to set. val must be a valid number meaning between 0 and 7 (inclusive) - * @return True if exp value was set successfully or false if val has invalid value - */ - bool setExperimentalUseValue(uint8_t val); - - /** - * @return The MPLS label value (20 bits) - */ - uint32_t getMplsLabel() const; - - /** - * Set the MPLS label (20 bits) - * @param[in] label The label to set. label must be a valid number meaning between 0 and 0xFFFFF (inclusive) - * @return True if label was set successfully or false if label has invalid value - */ - bool setMplsLabel(uint32_t label); - - // implement abstract methods - - /** - * Currently identifies the following next layers: IPv4Layer, IPv6Layer, MplsLayer. Otherwise sets PayloadLayer - */ - void parseNextLayer(); - - /** - * @return Size of MPLS header (4 bytes) - */ - size_t getHeaderLen() const { return sizeof(mpls_header); } - - /** - * Set/unset the bottom-of-stack bit according to next layer: if it's a MPLS layer then bottom-of-stack will be unset. If it's not a - * MPLS layer this bit will be set - */ - void computeCalculateFields(); - - std::string toString() const; - - OsiModelLayer getOsiModelLayer() const { return OsiModelNetworkLayer; } - }; +namespace pcpp { + +/** + * @class MplsLayer + * Represents a MPLS (Multi-Protocol Label Switching) layer + */ +class MplsLayer : public Layer { + private: +#pragma pack(push, 1) + struct mpls_header { + uint16_t hiLabel; + uint8_t misc; + uint8_t ttl; + }; +#pragma pack(pop) + + mpls_header* getMplsHeader() const { return (mpls_header*)m_Data; } + + public: + /** 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 + */ + MplsLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : Layer(data, dataLen, prevLayer, packet) { + m_Protocol = MPLS; + } + + /** + * A constructor that allocates a new MPLS header + * @param[in] mplsLabel MPLS label + * @param[in] ttl Time-to-leave value + * @param[in] experimentalUseValue Experimental use value + * @param[in] bottomOfStack Bottom-of-stack value which indicate whether the + * next layer will also be a MPLS label or not + */ + MplsLayer(uint32_t mplsLabel, uint8_t ttl, uint8_t experimentalUseValue, + bool bottomOfStack); + + virtual ~MplsLayer() {} + + /** + * @return TTL value of the MPLS header + */ + uint8_t getTTL() const { return getMplsHeader()->ttl; } + + /** + * Set the TTL value + * @param[in] ttl The TTL value to set + */ + void setTTL(uint8_t ttl) { getMplsHeader()->ttl = ttl; } + + /** + * Get an indication whether the next layer is also be a MPLS label or not + * @return True if it's the last MPLS layer, false otherwise + */ + bool isBottomOfStack() const; + + /** + * Set the bottom-of-stack bit in the MPLS label + * @param[in] val Set or unset the bit + */ + void setBottomOfStack(bool val); + + /** + * @return The exp value (3 bits) of the MPLS label + */ + uint8_t getExperimentalUseValue() const; + + /** + * Set the exp value (3 bits) of the MPLS label + * @param[in] val The exp value to set. val must be a valid number meaning + * between 0 and 7 (inclusive) + * @return True if exp value was set successfully or false if val has invalid + * value + */ + bool setExperimentalUseValue(uint8_t val); + + /** + * @return The MPLS label value (20 bits) + */ + uint32_t getMplsLabel() const; + + /** + * Set the MPLS label (20 bits) + * @param[in] label The label to set. label must be a valid number meaning + * between 0 and 0xFFFFF (inclusive) + * @return True if label was set successfully or false if label has invalid + * value + */ + bool setMplsLabel(uint32_t label); + + // implement abstract methods + + /** + * Currently identifies the following next layers: IPv4Layer, IPv6Layer, + * MplsLayer. Otherwise sets PayloadLayer + */ + void parseNextLayer(); + + /** + * @return Size of MPLS header (4 bytes) + */ + size_t getHeaderLen() const { return sizeof(mpls_header); } + + /** + * Set/unset the bottom-of-stack bit according to next layer: if it's a MPLS + * layer then bottom-of-stack will be unset. If it's not a MPLS layer this bit + * will be set + */ + void computeCalculateFields(); + + std::string toString() const; + + OsiModelLayer getOsiModelLayer() const { return OsiModelNetworkLayer; } +}; } // namespace pcpp diff --git a/Packet++/header/NdpLayer.h b/Packet++/header/NdpLayer.h index 3f82a5dda4..93cf06cbeb 100644 --- a/Packet++/header/NdpLayer.h +++ b/Packet++/header/NdpLayer.h @@ -15,356 +15,386 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ +namespace pcpp { /** - * An enum representing the available option types for Neighbor Discovery in IPv6 (see RFC 4861) + * An enum representing the available option types for Neighbor Discovery in + * IPv6 (see RFC 4861) */ -enum class NDPNeighborOptionTypes : int -{ - NDP_OPTION_SOURCE_LINK_LAYER = 1, - NDP_OPTION_TARGET_LINK_LAYER = 2, - NDP_OPTION_PREFIX_INFORMATION = 3, - NDP_OPTION_REDIRECTED_HEADER = 4, - NDP_OPTION_MTU = 5, - NDP_OPTION_UNKNOWN = 255 +enum class NDPNeighborOptionTypes : int { + NDP_OPTION_SOURCE_LINK_LAYER = 1, + NDP_OPTION_TARGET_LINK_LAYER = 2, + NDP_OPTION_PREFIX_INFORMATION = 3, + NDP_OPTION_REDIRECTED_HEADER = 4, + NDP_OPTION_MTU = 5, + NDP_OPTION_UNKNOWN = 255 }; /** * @class NdpOption - * A wrapper class for NDP options. This class does not create or modify NDP option records, but rather - * serves as a wrapper and provides useful methods for retrieving data from them + * A wrapper class for NDP options. This class does not create or modify NDP + * option records, but rather serves as a wrapper and provides useful methods + * for retrieving data from them */ -class NdpOption : public TLVRecord -{ -public: - /** - * A c'tor for this class that gets a pointer to the option raw data (byte array) - * @param[in] optionRawData A pointer to the NDP option raw data - */ - explicit NdpOption(uint8_t *optionRawData) : TLVRecord(optionRawData) {} - - /** - * A d'tor for this class, currently does nothing - */ - ~NdpOption() {} - - /** - * @return NDP option type casted as pcpp::NDPNeighborOptionTypes enum. If the data is null a value - * of NDP_OPTION_UNKNOWN is returned - */ - NDPNeighborOptionTypes getNdpOptionType() const - { - if (m_Data == nullptr) - return NDPNeighborOptionTypes::NDP_OPTION_UNKNOWN; - - return static_cast(m_Data->recordType); - } - - // implement abstract methods - - size_t getTotalSize() const - { - if (m_Data == nullptr) - return (size_t)0; - - return (size_t)m_Data->recordLen * 8; - } - - size_t getDataSize() const - { - if (m_Data == nullptr) - return 0; - - return (size_t)m_Data->recordLen * 8 - (2 * sizeof(uint8_t)); // length value is stored in units of 8 octets - } +class NdpOption : public TLVRecord { + public: + /** + * A c'tor for this class that gets a pointer to the option raw data (byte + * array) + * @param[in] optionRawData A pointer to the NDP option raw data + */ + explicit NdpOption(uint8_t* optionRawData) : TLVRecord(optionRawData) {} + + /** + * A d'tor for this class, currently does nothing + */ + ~NdpOption() {} + + /** + * @return NDP option type casted as pcpp::NDPNeighborOptionTypes enum. If the + * data is null a value of NDP_OPTION_UNKNOWN is returned + */ + NDPNeighborOptionTypes getNdpOptionType() const { + if (m_Data == nullptr) + return NDPNeighborOptionTypes::NDP_OPTION_UNKNOWN; + + return static_cast(m_Data->recordType); + } + + // implement abstract methods + + size_t getTotalSize() const { + if (m_Data == nullptr) + return (size_t)0; + + return (size_t)m_Data->recordLen * 8; + } + + size_t getDataSize() const { + if (m_Data == nullptr) + return 0; + + return (size_t)m_Data->recordLen * 8 - + (2 * sizeof(uint8_t)); // length value is stored in units of 8 octets + } }; /** * @class NdpOptionBuilder - * A class for building NDP option records. This builder receives the NDP option parameters in its c'tor, - * builds the NDP option raw buffer and provides a build() method to get a NdpOption object out of it + * A class for building NDP option records. This builder receives the NDP option + * parameters in its c'tor, builds the NDP option raw buffer and provides a + * build() method to get a NdpOption object out of it */ -class NdpOptionBuilder : public TLVRecordBuilder -{ -public: - /** - * A c'tor for building NDP options which their value is a byte array. The NdpOption object can be later - * retrieved by calling build(). Each option is padded to have a 64-bit boundary. - * @param[in] optionType NDP option type - * @param[in] optionValue A buffer containing the option value. This buffer is read-only and isn't modified in any - * way. - * @param[in] optionValueLen Option value length in bytes - */ - NdpOptionBuilder(NDPNeighborOptionTypes optionType, const uint8_t *optionValue, uint8_t optionValueLen) - : TLVRecordBuilder((uint8_t)optionType, optionValue, optionValueLen) {} - - /** - * Build the NdpOption object out of the parameters defined in the c'tor. Padding bytes are added to the - * option for option length with 64-bit boundaries. - * @return The NdpOption object - */ - NdpOption build() const; +class NdpOptionBuilder : public TLVRecordBuilder { + public: + /** + * A c'tor for building NDP options which their value is a byte array. The + * NdpOption object can be later retrieved by calling build(). Each option is + * padded to have a 64-bit boundary. + * @param[in] optionType NDP option type + * @param[in] optionValue A buffer containing the option value. This buffer is + * read-only and isn't modified in any way. + * @param[in] optionValueLen Option value length in bytes + */ + NdpOptionBuilder(NDPNeighborOptionTypes optionType, + const uint8_t* optionValue, uint8_t optionValueLen) + : TLVRecordBuilder((uint8_t)optionType, optionValue, optionValueLen) {} + + /** + * Build the NdpOption object out of the parameters defined in the c'tor. + * Padding bytes are added to the option for option length with 64-bit + * boundaries. + * @return The NdpOption object + */ + NdpOption build() const; }; /** * @class NDPLayerBase * Represents a base for NDP packet types */ -class NDPLayerBase : public IcmpV6Layer -{ -public: - virtual ~NDPLayerBase() {} - - /** - * @return The number of NDP options in this layer - */ - size_t getNdpOptionCount() const; - - /** - * Get a NDP option by type. - * @param[in] option NDP option type - * @return An NdpOption object that contains the first option that matches this type, or logical NULL - * (NdpOption#isNull() == true) if no such option found - */ - NdpOption getNdpOption(NDPNeighborOptionTypes option) const; - - /** - * @return The first NDP option in the packet. If the current layer contains no options the returned value will - * contain a logical NULL (NdpOption#isNull() == true) - */ - NdpOption getFirstNdpOption() const; - - /** - * Get the NDP option that comes after a given option. If the given option was the last one, the - * returned value will contain a logical NULL (IdpOption#isNull() == true) - * @param[in] option An NDP option object that exists in the current layer - * @return A NdpOption object that contains the NDP option data that comes next, or logical NULL if the given - * NDP option: (1) was the last one; or (2) contains a logical NULL; or (3) doesn't belong to this packet - */ - NdpOption getNextNdpOption(NdpOption &option) const; - - /** - * Add a new NDP option at the end of the layer (after the last NDP option) - * @param[in] optionBuilder An NdpOptionBuilder object that contains the NDP option data to be added - * @return A NdpOption object that contains the newly added NDP option data or logical NULL - * (NdpOption#isNull() == true) if addition failed. In case of a failure a corresponding error message will be - * printed to log - */ - NdpOption addNdpOption(const NdpOptionBuilder &optionBuilder); - - /** - * Remove all NDP options from the layer - * @return True if options removed successfully or false if some error occurred (an appropriate error message will - * be printed to log) - */ - bool removeAllNdpOptions(); - -protected: - NDPLayerBase() = default; - - NDPLayerBase(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) - : IcmpV6Layer(data, dataLen, prevLayer, packet) {} - -private: - TLVRecordReader m_OptionReader; - - virtual size_t getNdpHeaderLen() const = 0; - virtual uint8_t *getNdpOptionsBasePtr() const { return m_Data + getNdpHeaderLen(); }; - NdpOption addNdpOptionAt(const NdpOptionBuilder &optionBuilder, int offset); +class NDPLayerBase : public IcmpV6Layer { + public: + virtual ~NDPLayerBase() {} + + /** + * @return The number of NDP options in this layer + */ + size_t getNdpOptionCount() const; + + /** + * Get a NDP option by type. + * @param[in] option NDP option type + * @return An NdpOption object that contains the first option that matches + * this type, or logical NULL (NdpOption#isNull() == true) if no such option + * found + */ + NdpOption getNdpOption(NDPNeighborOptionTypes option) const; + + /** + * @return The first NDP option in the packet. If the current layer contains + * no options the returned value will contain a logical NULL + * (NdpOption#isNull() == true) + */ + NdpOption getFirstNdpOption() const; + + /** + * Get the NDP option that comes after a given option. If the given option was + * the last one, the returned value will contain a logical NULL + * (IdpOption#isNull() == true) + * @param[in] option An NDP option object that exists in the current layer + * @return A NdpOption object that contains the NDP option data that comes + * next, or logical NULL if the given NDP option: (1) was the last one; or (2) + * contains a logical NULL; or (3) doesn't belong to this packet + */ + NdpOption getNextNdpOption(NdpOption& option) const; + + /** + * Add a new NDP option at the end of the layer (after the last NDP option) + * @param[in] optionBuilder An NdpOptionBuilder object that contains the NDP + * option data to be added + * @return A NdpOption object that contains the newly added NDP option data or + * logical NULL (NdpOption#isNull() == true) if addition failed. In case of a + * failure a corresponding error message will be printed to log + */ + NdpOption addNdpOption(const NdpOptionBuilder& optionBuilder); + + /** + * Remove all NDP options from the layer + * @return True if options removed successfully or false if some error + * occurred (an appropriate error message will be printed to log) + */ + bool removeAllNdpOptions(); + + protected: + NDPLayerBase() = default; + + NDPLayerBase(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : IcmpV6Layer(data, dataLen, prevLayer, packet) {} + + private: + TLVRecordReader m_OptionReader; + + virtual size_t getNdpHeaderLen() const = 0; + virtual uint8_t* getNdpOptionsBasePtr() const { + return m_Data + getNdpHeaderLen(); + }; + NdpOption addNdpOptionAt(const NdpOptionBuilder& optionBuilder, int offset); }; /** * @class NDPNeighborSolicitationLayer * Represents a NDP Neighbor Solicitation protocol layer */ -class NDPNeighborSolicitationLayer : public NDPLayerBase -{ -public: - /** - * @struct ndpneighborsolicitationhdr - * Represents neighbor solicitation message format - */ +class NDPNeighborSolicitationLayer : public NDPLayerBase { + public: + /** + * @struct ndpneighborsolicitationhdr + * Represents neighbor solicitation message format + */ #pragma pack(push, 1) - struct ndpneighborsolicitationhdr : icmpv6hdr - { - /** Reserved */ - uint32_t reserved; - /** Target address - Target address of solicitation message */ - uint8_t targetIP[16]; - }; + struct ndpneighborsolicitationhdr : icmpv6hdr { + /** Reserved */ + uint32_t reserved; + /** Target address - Target address of solicitation message */ + uint8_t targetIP[16]; + }; #pragma pack(pop) - /** - * 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 - */ - NDPNeighborSolicitationLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) - : NDPLayerBase(data, dataLen, prevLayer, packet) {} - - /** - * A constructor for a new NDPNeighborSolicitationLayer object - * @param[in] code Code field - * @param[in] targetIP Target IP address for which the solicitation shall be created - */ - NDPNeighborSolicitationLayer(uint8_t code, const IPv6Address &targetIP); - - /** - * A constructor for a new NDPNeighborSolicitationLayer object - * @param[in] code Code field - * @param[in] targetIP Target IP address for which the solicitation shall be created - * @param[in] srcMac Mac address which shall be put in the linklayer option - */ - NDPNeighborSolicitationLayer(uint8_t code, const IPv6Address &targetIP, const MacAddress &srcMac); - - virtual ~NDPNeighborSolicitationLayer() {} - - /** - * @return Get the IP address specified as the target IP address in the solicitation message - */ - IPv6Address getTargetIP() const { return IPv6Address(getNdpHeader()->targetIP); }; - - /** - * Checks if the layer has a link layer address option set - * @return true if link layer address option is available, false otherwise - */ - bool hasLinkLayerAddress() const; - - /** - * Get the Link Layer Address - * @return Mac address which is specified in the link layer address option - */ - MacAddress getLinkLayerAddress() const; - - std::string toString() const; - -private: - void initLayer(uint8_t code, const IPv6Address &targetIP); - ndpneighborsolicitationhdr *getNdpHeader() const { return (ndpneighborsolicitationhdr *)m_Data; } - size_t getNdpHeaderLen() const { return sizeof(ndpneighborsolicitationhdr); }; + /** + * 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 + */ + NDPNeighborSolicitationLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet) + : NDPLayerBase(data, dataLen, prevLayer, packet) {} + + /** + * A constructor for a new NDPNeighborSolicitationLayer object + * @param[in] code Code field + * @param[in] targetIP Target IP address for which the solicitation shall be + * created + */ + NDPNeighborSolicitationLayer(uint8_t code, const IPv6Address& targetIP); + + /** + * A constructor for a new NDPNeighborSolicitationLayer object + * @param[in] code Code field + * @param[in] targetIP Target IP address for which the solicitation shall be + * created + * @param[in] srcMac Mac address which shall be put in the linklayer option + */ + NDPNeighborSolicitationLayer(uint8_t code, const IPv6Address& targetIP, + const MacAddress& srcMac); + + virtual ~NDPNeighborSolicitationLayer() {} + + /** + * @return Get the IP address specified as the target IP address in the + * solicitation message + */ + IPv6Address getTargetIP() const { + return IPv6Address(getNdpHeader()->targetIP); + }; + + /** + * Checks if the layer has a link layer address option set + * @return true if link layer address option is available, false otherwise + */ + bool hasLinkLayerAddress() const; + + /** + * Get the Link Layer Address + * @return Mac address which is specified in the link layer address option + */ + MacAddress getLinkLayerAddress() const; + + std::string toString() const; + + private: + void initLayer(uint8_t code, const IPv6Address& targetIP); + ndpneighborsolicitationhdr* getNdpHeader() const { + return (ndpneighborsolicitationhdr*)m_Data; + } + size_t getNdpHeaderLen() const { return sizeof(ndpneighborsolicitationhdr); }; }; /** * @class NDPNeighborAdvertisementLayer * Represents a NDP Neighbor Advertisement protocol layer */ -class NDPNeighborAdvertisementLayer : public NDPLayerBase -{ -public: - /** - * @struct ndpneighboradvertisementhdr - * Represents neighbor advertisement message format - */ +class NDPNeighborAdvertisementLayer : public NDPLayerBase { + public: + /** + * @struct ndpneighboradvertisementhdr + * Represents neighbor advertisement message format + */ #pragma pack(push, 1) - struct ndpneighboradvertisementhdr : icmpv6hdr - { + struct ndpneighboradvertisementhdr : icmpv6hdr { #if (BYTE_ORDER == LITTLE_ENDIAN) - uint32_t - /** Unused field */ - reserved : 5, - /** Flag indicating that this entry should override the old one */ - override : 1, - /** Flag indicating that the advertisement was sent in response to a Neighbor Solicitation from the - Destination address */ - solicited : 1, - /** Flag indicating that the advertisement is sent by a router */ - router : 1, - /** Unused field */ - reserved2 : 24; + uint32_t + /** Unused field */ + reserved : 5, + /** Flag indicating that this entry should override the old one */ + override : 1, + /** Flag indicating that the advertisement was sent in response to a + Neighbor Solicitation from the Destination address */ + solicited : 1, + /** Flag indicating that the advertisement is sent by a router */ + router : 1, + /** Unused field */ + reserved2 : 24; #else - uint32_t - /** Flag indicating that the advertisement is sent by a router */ - router : 1, - /** Flag indicating that the advertisement was sent in response to a Neighbor Solicitation from the - Destination address */ - solicited : 1, - /** Flag indicating that this entry should override the old one */ - override : 1, - /** Unused field */ - reserved : 29; + uint32_t + /** Flag indicating that the advertisement is sent by a router */ + router : 1, + /** Flag indicating that the advertisement was sent in response to a + Neighbor Solicitation from the Destination address */ + solicited : 1, + /** Flag indicating that this entry should override the old one */ + override : 1, + /** Unused field */ + reserved : 29; #endif - /** Target address - Either source address of advertisement or address for requested MAC */ - uint8_t targetIP[16]; - }; + /** Target address - Either source address of advertisement or address for + * requested MAC */ + uint8_t targetIP[16]; + }; #pragma pack(pop) - /** - * 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 - */ - NDPNeighborAdvertisementLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) - : NDPLayerBase(data, dataLen, prevLayer, packet) {} - - /** - * A constructor that allocates a new NDP Advertisement Layer with target link-layer address option - * @param[in] code Code field - * @param[in] targetIP The target IP address from the Neighbor Solicitation message (solicited advertisements) or - * the address whose link-layer address has changed (unsolicited advertisement) - * @param[in] targetMac Adds the target link-layer address into the option field of the layer - * @param[in] routerFlag The router flag - * @param[in] unicastFlag The solicited flag - * @param[in] overrideFlag The override flag - */ - NDPNeighborAdvertisementLayer(uint8_t code, const IPv6Address &targetIP, const MacAddress &targetMac, - bool routerFlag, bool unicastFlag, bool overrideFlag); - - /** - * A constructor that allocates a new NDP Advertisement Layer - * @param code Code field - * @param targetIP The target IP address from the Neighbor Solicitation message (solicited advertisements) or the - * address whose link-layer address has changed (unsolicited advertisement) - * @param routerFlag The router flag - * @param unicastFlag The solicited flag - * @param overrideFlag The override flag - */ - NDPNeighborAdvertisementLayer(uint8_t code, const IPv6Address &targetIP, bool routerFlag, bool unicastFlag, - bool overrideFlag); - - virtual ~NDPNeighborAdvertisementLayer() {} - - /** - * @return Get the target MAC address - */ - MacAddress getTargetMac() const; - - /** - * @return Get the target IP address - */ - IPv6Address getTargetIP() const { return IPv6Address(getNdpHeader()->targetIP); } - - /** - * @return Get information if the target link-layer address was added in the option field of the header - */ - bool hasTargetMacInfo() const; - - /** - * @return Get the router flag - */ - bool getRouterFlag() const { return getNdpHeader()->router; } - - /** - * @return Get the unicast flag - */ - bool getUnicastFlag() const { return getNdpHeader()->solicited; } - - /** - * @return Get the override flag - */ - bool getOverrideFlag() const { return getNdpHeader()->override; } - - std::string toString() const; - -private: - void initLayer(uint8_t code, const IPv6Address &targetIP, bool routerFlag, bool unicastFlag, bool overrideFlag); - ndpneighboradvertisementhdr *getNdpHeader() const { return (ndpneighboradvertisementhdr *)m_Data; } - size_t getNdpHeaderLen() const { return sizeof(ndpneighboradvertisementhdr); }; + /** + * 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 + */ + NDPNeighborAdvertisementLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet) + : NDPLayerBase(data, dataLen, prevLayer, packet) {} + + /** + * A constructor that allocates a new NDP Advertisement Layer with target + * link-layer address option + * @param[in] code Code field + * @param[in] targetIP The target IP address from the Neighbor Solicitation + * message (solicited advertisements) or the address whose link-layer address + * has changed (unsolicited advertisement) + * @param[in] targetMac Adds the target link-layer address into the option + * field of the layer + * @param[in] routerFlag The router flag + * @param[in] unicastFlag The solicited flag + * @param[in] overrideFlag The override flag + */ + NDPNeighborAdvertisementLayer(uint8_t code, const IPv6Address& targetIP, + const MacAddress& targetMac, bool routerFlag, + bool unicastFlag, bool overrideFlag); + + /** + * A constructor that allocates a new NDP Advertisement Layer + * @param code Code field + * @param targetIP The target IP address from the Neighbor Solicitation + * message (solicited advertisements) or the address whose link-layer address + * has changed (unsolicited advertisement) + * @param routerFlag The router flag + * @param unicastFlag The solicited flag + * @param overrideFlag The override flag + */ + NDPNeighborAdvertisementLayer(uint8_t code, const IPv6Address& targetIP, + bool routerFlag, bool unicastFlag, + bool overrideFlag); + + virtual ~NDPNeighborAdvertisementLayer() {} + + /** + * @return Get the target MAC address + */ + MacAddress getTargetMac() const; + + /** + * @return Get the target IP address + */ + IPv6Address getTargetIP() const { + return IPv6Address(getNdpHeader()->targetIP); + } + + /** + * @return Get information if the target link-layer address was added in the + * option field of the header + */ + bool hasTargetMacInfo() const; + + /** + * @return Get the router flag + */ + bool getRouterFlag() const { return getNdpHeader()->router; } + + /** + * @return Get the unicast flag + */ + bool getUnicastFlag() const { return getNdpHeader()->solicited; } + + /** + * @return Get the override flag + */ + bool getOverrideFlag() const { return getNdpHeader()->override; } + + std::string toString() const; + + private: + void initLayer(uint8_t code, const IPv6Address& targetIP, bool routerFlag, + bool unicastFlag, bool overrideFlag); + ndpneighboradvertisementhdr* getNdpHeader() const { + return (ndpneighboradvertisementhdr*)m_Data; + } + size_t getNdpHeaderLen() const { + return sizeof(ndpneighboradvertisementhdr); + }; }; } // namespace pcpp diff --git a/Packet++/header/NflogLayer.h b/Packet++/header/NflogLayer.h index c93d3064d1..ac4b950282 100644 --- a/Packet++/header/NflogLayer.h +++ b/Packet++/header/NflogLayer.h @@ -1,9 +1,9 @@ #ifndef PACKETPP_NFLOG_LAYER #define PACKETPP_NFLOG_LAYER +#include "GeneralUtils.h" #include "Layer.h" #include "TLVData.h" -#include "GeneralUtils.h" /// @file @@ -11,231 +11,230 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - /** - * @struct nflog_header - * Represents Nflog header - */ +namespace pcpp { +/** + * @struct nflog_header + * Represents Nflog header + */ #pragma pack(push, 1) - struct nflog_header - { - /** A Linux AF_ value, so it's 2 for IPv4 and 10 for IPv6 */ - uint8_t addressFamily; - /** The version field is 0 for the current version of the pseudo-header */ - uint8_t version; - /** The network byte order (big-endian) */ - uint16_t resourceId; - }; +struct nflog_header { + /** A Linux AF_ value, so it's 2 for IPv4 and 10 for IPv6 */ + uint8_t addressFamily; + /** The version field is 0 for the current version of the pseudo-header */ + uint8_t version; + /** The network byte order (big-endian) */ + uint16_t resourceId; +}; #pragma pack(pop) - /** - * @enum NflogTlvType - * Represents TLV types of NFLOG packets - */ - enum class NflogTlvType - { - /** the packet header structure */ - NFULA_PACKET_HDR = 1, - /** packet mark from skbuff */ - NFULA_MARK = 2, - /** nflog_timestamp_t for skbuff's time stamp */ - NFULA_TIMESTAMP = 3, - /** ifindex of device on which packet received (possibly bridge group) */ - NFULA_IFINDEX_INDEV = 4, - /** ifindex of device on which packet transmitted (possibly bridge group) */ - NFULA_IFINDEX_OUTDEV = 5, - /** ifindex of physical device on which packet received (not bridge group) */ - NFULA_IFINDEX_PHYSINDEV = 6, - /** ifindex of physical device on which packet transmitted (not bridge group) */ - NFULA_IFINDEX_PHYSOUTDEV = 7, - /** nflog_hwaddr_t for hardware address */ - NFULA_HWADDR = 8, - /** packet payload */ - NFULA_PAYLOAD = 9, - /** text string - null-terminated, count includes NUL */ - NFULA_PREFIX = 10, - /** UID owning socket on which packet was sent/received */ - NFULA_UID = 11, - /** sequence number of packets on this NFLOG socket */ - NFULA_SEQ = 12, - /** sequence number of packets on all NFLOG sockets */ - NFULA_SEQ_GLOBAL = 13, - /** GID owning socket on which packet was sent/received */ - NFULA_GID = 14, - /** ARPHRD_ type of skbuff's device */ - NFULA_HWTYPE = 15, - /** skbuff's MAC-layer header */ - NFULA_HWHEADER = 16, - /** length of skbuff's MAC-layer header */ - NFULA_HWLEN = 17, - }; - - /** - * @class NflogTlv - * A wrapper class for NFLOG TLV fields. This class does not create or modify TLVs related to NFLOG, but rather - * serves as a wrapper and provides useful methods for setting and retrieving data to/from them - */ - class NflogTlv - { - private: - struct NflogTLVRawData - { - /** Record length in bytes */ - uint16_t recordLen; - /** Record type */ - uint16_t recordType; - /** Record value (variable size) */ - uint8_t recordValue[]; - }; - NflogTLVRawData* m_Data; - public: - /** - * A c'tor for this class that gets a pointer to the option raw data (byte array) - * @param[in] recordRawData A pointer to the option raw data - */ - explicit NflogTlv(uint8_t* recordRawData) - { - assign(recordRawData); - } - - /** - * @return recordLen attribute in NflogTLVRawData - */ - size_t getTotalSize() const - { - // as in https://github.com/the-tcpdump-group/libpcap/blob/766b607d60d8038087b49fc4cf433dac3dcdb49c/pcap-util.c#L371-L374 - return align<4>(m_Data->recordLen); - } - - /** - * Assign a pointer to the TLV record raw data (byte array) - * @param[in] recordRawData A pointer to the TLV record raw data - */ - void assign(uint8_t* recordRawData) - { - m_Data = (NflogTLVRawData*)recordRawData; - } - - /** - * Check if a pointer can be assigned to the TLV record data - * @param[in] recordRawData A pointer to the TLV record raw data - * @param[in] tlvDataLen The size of the TLV record raw data - * * @return True if data is valid and can be assigned - */ - static bool canAssign(const uint8_t* recordRawData, size_t tlvDataLen) - { - return recordRawData != nullptr && tlvDataLen >= sizeof(NflogTLVRawData::recordLen); - } - - /** - * @return True if the TLV record raw data is nullptr, false otherwise - */ - bool isNull() const - { - return (m_Data == nullptr); - } - - /** - * @return The type field of the record (the 'T' in __Type__-Length-Value) - */ - uint16_t getType() const { return m_Data->recordType; } - - /** - * @return A pointer to the TLV record raw data byte stream - */ - uint8_t* getRecordBasePtr() const { return (uint8_t*)m_Data; } - - /** - * @return A pointer to the value of the record as byte array (the 'V' in Type-Length- __Value__) - */ - uint8_t* getValue() const { return m_Data->recordValue; } - }; - - /** - * @class NflogLayer - * Represents an NFLOG protocol layer - */ - class NflogLayer : public Layer - { - public: - /** - * A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data (will be casted to ether_header) - * @param[in] dataLen Size of the data in bytes - * @param[in] packet A pointer to the Packet instance where layer will be stored in - */ - NflogLayer(uint8_t* data, size_t dataLen, Packet* packet) : Layer(data, dataLen, NULL, packet) { m_Protocol = NFLOG; } - - ~NflogLayer() {} - - /** - * Get a pointer to the Nflog header. - * @return A pointer to the nflog_header - */ - nflog_header* getNflogHeader() const { return (nflog_header*)m_Data; } - - /** - * Get address family of the packet. e.g. 2 for ipv4 and 10 for ipv6 - * @return an unsigned char of address family - */ - uint8_t getFamily(); - - /** - * Get Version number inside packet header - * The version field is 0 for the current version of the pseudo-header - * @return an unsigned char for version - */ - uint8_t getVersion(); - - /** - * Get Resource Id in packet header - * On one netlink socket it's possible to listen to several nflog groups; the resource ID is the nflog group for the packet - */ - uint16_t getResourceId(); - - /** - * Get a TLV object found with the input type. if no tlv is found, the internal value of the object will set to nullptr - * @param[in] type type of tlv by using enum class defined as NflogTlvType - * @return NflogTlv obtained by type - */ - NflogTlv getTlvByType(NflogTlvType type) const; - - // implement abstract methods - - /** - * Currently identifies the following next layers: IPv4Layer, IPv6Layer using address family - * Otherwise sets PayloadLayer - */ - void parseNextLayer(); - - /** - * @return Size of nflog_header - */ - size_t getHeaderLen() const; - - /** - * Does nothing for this layer - */ - void computeCalculateFields() {}; - - std::string toString() const; - - OsiModelLayer getOsiModelLayer() const { return OsiModelDataLinkLayer; } - - /** - * A static method that validates the input data - * @param[in] data The pointer to the beginning of a byte stream of an NFLOG packet - * @param[in] dataLen The length of the byte stream - * @return True if the data is valid and can represent an NFLOG packet - */ - static bool isDataValid(const uint8_t* data, size_t dataLen); - - private: - uint8_t* getTlvsBasePtr() const { return m_Data + sizeof(nflog_header); } - - TLVRecordReader m_TlvReader; - }; +/** + * @enum NflogTlvType + * Represents TLV types of NFLOG packets + */ +enum class NflogTlvType { + /** the packet header structure */ + NFULA_PACKET_HDR = 1, + /** packet mark from skbuff */ + NFULA_MARK = 2, + /** nflog_timestamp_t for skbuff's time stamp */ + NFULA_TIMESTAMP = 3, + /** ifindex of device on which packet received (possibly bridge group) */ + NFULA_IFINDEX_INDEV = 4, + /** ifindex of device on which packet transmitted (possibly bridge group) */ + NFULA_IFINDEX_OUTDEV = 5, + /** ifindex of physical device on which packet received (not bridge group) */ + NFULA_IFINDEX_PHYSINDEV = 6, + /** ifindex of physical device on which packet transmitted (not bridge group) + */ + NFULA_IFINDEX_PHYSOUTDEV = 7, + /** nflog_hwaddr_t for hardware address */ + NFULA_HWADDR = 8, + /** packet payload */ + NFULA_PAYLOAD = 9, + /** text string - null-terminated, count includes NUL */ + NFULA_PREFIX = 10, + /** UID owning socket on which packet was sent/received */ + NFULA_UID = 11, + /** sequence number of packets on this NFLOG socket */ + NFULA_SEQ = 12, + /** sequence number of packets on all NFLOG sockets */ + NFULA_SEQ_GLOBAL = 13, + /** GID owning socket on which packet was sent/received */ + NFULA_GID = 14, + /** ARPHRD_ type of skbuff's device */ + NFULA_HWTYPE = 15, + /** skbuff's MAC-layer header */ + NFULA_HWHEADER = 16, + /** length of skbuff's MAC-layer header */ + NFULA_HWLEN = 17, +}; + +/** + * @class NflogTlv + * A wrapper class for NFLOG TLV fields. This class does not create or modify + * TLVs related to NFLOG, but rather serves as a wrapper and provides useful + * methods for setting and retrieving data to/from them + */ +class NflogTlv { + private: + struct NflogTLVRawData { + /** Record length in bytes */ + uint16_t recordLen; + /** Record type */ + uint16_t recordType; + /** Record value (variable size) */ + uint8_t recordValue[]; + }; + NflogTLVRawData* m_Data; + + public: + /** + * A c'tor for this class that gets a pointer to the option raw data (byte + * array) + * @param[in] recordRawData A pointer to the option raw data + */ + explicit NflogTlv(uint8_t* recordRawData) { assign(recordRawData); } + + /** + * @return recordLen attribute in NflogTLVRawData + */ + size_t getTotalSize() const { + // as in + // https://github.com/the-tcpdump-group/libpcap/blob/766b607d60d8038087b49fc4cf433dac3dcdb49c/pcap-util.c#L371-L374 + return align<4>(m_Data->recordLen); + } + + /** + * Assign a pointer to the TLV record raw data (byte array) + * @param[in] recordRawData A pointer to the TLV record raw data + */ + void assign(uint8_t* recordRawData) { + m_Data = (NflogTLVRawData*)recordRawData; + } + + /** + * Check if a pointer can be assigned to the TLV record data + * @param[in] recordRawData A pointer to the TLV record raw data + * @param[in] tlvDataLen The size of the TLV record raw data + * * @return True if data is valid and can be assigned + */ + static bool canAssign(const uint8_t* recordRawData, size_t tlvDataLen) { + return recordRawData != nullptr && + tlvDataLen >= sizeof(NflogTLVRawData::recordLen); + } + + /** + * @return True if the TLV record raw data is nullptr, false otherwise + */ + bool isNull() const { return (m_Data == nullptr); } + + /** + * @return The type field of the record (the 'T' in __Type__-Length-Value) + */ + uint16_t getType() const { return m_Data->recordType; } + + /** + * @return A pointer to the TLV record raw data byte stream + */ + uint8_t* getRecordBasePtr() const { return (uint8_t*)m_Data; } + + /** + * @return A pointer to the value of the record as byte array (the 'V' in + * Type-Length- __Value__) + */ + uint8_t* getValue() const { return m_Data->recordValue; } +}; + +/** + * @class NflogLayer + * Represents an NFLOG protocol layer + */ +class NflogLayer : public Layer { + public: + /** + * A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data (will be casted to ether_header) + * @param[in] dataLen Size of the data in bytes + * @param[in] packet A pointer to the Packet instance where layer will be + * stored in + */ + NflogLayer(uint8_t* data, size_t dataLen, Packet* packet) + : Layer(data, dataLen, NULL, packet) { + m_Protocol = NFLOG; + } + + ~NflogLayer() {} + + /** + * Get a pointer to the Nflog header. + * @return A pointer to the nflog_header + */ + nflog_header* getNflogHeader() const { return (nflog_header*)m_Data; } + + /** + * Get address family of the packet. e.g. 2 for ipv4 and 10 for ipv6 + * @return an unsigned char of address family + */ + uint8_t getFamily(); + + /** + * Get Version number inside packet header + * The version field is 0 for the current version of the pseudo-header + * @return an unsigned char for version + */ + uint8_t getVersion(); + + /** + * Get Resource Id in packet header + * On one netlink socket it's possible to listen to several nflog groups; the + * resource ID is the nflog group for the packet + */ + uint16_t getResourceId(); + + /** + * Get a TLV object found with the input type. if no tlv is found, the + * internal value of the object will set to nullptr + * @param[in] type type of tlv by using enum class defined as NflogTlvType + * @return NflogTlv obtained by type + */ + NflogTlv getTlvByType(NflogTlvType type) const; + + // implement abstract methods + + /** + * Currently identifies the following next layers: IPv4Layer, IPv6Layer using + * address family Otherwise sets PayloadLayer + */ + void parseNextLayer(); + + /** + * @return Size of nflog_header + */ + size_t getHeaderLen() const; + + /** + * Does nothing for this layer + */ + void computeCalculateFields(){}; + + std::string toString() const; + + OsiModelLayer getOsiModelLayer() const { return OsiModelDataLinkLayer; } + + /** + * A static method that validates the input data + * @param[in] data The pointer to the beginning of a byte stream of an NFLOG + * packet + * @param[in] dataLen The length of the byte stream + * @return True if the data is valid and can represent an NFLOG packet + */ + static bool isDataValid(const uint8_t* data, size_t dataLen); + + private: + uint8_t* getTlvsBasePtr() const { return m_Data + sizeof(nflog_header); } + + TLVRecordReader m_TlvReader; +}; } // namespace pcpp diff --git a/Packet++/header/NtpLayer.h b/Packet++/header/NtpLayer.h index 49ab0c86c1..71cf97afce 100644 --- a/Packet++/header/NtpLayer.h +++ b/Packet++/header/NtpLayer.h @@ -1,9 +1,9 @@ #ifndef PACKETPP_NTP_LAYER #define PACKETPP_NTP_LAYER -#include "Logger.h" -#include "Layer.h" #include "IpAddress.h" +#include "Layer.h" +#include "Logger.h" /// @file @@ -11,667 +11,699 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - /** - * @class NtpLayer - * Represents a NTP (Network Time Protocol) layer - * - * @brief The NTP packet consists of an integral number of 32-bit (4 octet) words in network byte order. - * The packet format consists of three components: the header itself, one or more optional extension fields (for v4), - * and an optional message authentication code (MAC). Currently the extension fields are not supported. The NTP header is: - * - * @verbatim - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |LI | VN |Mode | Stratum | Poll | Precision | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Root Delay | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Root Dispersion | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Reference ID | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | | - + Reference Timestamp (64) + - | | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | | - + Origin Timestamp (64) + - | | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | | - + Receive Timestamp (64) + - | | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | | - + Transmit Timestamp (64) + - | | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | | - . . - . Extension Field 1 (variable, only v4) . - . . - | | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | | - . . - . Extension Field 1 (variable, only v4) . - . . - | | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Key Identifier | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | | - | dgst (128 for v4, 64 for v3) | - | | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - @endverbatim - * - */ - class NtpLayer : public Layer - { - private: +namespace pcpp { +/** + * @class NtpLayer + * Represents a NTP (Network Time Protocol) layer + * + * @brief The NTP packet consists of an integral number of 32-bit (4 octet) + words in network byte order. + * The packet format consists of three components: the header itself, one or + more optional extension fields (for v4), + * and an optional message authentication code (MAC). Currently the extension + fields are not supported. The NTP header is: + * + * @verbatim + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |LI | VN |Mode | Stratum | Poll | Precision | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Root Delay | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Root Dispersion | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Reference ID | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + + Reference Timestamp (64) + + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + + Origin Timestamp (64) + + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + + Receive Timestamp (64) + + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + + Transmit Timestamp (64) + + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + . . + . Extension Field 1 (variable, only v4) . + . . + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + . . + . Extension Field 1 (variable, only v4) . + . . + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Key Identifier | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + | dgst (128 for v4, 64 for v3) | + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + @endverbatim + * + */ +class NtpLayer : public Layer { + private: #pragma pack(push, 1) - struct ntp_header - { + struct ntp_header { #if (BYTE_ORDER == LITTLE_ENDIAN) - /// 3-bit integer representing the mode - uint8_t mode:3, + /// 3-bit integer representing the mode + uint8_t mode : 3, /// 3-bit integer representing the NTP version number - version:3, - /// LI Leap Indicator (leap): 2-bit integer warning of an impending leap second to be inserted or deleted in the last minute of the current month - leapIndicator:2; + version : 3, + /// LI Leap Indicator (leap): 2-bit integer warning of an impending leap + /// second to be inserted or deleted in the last minute of the current + /// month + leapIndicator : 2; #else - /// LI Leap Indicator (leap): 2-bit integer warning of an impending leap second to be inserted or deleted in the last minute of the current month - uint8_t leapIndicator:2, + /// LI Leap Indicator (leap): 2-bit integer warning of an impending leap + /// second to be inserted or deleted in the last minute of the current month + uint8_t leapIndicator : 2, /// 3-bit integer representing the NTP version number - version:3, + version : 3, /// 3-bit integer representing the mode - mode:3; + mode : 3; #endif - /// 8-bit integer representing the stratum - uint8_t stratum; - /// Total round-trip delay to the reference clock, in log2 seconds. - int8_t pollInterval, - /// 8-bit signed integer representing the precision of the system clock, in log2 seconds. + /// 8-bit integer representing the stratum + uint8_t stratum; + /// Total round-trip delay to the reference clock, in log2 seconds. + int8_t pollInterval, + /// 8-bit signed integer representing the precision of the system clock, + /// in log2 seconds. precision; - /// Total round-trip delay to the reference clock, in NTP short format. - uint32_t rootDelay, + /// Total round-trip delay to the reference clock, in NTP short format. + uint32_t rootDelay, /// Total dispersion to the reference clock, in NTP short format. rootDispersion, - /// 32-bit code identifying the particular server or reference clock. The interpretation depends on the value in the stratum field. + /// 32-bit code identifying the particular server or reference clock. + /// The interpretation depends on the value in the stratum field. referenceIdentifier; - /// Time when the system clock was last set or corrected, in NTP timestamp format. - uint64_t referenceTimestamp, - /// Time at the client when the request departed for the server, in NTP timestamp format. + /// Time when the system clock was last set or corrected, in NTP timestamp + /// format. + uint64_t referenceTimestamp, + /// Time at the client when the request departed for the server, in NTP + /// timestamp format. originTimestamp, - /// Time at the client when the request departed for the server, in NTP timestamp format. + /// Time at the client when the request departed for the server, in NTP + /// timestamp format. receiveTimestamp, - /// Time at the server when the response left for the client, in NTP timestamp format. + /// Time at the server when the response left for the client, in NTP + /// timestamp format. transmitTimestamp; - }; + }; #pragma pack(pop) #pragma pack(push, 1) - struct ntp_v3_auth - { - /// An integer identifying the cryptographic key used to generate the message-authentication code - uint32_t keyID; - /// This is an integer identifying the cryptographic key used to generate the message-authentication code. - uint8_t dgst[8]; // 64 bit DES based - }; + struct ntp_v3_auth { + /// An integer identifying the cryptographic key used to generate the + /// message-authentication code + uint32_t keyID; + /// This is an integer identifying the cryptographic key used to generate + /// the message-authentication code. + uint8_t dgst[8]; // 64 bit DES based + }; #pragma pack(pop) #pragma pack(push, 1) - struct ntp_v4_auth_md5 - { - /// 32-bit unsigned integer used by the client and server to designate a secret 128-bit MD5 key. - uint32_t keyID; - /// 128-bit MD5 hash - uint8_t dgst[16]; - }; + struct ntp_v4_auth_md5 { + /// 32-bit unsigned integer used by the client and server to designate a + /// secret 128-bit MD5 key. + uint32_t keyID; + /// 128-bit MD5 hash + uint8_t dgst[16]; + }; #pragma pack(pop) #pragma pack(push, 1) - struct ntp_v4_auth_sha1 - { - /// 32-bit unsigned integer used by the client and server to designate a secret 160-bit SHA1 key. - uint32_t keyID; - /// 160-bit SHA1 hash - uint8_t dgst[20]; - }; + struct ntp_v4_auth_sha1 { + /// 32-bit unsigned integer used by the client and server to designate a + /// secret 160-bit SHA1 key. + uint32_t keyID; + /// 160-bit SHA1 hash + uint8_t dgst[20]; + }; #pragma pack(pop) - ntp_header *getNtpHeader() const { return (ntp_header *)m_Data; } - - public: - /** - * Warning of an impending leap second to be inserted or deleted in the last minute of the current month - */ - enum LeapIndicator - { - /// Normal, no leap second - NoWarning = 0, - /// Last minute of the day has 61 seconds - Last61Secs, - /// Last minute of the day has 59 seconds - Last59Secs, - /// Unknown (clock unsynchronized) - Unknown - }; - - /** - * Representing the NTP association modes - */ - enum Mode - { - /// Reserved variable - Reserved = 0, - /// Symmetrically active - SymActive, - /// Symmetrically passive - SymPassive, - /// Client mode - Client, - /// Server mode - Server, - /// Broadcasting mode - Broadcast, - /// NTP control messages - Control, - /// Reserved for private use - PrivateUse - }; - - /** - * 32-bit code identifying the particular server or reference clock. - * The interpretation depends on the value in the stratum field. - */ - enum class ClockSource : uint32_t - { - // NTPv4 - - /// Geosynchronous Orbit Environment Satellite - GOES = ('G') | ('O' << 8) | ('E' << 16) | ('S' << 24), - /// Global Position System - GPS = ('G') | ('P' << 8) | ('S' << 16), - /// Galileo Positioning System - GAL = ('G') | ('A' << 8) | ('L' << 16), - /// Generic pulse-per-second - PPS = ('P') | ('P' << 8) | ('S' << 16), - /// Inter-Range Instrumentation Group - IRIG = ('I') | ('R' << 8) | ('I' << 16) | ('G' << 24), - /// LF Radio WWVB Ft. Collins, CO 60 kHz - WWVB = ('W') | ('W' << 8) | ('V' << 16) | ('B' << 24), - /// LF Radio DCF77 Mainflingen, DE 77.5 kHz - DCF = ('D') | ('C' << 8) | ('F' << 16), - /// LF Radio HBG Prangins, HB 75 kHz - HBG = ('H') | ('B' << 8) | ('G' << 16), - /// LF Radio MSF Anthorn, UK 60 kHz - MSF = ('M') | ('S' << 8) | ('F' << 16), - /// LF Radio JJY Fukushima, JP 40 kHz, Saga, JP 60 kHz - JJY = ('J') | ('J' << 8) | ('Y' << 16), - /// MF Radio LORAN C station, 100 kHz - LORC = ('L') | ('O' << 8) | ('R' << 16) | ('C' << 24), - /// MF Radio Allouis, FR 162 kHz - TDF = ('T') | ('D' << 8) | ('F' << 16), - /// HF Radio CHU Ottawa, Ontario - CHU = ('C') | ('H' << 8) | ('U' << 16), - /// HF Radio WWV Ft. Collins, CO - WWV = ('W') | ('W' << 8) | ('V' << 16), - /// HF Radio WWVH Kauai, HI - WWVH = ('W') | ('W' << 8) | ('V' << 16) | ('H' << 24), - /// NIST telephone modem - NIST = ('N') | ('I' << 8) | ('S' << 16) | ('T' << 24), - /// NIST telephone modem - ACTS = ('A') | ('C' << 8) | ('T' << 16) | ('S' << 24), - /// USNO telephone modem - USNO = ('U') | ('S' << 8) | ('N' << 16) | ('O' << 24), - /// European telephone modem - PTB = ('P') | ('T' << 8) | ('B' << 16), - /// Multi Reference Sources - MRS = ('M') | ('R' << 8) | ('S' << 16), - /// Inter Face Association Changed - XFAC = ('X') | ('F' << 8) | ('A' << 16) | ('C' << 24), - /// Step time change - STEP = ('S') | ('T' << 8) | ('E' << 16) | ('P' << 24), - /// Google Refid used by Google NTP servers as time4.google.com - GOOG = ('G') | ('O' << 8) | ('O' << 16) | ('G' << 24), - /// Meinberg DCF77 with amplitude modulation (Ref: https://www.meinbergglobal.com/english/info/ntp-refid.htm) - DCFa = ('D') | ('C' << 8) | ('F' << 16) | ('a' << 24), - /// Meinberg DCF77 with phase modulation)/pseudo random phase modulation (Ref: https://www.meinbergglobal.com/english/info/ntp-refid.htm) - DCFp = ('D') | ('C' << 8) | ('F' << 16) | ('p' << 24), - /// Meinberg GPS (with shared memory access) (Ref: https://www.meinbergglobal.com/english/info/ntp-refid.htm) - GPSs = ('G') | ('P' << 8) | ('S' << 16) | ('s' << 24), - /// Meinberg GPS (with interrupt based access) (Ref: https://www.meinbergglobal.com/english/info/ntp-refid.htm) - GPSi = ('G') | ('P' << 8) | ('S' << 16) | ('i' << 24), - /// Meinberg GPS/GLONASS (with shared memory access) (Ref: https://www.meinbergglobal.com/english/info/ntp-refid.htm) - GLNs = ('G') | ('L' << 8) | ('N' << 16) | ('s' << 24), - /// Meinberg GPS/GLONASS (with interrupt based access) (Ref: https://www.meinbergglobal.com/english/info/ntp-refid.htm) - GLNi = ('G') | ('L' << 8) | ('N' << 16) | ('i' << 24), - /// Meinberg Undisciplined local clock (Ref: https://www.meinbergglobal.com/english/info/ntp-refid.htm) - LCL = ('L') | ('C' << 8) | ('L' << 16), - /// Meinberg Undisciplined local clock (Ref: https://www.meinbergglobal.com/english/info/ntp-refid.htm) - LOCL = ('L') | ('O' << 8) | ('C' << 16) | ('L' << 24), - - // NTPv3 - - /// DCN routing protocol - DCN = ('D') | ('C' << 8) | ('N' << 16), - /// TSP time protocol - TSP = ('T') | ('S' << 8) | ('P' << 16), - /// Digital Time Service - DTS = ('D') | ('T' << 8) | ('S' << 16), - /// Atomic clock (calibrated) - ATOM = ('A') | ('T' << 8) | ('O' << 16) | ('M' << 24), - /// VLF radio (OMEGA, etc.) - VLF = ('V') | ('L' << 8) | ('F' << 16) - - }; - - /** - * 32-bit Kiss of Death (KoD) codes - */ - enum class KissODeath : uint32_t - { - /// The association belongs to a anycast server - ACST = ('A') | ('C' << 8) | ('S' << 16) | ('T' << 24), - /// Server authentication failed - AUTH = ('A') | ('U' << 8) | ('T' << 16) | ('H' << 24), - /// Autokey sequence failed - AUTO = ('A') | ('U' << 8) | ('T' << 16) | ('O' << 24), - /// The association belongs to a broadcast server - BCST = ('B') | ('C' << 8) | ('S' << 16) | ('T' << 24), - /// Cryptographic authentication or identification failed - CRYP = ('C') | ('R' << 8) | ('Y' << 16) | ('P' << 24), - /// Access denied by remote server - DENY = ('D') | ('E' << 8) | ('N' << 16) | ('Y' << 24), - /// Lost peer in symmetric mode - DROP = ('D') | ('R' << 8) | ('O' << 16) | ('P' << 24), - /// Access denied due to local policy - RSTR = ('R') | ('S' << 8) | ('T' << 16) | ('R' << 24), - /// The association has not yet synchronized for the first time - INIT = ('I') | ('N' << 8) | ('I' << 16) | ('T' << 24), - /// The association belongs to a manycast server - MCST = ('M') | ('C' << 8) | ('S' << 16) | ('T' << 24), - /// No key found. Either the key was never installed or is not trusted - NKEY = ('N') | ('K' << 8) | ('E' << 16) | ('Y' << 24), - /// Rate exceeded. The server has temporarily denied access because the client exceeded the rate threshold - RATE = ('R') | ('A' << 8) | ('T' << 16) | ('E' << 24), - /// Somebody is tinkering with the association from a remote host running ntpdc. Not to worry unless some rascal has stolen your keys - RMOT = ('R') | ('M' << 8) | ('O' << 16) | ('T' << 24), - /// A step change in system time has occurred, but the association has not yet resynchronized - STEP = ('S') | ('T' << 8) | ('E' << 16) | ('P' << 24), - }; - - /** - * 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 - */ - NtpLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) : Layer(data, dataLen, prevLayer, packet) { m_Protocol = NTP; } - - /** - * Empty c'tor - */ - NtpLayer(); - - /** - * @return The leap indicator - */ - LeapIndicator getLeapIndicator() const; - - /** - * Set the leap indicator - */ - void setLeapIndicator(LeapIndicator val); - - /** - * @return The version of NTP - */ - uint8_t getVersion() const; - - /** - * Set the version of NTP - */ - void setVersion(uint8_t val); - - /** - * @return The mode value - */ - Mode getMode() const; - - /** - * @return The mode as string - */ - std::string getModeString() const; - - /** - * Set the mode - */ - void setMode(Mode val); - - /** - * @return The value of stratum - */ - uint8_t getStratum() const; - - /** - * Set the value of stratum - */ - void setStratum(uint8_t val); - - /** - * @return The value of poll interval in log2 seconds - */ - int8_t getPollInterval() const; - - /** - * Set the value of poll interval - * @param[in] val Poll interval in log2 seconds - */ - void setPollInterval(int8_t val); - - /** - * @return The value of poll interval in seconds - */ - double getPollIntervalInSecs() const; - - /** - * @return The value of precision in log2 seconds - */ - int8_t getPrecision() const; - - /** - * Set the value of precision - * @param[in] val Precision in log2 seconds - */ - void setPrecision(int8_t val); - - /** - * @return The value of precision in seconds - */ - double getPrecisionInSecs() const; - - /** - * @return The value of root delay in NTP short format - */ - uint32_t getRootDelay() const; - - /** - * Set the value of root delay - * @param[in] val Root delay in NTP short format - */ - void setRootDelay(uint32_t val); - - /** - * @return The value of root delay in seconds - */ - double getRootDelayInSecs() const; - - /** - * Set the value of root delay - * @param[in] val Root delay in seconds - */ - void setRootDelayInSecs(double val); - - /** - * @return The value of root dispersion in NTP short format - */ - uint32_t getRootDispersion() const; - - /** - * Set the value of root delay - * @param[in] val Root dispersion in NTP short format - */ - void setRootDispersion(uint32_t val); - - /** - * @return The value of root dispersion in seconds - */ - double getRootDispersionInSecs() const; - - /** - * Set the value of root dispersion - * @param[in] val Root dispersion in seconds - */ - void setRootDispersionInSecs(double val); - - /** - * @return The value of reference identifier - */ - uint32_t getReferenceIdentifier() const; - - /** - * Set the value of reference identifier - * @param[in] val Value of the reference identifier as IPv4 address - */ - void setReferenceIdentifier(IPv4Address val); - - /** - * Set the value of reference identifier - * @param[in] val Value of the reference identifier as ClockSource - */ - void setReferenceIdentifier(ClockSource val); - - /** - * Set the value of reference identifier - * @param[in] val Value of the reference identifier as Kiss-O-Death code - */ - void setReferenceIdentifier(KissODeath val); - - /** - * @return The value of reference identifier as a string. String representation of NTP clock source if stratum is 1, - * IPv4 address or MD5 hash of first four octets of IPv6 - */ - std::string getReferenceIdentifierString() const; - - /** - * @return The value of reference timestamp in NTP timestamp format - */ - uint64_t getReferenceTimestamp() const; - - /** - * Set the value of reference timestamp - * @param[in] val Timestamp in NTP timestamp format - */ - void setReferenceTimestamp(uint64_t val); - - /** - * @return The value of reference timestamp in seconds from Unix Epoch (1 Jan 1970) - */ - double getReferenceTimestampInSecs() const; - - /** - * Set the value of reference timestamp - * @param[in] val Value in seconds from Unix Epoch (1 Jan 1970) - */ - void setReferenceTimestampInSecs(double val); - - /** - * @return The reference timestamp value as readable string in ISO8601 format - */ - std::string getReferenceTimestampAsString(); - - /** - * @return The value of origin timestamp in NTP timestamp format - */ - uint64_t getOriginTimestamp() const; - - /** - * Set the value of origin timestamp - * @param[in] val Value in NTP timestamp format - */ - void setOriginTimestamp(uint64_t val); - - /** - * @return The value of origin timestamp in seconds from Unix Epoch (1 Jan 1970) - */ - double getOriginTimestampInSecs() const; - - /** - * Set the value of origin timestamp - * @param[in] val Value in seconds from Unix Epoch (1 Jan 1970) - */ - void setOriginTimestampInSecs(double val); - - /** - * @return the origin timestamp value as readable string in ISO8601 format - */ - std::string getOriginTimestampAsString(); - - /** - * @return The value of receive timestamp in NTP timestamp format - */ - uint64_t getReceiveTimestamp() const; - - /** - * Set the value of receive timestamp - * @param[in] val Value in NTP timestamp format - */ - void setReceiveTimestamp(uint64_t val); - - /** - * @return The value of receive timestampin seconds from Unix Epoch (1 Jan 1970) - */ - double getReceiveTimestampInSecs() const; - - /** - * Set the value of receive timestamp - * @param[in] val Value in seconds from Unix Epoch (1 Jan 1970) - */ - void setReceiveTimestampInSecs(double val); - - /** - * @return The receive timestamp value as readable string in ISO8601 format - */ - std::string getReceiveTimestampAsString(); - - /** - * @return The value of transmit timestamp in NTP timestamp format - */ - uint64_t getTransmitTimestamp() const; - - /** - * Set the value of transmit timestamp - * @param[in] val Value in NTP timestamp format - */ - void setTransmitTimestamp(uint64_t val); - - /** - * @return The value of transmit timestamp in seconds from Unix Epoch (1 Jan 1970) - */ - double getTransmitTimestampInSecs() const; - - /** - * Set the value of transmit timestamp - * @param[in] val Value in seconds from Unix Epoch (1 Jan 1970) - */ - void setTransmitTimestampInSecs(double val); - - /** - * @return The transmit timestamp value as readable string in ISO8601 format - */ - std::string getTransmitTimestampAsString(); - - /** - * @return Returns the key identifier if exists, returns 0 on unsupported NTP version or key identifier not found - */ - uint32_t getKeyID() const; - - /** - * @return Get the digest value as hexadecimal string, empty string on unsupported version - */ - std::string getDigest() const; - - /** - * Convert NTP short format to seconds from the Unix Epoch - * - * @param[in] val Value in NTP short format - * @return Value in seconds from Unix Epoch (1 Jan 1970) - */ - static double convertFromShortFormat(const uint32_t val); - - /** - * Convert NTP timestamp format to seconds from the Unix Epoch - * - * @param[in] val Value in NTP timestamp format - * @return Value in seconds from Unix Epoch (1 Jan 1970) - */ - static double convertFromTimestampFormat(const uint64_t val); - - /** - * Convert seconds from the Unix Epoch to NTP short format - * - * @param[in] val Value in seconds from Unix Epoch (1 Jan 1970) - * @return Value in NTP short format - */ - static uint32_t convertToShortFormat(const double val); - - /** - * Convert seconds from the Unix Epoch to NTP timestamp format - * - * @param[in] val Value in seconds from Unix Epoch (1 Jan 1970) - * @return Value in NTP timestamp format - */ - static uint64_t convertToTimestampFormat(const double val); - - /** - * A static method to convert timestamp value to ISO8601 date time format - * @param[in] timestamp Value in seconds from the Unix Epoch - * @return std::string ISO8601 formatted string - */ - static std::string convertToIsoFormat(const double timestamp); - - /** - * A static method to convert timestamp value to ISO8601 date time format - * @param[in] timestampInNTPformat Value in NTP timestamp format - * @return std::string ISO8601 formatted string - */ - static std::string convertToIsoFormat(const uint64_t timestampInNTPformat); - - /** - * A static method that takes a byte array and detects whether it is a NTP message - * @param[in] data A byte array - * @param[in] dataSize The byte array size (in bytes) - * @return True if the data is identified as NTP message - */ - static bool isDataValid(const uint8_t *data, size_t dataSize); - - /** - * A static method that checks whether the port is considered as NTP - * @param[in] port The port number to be checked - */ - static bool isNTPPort(uint16_t port) { return port == 123; } - - // overridden methods - - /// Parses the next layer. NTP is the always last so does nothing for this layer - void parseNextLayer() {} - - /** - * @return Get the size of the layer (Including the extension and authentication fields if exists) - */ - size_t getHeaderLen() const { return m_DataLen; } - - /// Does nothing for this layer - void computeCalculateFields() {} - - /** - * @return The OSI layer level of NTP (Application Layer). - */ - OsiModelLayer getOsiModelLayer() const { return OsiModelApplicationLayer; } - - /** - * @return Returns the protocol info as readable string - */ - std::string toString() const; + ntp_header* getNtpHeader() const { return (ntp_header*)m_Data; } + + public: + /** + * Warning of an impending leap second to be inserted or deleted in the last + * minute of the current month + */ + enum LeapIndicator { + /// Normal, no leap second + NoWarning = 0, + /// Last minute of the day has 61 seconds + Last61Secs, + /// Last minute of the day has 59 seconds + Last59Secs, + /// Unknown (clock unsynchronized) + Unknown + }; + + /** + * Representing the NTP association modes + */ + enum Mode { + /// Reserved variable + Reserved = 0, + /// Symmetrically active + SymActive, + /// Symmetrically passive + SymPassive, + /// Client mode + Client, + /// Server mode + Server, + /// Broadcasting mode + Broadcast, + /// NTP control messages + Control, + /// Reserved for private use + PrivateUse + }; + + /** + * 32-bit code identifying the particular server or reference clock. + * The interpretation depends on the value in the stratum field. + */ + enum class ClockSource : uint32_t { + // NTPv4 + + /// Geosynchronous Orbit Environment Satellite + GOES = ('G') | ('O' << 8) | ('E' << 16) | ('S' << 24), + /// Global Position System + GPS = ('G') | ('P' << 8) | ('S' << 16), + /// Galileo Positioning System + GAL = ('G') | ('A' << 8) | ('L' << 16), + /// Generic pulse-per-second + PPS = ('P') | ('P' << 8) | ('S' << 16), + /// Inter-Range Instrumentation Group + IRIG = ('I') | ('R' << 8) | ('I' << 16) | ('G' << 24), + /// LF Radio WWVB Ft. Collins, CO 60 kHz + WWVB = ('W') | ('W' << 8) | ('V' << 16) | ('B' << 24), + /// LF Radio DCF77 Mainflingen, DE 77.5 kHz + DCF = ('D') | ('C' << 8) | ('F' << 16), + /// LF Radio HBG Prangins, HB 75 kHz + HBG = ('H') | ('B' << 8) | ('G' << 16), + /// LF Radio MSF Anthorn, UK 60 kHz + MSF = ('M') | ('S' << 8) | ('F' << 16), + /// LF Radio JJY Fukushima, JP 40 kHz, Saga, JP 60 kHz + JJY = ('J') | ('J' << 8) | ('Y' << 16), + /// MF Radio LORAN C station, 100 kHz + LORC = ('L') | ('O' << 8) | ('R' << 16) | ('C' << 24), + /// MF Radio Allouis, FR 162 kHz + TDF = ('T') | ('D' << 8) | ('F' << 16), + /// HF Radio CHU Ottawa, Ontario + CHU = ('C') | ('H' << 8) | ('U' << 16), + /// HF Radio WWV Ft. Collins, CO + WWV = ('W') | ('W' << 8) | ('V' << 16), + /// HF Radio WWVH Kauai, HI + WWVH = ('W') | ('W' << 8) | ('V' << 16) | ('H' << 24), + /// NIST telephone modem + NIST = ('N') | ('I' << 8) | ('S' << 16) | ('T' << 24), + /// NIST telephone modem + ACTS = ('A') | ('C' << 8) | ('T' << 16) | ('S' << 24), + /// USNO telephone modem + USNO = ('U') | ('S' << 8) | ('N' << 16) | ('O' << 24), + /// European telephone modem + PTB = ('P') | ('T' << 8) | ('B' << 16), + /// Multi Reference Sources + MRS = ('M') | ('R' << 8) | ('S' << 16), + /// Inter Face Association Changed + XFAC = ('X') | ('F' << 8) | ('A' << 16) | ('C' << 24), + /// Step time change + STEP = ('S') | ('T' << 8) | ('E' << 16) | ('P' << 24), + /// Google Refid used by Google NTP servers as time4.google.com + GOOG = ('G') | ('O' << 8) | ('O' << 16) | ('G' << 24), + /// Meinberg DCF77 with amplitude modulation (Ref: + /// https://www.meinbergglobal.com/english/info/ntp-refid.htm) + DCFa = ('D') | ('C' << 8) | ('F' << 16) | ('a' << 24), + /// Meinberg DCF77 with phase modulation)/pseudo random phase modulation + /// (Ref: https://www.meinbergglobal.com/english/info/ntp-refid.htm) + DCFp = ('D') | ('C' << 8) | ('F' << 16) | ('p' << 24), + /// Meinberg GPS (with shared memory access) (Ref: + /// https://www.meinbergglobal.com/english/info/ntp-refid.htm) + GPSs = ('G') | ('P' << 8) | ('S' << 16) | ('s' << 24), + /// Meinberg GPS (with interrupt based access) (Ref: + /// https://www.meinbergglobal.com/english/info/ntp-refid.htm) + GPSi = ('G') | ('P' << 8) | ('S' << 16) | ('i' << 24), + /// Meinberg GPS/GLONASS (with shared memory access) (Ref: + /// https://www.meinbergglobal.com/english/info/ntp-refid.htm) + GLNs = ('G') | ('L' << 8) | ('N' << 16) | ('s' << 24), + /// Meinberg GPS/GLONASS (with interrupt based access) (Ref: + /// https://www.meinbergglobal.com/english/info/ntp-refid.htm) + GLNi = ('G') | ('L' << 8) | ('N' << 16) | ('i' << 24), + /// Meinberg Undisciplined local clock (Ref: + /// https://www.meinbergglobal.com/english/info/ntp-refid.htm) + LCL = ('L') | ('C' << 8) | ('L' << 16), + /// Meinberg Undisciplined local clock (Ref: + /// https://www.meinbergglobal.com/english/info/ntp-refid.htm) + LOCL = ('L') | ('O' << 8) | ('C' << 16) | ('L' << 24), + + // NTPv3 + + /// DCN routing protocol + DCN = ('D') | ('C' << 8) | ('N' << 16), + /// TSP time protocol + TSP = ('T') | ('S' << 8) | ('P' << 16), + /// Digital Time Service + DTS = ('D') | ('T' << 8) | ('S' << 16), + /// Atomic clock (calibrated) + ATOM = ('A') | ('T' << 8) | ('O' << 16) | ('M' << 24), + /// VLF radio (OMEGA, etc.) + VLF = ('V') | ('L' << 8) | ('F' << 16) + }; + /** + * 32-bit Kiss of Death (KoD) codes + */ + enum class KissODeath : uint32_t { + /// The association belongs to a anycast server + ACST = ('A') | ('C' << 8) | ('S' << 16) | ('T' << 24), + /// Server authentication failed + AUTH = ('A') | ('U' << 8) | ('T' << 16) | ('H' << 24), + /// Autokey sequence failed + AUTO = ('A') | ('U' << 8) | ('T' << 16) | ('O' << 24), + /// The association belongs to a broadcast server + BCST = ('B') | ('C' << 8) | ('S' << 16) | ('T' << 24), + /// Cryptographic authentication or identification failed + CRYP = ('C') | ('R' << 8) | ('Y' << 16) | ('P' << 24), + /// Access denied by remote server + DENY = ('D') | ('E' << 8) | ('N' << 16) | ('Y' << 24), + /// Lost peer in symmetric mode + DROP = ('D') | ('R' << 8) | ('O' << 16) | ('P' << 24), + /// Access denied due to local policy + RSTR = ('R') | ('S' << 8) | ('T' << 16) | ('R' << 24), + /// The association has not yet synchronized for the first time + INIT = ('I') | ('N' << 8) | ('I' << 16) | ('T' << 24), + /// The association belongs to a manycast server + MCST = ('M') | ('C' << 8) | ('S' << 16) | ('T' << 24), + /// No key found. Either the key was never installed or is not trusted + NKEY = ('N') | ('K' << 8) | ('E' << 16) | ('Y' << 24), + /// Rate exceeded. The server has temporarily denied access because the + /// client exceeded the rate threshold + RATE = ('R') | ('A' << 8) | ('T' << 16) | ('E' << 24), + /// Somebody is tinkering with the association from a remote host running + /// ntpdc. Not to worry unless some rascal has stolen your keys + RMOT = ('R') | ('M' << 8) | ('O' << 16) | ('T' << 24), + /// A step change in system time has occurred, but the association has not + /// yet resynchronized + STEP = ('S') | ('T' << 8) | ('E' << 16) | ('P' << 24), + }; + + /** + * 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 + */ + NtpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : Layer(data, dataLen, prevLayer, packet) { + m_Protocol = NTP; + } + + /** + * Empty c'tor + */ + NtpLayer(); + + /** + * @return The leap indicator + */ + LeapIndicator getLeapIndicator() const; + + /** + * Set the leap indicator + */ + void setLeapIndicator(LeapIndicator val); + + /** + * @return The version of NTP + */ + uint8_t getVersion() const; + + /** + * Set the version of NTP + */ + void setVersion(uint8_t val); + + /** + * @return The mode value + */ + Mode getMode() const; + + /** + * @return The mode as string + */ + std::string getModeString() const; + + /** + * Set the mode + */ + void setMode(Mode val); + + /** + * @return The value of stratum + */ + uint8_t getStratum() const; + + /** + * Set the value of stratum + */ + void setStratum(uint8_t val); + + /** + * @return The value of poll interval in log2 seconds + */ + int8_t getPollInterval() const; + + /** + * Set the value of poll interval + * @param[in] val Poll interval in log2 seconds + */ + void setPollInterval(int8_t val); + + /** + * @return The value of poll interval in seconds + */ + double getPollIntervalInSecs() const; + + /** + * @return The value of precision in log2 seconds + */ + int8_t getPrecision() const; + + /** + * Set the value of precision + * @param[in] val Precision in log2 seconds + */ + void setPrecision(int8_t val); + + /** + * @return The value of precision in seconds + */ + double getPrecisionInSecs() const; + + /** + * @return The value of root delay in NTP short format + */ + uint32_t getRootDelay() const; + + /** + * Set the value of root delay + * @param[in] val Root delay in NTP short format + */ + void setRootDelay(uint32_t val); + + /** + * @return The value of root delay in seconds + */ + double getRootDelayInSecs() const; + + /** + * Set the value of root delay + * @param[in] val Root delay in seconds + */ + void setRootDelayInSecs(double val); + + /** + * @return The value of root dispersion in NTP short format + */ + uint32_t getRootDispersion() const; + + /** + * Set the value of root delay + * @param[in] val Root dispersion in NTP short format + */ + void setRootDispersion(uint32_t val); + + /** + * @return The value of root dispersion in seconds + */ + double getRootDispersionInSecs() const; + + /** + * Set the value of root dispersion + * @param[in] val Root dispersion in seconds + */ + void setRootDispersionInSecs(double val); + + /** + * @return The value of reference identifier + */ + uint32_t getReferenceIdentifier() const; + + /** + * Set the value of reference identifier + * @param[in] val Value of the reference identifier as IPv4 address + */ + void setReferenceIdentifier(IPv4Address val); + + /** + * Set the value of reference identifier + * @param[in] val Value of the reference identifier as ClockSource + */ + void setReferenceIdentifier(ClockSource val); + + /** + * Set the value of reference identifier + * @param[in] val Value of the reference identifier as Kiss-O-Death code + */ + void setReferenceIdentifier(KissODeath val); + + /** + * @return The value of reference identifier as a string. String + * representation of NTP clock source if stratum is 1, IPv4 address or MD5 + * hash of first four octets of IPv6 + */ + std::string getReferenceIdentifierString() const; + + /** + * @return The value of reference timestamp in NTP timestamp format + */ + uint64_t getReferenceTimestamp() const; + + /** + * Set the value of reference timestamp + * @param[in] val Timestamp in NTP timestamp format + */ + void setReferenceTimestamp(uint64_t val); + + /** + * @return The value of reference timestamp in seconds from Unix Epoch (1 Jan + * 1970) + */ + double getReferenceTimestampInSecs() const; + + /** + * Set the value of reference timestamp + * @param[in] val Value in seconds from Unix Epoch (1 Jan 1970) + */ + void setReferenceTimestampInSecs(double val); + + /** + * @return The reference timestamp value as readable string in ISO8601 format + */ + std::string getReferenceTimestampAsString(); + + /** + * @return The value of origin timestamp in NTP timestamp format + */ + uint64_t getOriginTimestamp() const; + + /** + * Set the value of origin timestamp + * @param[in] val Value in NTP timestamp format + */ + void setOriginTimestamp(uint64_t val); + + /** + * @return The value of origin timestamp in seconds from Unix Epoch (1 Jan + * 1970) + */ + double getOriginTimestampInSecs() const; + + /** + * Set the value of origin timestamp + * @param[in] val Value in seconds from Unix Epoch (1 Jan 1970) + */ + void setOriginTimestampInSecs(double val); + + /** + * @return the origin timestamp value as readable string in ISO8601 format + */ + std::string getOriginTimestampAsString(); + + /** + * @return The value of receive timestamp in NTP timestamp format + */ + uint64_t getReceiveTimestamp() const; + + /** + * Set the value of receive timestamp + * @param[in] val Value in NTP timestamp format + */ + void setReceiveTimestamp(uint64_t val); + + /** + * @return The value of receive timestampin seconds from Unix Epoch (1 Jan + * 1970) + */ + double getReceiveTimestampInSecs() const; + + /** + * Set the value of receive timestamp + * @param[in] val Value in seconds from Unix Epoch (1 Jan 1970) + */ + void setReceiveTimestampInSecs(double val); + + /** + * @return The receive timestamp value as readable string in ISO8601 format + */ + std::string getReceiveTimestampAsString(); + + /** + * @return The value of transmit timestamp in NTP timestamp format + */ + uint64_t getTransmitTimestamp() const; + + /** + * Set the value of transmit timestamp + * @param[in] val Value in NTP timestamp format + */ + void setTransmitTimestamp(uint64_t val); + + /** + * @return The value of transmit timestamp in seconds from Unix Epoch (1 Jan + * 1970) + */ + double getTransmitTimestampInSecs() const; + + /** + * Set the value of transmit timestamp + * @param[in] val Value in seconds from Unix Epoch (1 Jan 1970) + */ + void setTransmitTimestampInSecs(double val); + + /** + * @return The transmit timestamp value as readable string in ISO8601 format + */ + std::string getTransmitTimestampAsString(); + + /** + * @return Returns the key identifier if exists, returns 0 on unsupported NTP + * version or key identifier not found + */ + uint32_t getKeyID() const; + + /** + * @return Get the digest value as hexadecimal string, empty string on + * unsupported version + */ + std::string getDigest() const; + + /** + * Convert NTP short format to seconds from the Unix Epoch + * + * @param[in] val Value in NTP short format + * @return Value in seconds from Unix Epoch (1 Jan 1970) + */ + static double convertFromShortFormat(const uint32_t val); + + /** + * Convert NTP timestamp format to seconds from the Unix Epoch + * + * @param[in] val Value in NTP timestamp format + * @return Value in seconds from Unix Epoch (1 Jan 1970) + */ + static double convertFromTimestampFormat(const uint64_t val); + + /** + * Convert seconds from the Unix Epoch to NTP short format + * + * @param[in] val Value in seconds from Unix Epoch (1 Jan 1970) + * @return Value in NTP short format + */ + static uint32_t convertToShortFormat(const double val); + + /** + * Convert seconds from the Unix Epoch to NTP timestamp format + * + * @param[in] val Value in seconds from Unix Epoch (1 Jan 1970) + * @return Value in NTP timestamp format + */ + static uint64_t convertToTimestampFormat(const double val); + + /** + * A static method to convert timestamp value to ISO8601 date time format + * @param[in] timestamp Value in seconds from the Unix Epoch + * @return std::string ISO8601 formatted string + */ + static std::string convertToIsoFormat(const double timestamp); + + /** + * A static method to convert timestamp value to ISO8601 date time format + * @param[in] timestampInNTPformat Value in NTP timestamp format + * @return std::string ISO8601 formatted string + */ + static std::string convertToIsoFormat(const uint64_t timestampInNTPformat); + + /** + * A static method that takes a byte array and detects whether it is a NTP + * message + * @param[in] data A byte array + * @param[in] dataSize The byte array size (in bytes) + * @return True if the data is identified as NTP message + */ + static bool isDataValid(const uint8_t* data, size_t dataSize); + + /** + * A static method that checks whether the port is considered as NTP + * @param[in] port The port number to be checked + */ + static bool isNTPPort(uint16_t port) { return port == 123; } + + // overridden methods + + /// Parses the next layer. NTP is the always last so does nothing for this + /// layer + void parseNextLayer() {} + + /** + * @return Get the size of the layer (Including the extension and + * authentication fields if exists) + */ + size_t getHeaderLen() const { return m_DataLen; } + + /// Does nothing for this layer + void computeCalculateFields() {} + + /** + * @return The OSI layer level of NTP (Application Layer). + */ + OsiModelLayer getOsiModelLayer() const { return OsiModelApplicationLayer; } + + /** + * @return Returns the protocol info as readable string + */ + std::string toString() const; +}; + } // namespace pcpp #endif /* PACKETPP_NTP_LAYER */ diff --git a/Packet++/header/NullLoopbackLayer.h b/Packet++/header/NullLoopbackLayer.h index 35deb858bf..5c88d12fa6 100644 --- a/Packet++/header/NullLoopbackLayer.h +++ b/Packet++/header/NullLoopbackLayer.h @@ -5,87 +5,89 @@ #include "Layer.h" -namespace pcpp -{ - - /** IPv4 protocol **/ - #define PCPP_BSD_AF_INET 2 - /** XEROX NS protocols */ - #define PCPP_BSD_AF_NS 6 - /** ISO */ - #define PCPP_BSD_AF_ISO 7 - /** AppleTalk */ - #define PCPP_BSD_AF_APPLETALK 16 - /** IPX */ - #define PCPP_BSD_AF_IPX 23 - /** OpenBSD (and probably NetBSD), BSD/OS IPv6 */ - #define PCPP_BSD_AF_INET6_BSD 24 - /** FreeBSD IPv6 */ - #define PCPP_BSD_AF_INET6_FREEBSD 28 - /** Darwin IPv6 */ - #define PCPP_BSD_AF_INET6_DARWIN 30 - - /** - * @class NullLoopbackLayer - * Represents a NULL/Loopback layer - */ - class NullLoopbackLayer : public Layer - { - public: - /** 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] packet A pointer to the Packet instance where layer will be stored in - */ - NullLoopbackLayer(uint8_t* data, size_t dataLen, Packet* packet) : Layer(data, dataLen, NULL, packet) { m_Protocol = NULL_LOOPBACK; } - - /** - * A constructor that allocates a new Null/Loopback header - * @param[in] family The family protocol to set - */ - explicit NullLoopbackLayer(uint32_t family); - - /** - * A destructor for this layer (does nothing) - */ - ~NullLoopbackLayer() {} - - /** - * @return The protocol family in this layer - */ - uint32_t getFamily() const; - - /** - * Set a protocol family - * @param[in] family The family protocol to set - */ - void setFamily(uint32_t family); - - - // implement abstract methods - - /** - * Identifies the next layers by family: - * - for ::PCPP_BSD_AF_INET the next layer is IPv4Layer - * - for ::PCPP_BSD_AF_INET6_BSD, ::PCPP_BSD_AF_INET6_FREEBSD, ::PCPP_BSD_AF_INET6_DARWIN the next layer is IPv6Layer - * - for other values the next layer in PayloadLayer (unknown protocol) - */ - void parseNextLayer(); - - /** - * @return Size of Null/Loopback header = 4B - */ - size_t getHeaderLen() const { return sizeof(uint32_t); } - - /** - * Does nothing for this layer - */ - void computeCalculateFields() {} - - std::string toString() const; - - OsiModelLayer getOsiModelLayer() const { return OsiModelDataLinkLayer; } - }; +namespace pcpp { + +/** IPv4 protocol **/ +#define PCPP_BSD_AF_INET 2 +/** XEROX NS protocols */ +#define PCPP_BSD_AF_NS 6 +/** ISO */ +#define PCPP_BSD_AF_ISO 7 +/** AppleTalk */ +#define PCPP_BSD_AF_APPLETALK 16 +/** IPX */ +#define PCPP_BSD_AF_IPX 23 +/** OpenBSD (and probably NetBSD), BSD/OS IPv6 */ +#define PCPP_BSD_AF_INET6_BSD 24 +/** FreeBSD IPv6 */ +#define PCPP_BSD_AF_INET6_FREEBSD 28 +/** Darwin IPv6 */ +#define PCPP_BSD_AF_INET6_DARWIN 30 + +/** + * @class NullLoopbackLayer + * Represents a NULL/Loopback layer + */ +class NullLoopbackLayer : public Layer { + public: + /** 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] packet A pointer to the Packet instance where layer will be + * stored in + */ + NullLoopbackLayer(uint8_t* data, size_t dataLen, Packet* packet) + : Layer(data, dataLen, NULL, packet) { + m_Protocol = NULL_LOOPBACK; + } + + /** + * A constructor that allocates a new Null/Loopback header + * @param[in] family The family protocol to set + */ + explicit NullLoopbackLayer(uint32_t family); + + /** + * A destructor for this layer (does nothing) + */ + ~NullLoopbackLayer() {} + + /** + * @return The protocol family in this layer + */ + uint32_t getFamily() const; + + /** + * Set a protocol family + * @param[in] family The family protocol to set + */ + void setFamily(uint32_t family); + + // implement abstract methods + + /** + * Identifies the next layers by family: + * - for ::PCPP_BSD_AF_INET the next layer is IPv4Layer + * - for ::PCPP_BSD_AF_INET6_BSD, ::PCPP_BSD_AF_INET6_FREEBSD, + * ::PCPP_BSD_AF_INET6_DARWIN the next layer is IPv6Layer + * - for other values the next layer in PayloadLayer (unknown protocol) + */ + void parseNextLayer(); + + /** + * @return Size of Null/Loopback header = 4B + */ + size_t getHeaderLen() const { return sizeof(uint32_t); } + + /** + * Does nothing for this layer + */ + void computeCalculateFields() {} + + std::string toString() const; + + OsiModelLayer getOsiModelLayer() const { return OsiModelDataLinkLayer; } +}; } // namespace pcpp diff --git a/Packet++/header/PPPoELayer.h b/Packet++/header/PPPoELayer.h index cb76b3a77d..27decfb4a1 100644 --- a/Packet++/header/PPPoELayer.h +++ b/Packet++/header/PPPoELayer.h @@ -3,8 +3,8 @@ #include "Layer.h" #include "TLVData.h" -#include #include +#include /// @file @@ -12,696 +12,724 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ +namespace pcpp { - /** - * @struct pppoe_header - * Represents an PPPoE protocol header - */ +/** + * @struct pppoe_header + * Represents an PPPoE protocol header + */ #pragma pack(push, 1) - struct pppoe_header - { +struct pppoe_header { #if (BYTE_ORDER == LITTLE_ENDIAN) - /** PPPoE version */ - uint8_t version:4, - /** PPPoE type */ - type:4; - /** PPPoE code */ - uint8_t code; + /** PPPoE version */ + uint8_t version : 4, + /** PPPoE type */ + type : 4; + /** PPPoE code */ + uint8_t code; #else - /** PPPoE version */ - uint16_t version:4, - /** PPPoE type */ - type:4, - /** PPPoE code */ - code:8; + /** PPPoE version */ + uint16_t version : 4, + /** PPPoE type */ + type : 4, + /** PPPoE code */ + code : 8; #endif - /** PPPoE session ID (relevant for PPPoE session packets only) */ - uint16_t sessionId; - /** Length (in bytes) of payload, not including the PPPoE header */ - uint16_t payloadLength; - }; + /** PPPoE session ID (relevant for PPPoE session packets only) */ + uint16_t sessionId; + /** Length (in bytes) of payload, not including the PPPoE header */ + uint16_t payloadLength; +}; #pragma pack(pop) +/** + * @class PPPoELayer + * An abstract class that describes the PPPoE protocol. Contains common data and + * logic of the two types of PPPoE packets: PPPoE session and PPPoE discovery + */ +class PPPoELayer : public Layer { + public: + /** + * PPPoE possible codes + */ + enum PPPoECode { + /** PPPoE session code */ + PPPOE_CODE_SESSION = 0x00, + /** PPPoE discovery PADO */ + PPPOE_CODE_PADO = 0x07, + /** PPPoE discovery PADI */ + PPPOE_CODE_PADI = 0x09, + /** PPPoE discovery PADG */ + PPPOE_CODE_PADG = 0x0a, + /** PPPoE discovery PADC */ + PPPOE_CODE_PADC = 0x0b, + /** PPPoE discovery PADQ */ + PPPOE_CODE_PADQ = 0x0c, + /** PPPoE discovery PADR */ + PPPOE_CODE_PADR = 0x19, + /** PPPoE discovery PADS */ + PPPOE_CODE_PADS = 0x65, + /** PPPoE discovery PADT */ + PPPOE_CODE_PADT = 0xa7, + /** PPPoE discovery PADM */ + PPPOE_CODE_PADM = 0xd3, + /** PPPoE discovery PADN */ + PPPOE_CODE_PADN = 0xd4 + }; + + ~PPPoELayer() {} + + /** + * Get a pointer to the PPPoE header. Notice this points directly to the data, + * so every change will change the actual packet data + * @return A pointer to the pppoe_header + */ + pppoe_header* getPPPoEHeader() const { return (pppoe_header*)m_Data; } + + // abstract methods implementation + + /** + * Calculate @ref pppoe_header#payloadLength field + */ + virtual void computeCalculateFields(); + + OsiModelLayer getOsiModelLayer() const { return OsiModelDataLinkLayer; } + + protected: + // protected c'tor as this class shouldn't be instantiated + PPPoELayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : Layer(data, dataLen, prevLayer, packet) {} + + // protected c'tor as this class shouldn't be instantiated + PPPoELayer(uint8_t version, uint8_t type, PPPoELayer::PPPoECode code, + uint16_t sessionId, size_t additionalBytesToAllocate = 0); +}; - /** - * @class PPPoELayer - * An abstract class that describes the PPPoE protocol. Contains common data and logic of the two types of PPPoE packets: PPPoE session - * and PPPoE discovery - */ - class PPPoELayer : public Layer - { - public: - /** - * PPPoE possible codes - */ - enum PPPoECode - { - /** PPPoE session code */ - PPPOE_CODE_SESSION = 0x00, - /** PPPoE discovery PADO */ - PPPOE_CODE_PADO = 0x07, - /** PPPoE discovery PADI */ - PPPOE_CODE_PADI = 0x09, - /** PPPoE discovery PADG */ - PPPOE_CODE_PADG = 0x0a, - /** PPPoE discovery PADC */ - PPPOE_CODE_PADC = 0x0b, - /** PPPoE discovery PADQ */ - PPPOE_CODE_PADQ = 0x0c, - /** PPPoE discovery PADR */ - PPPOE_CODE_PADR = 0x19, - /** PPPoE discovery PADS */ - PPPOE_CODE_PADS = 0x65, - /** PPPoE discovery PADT */ - PPPOE_CODE_PADT = 0xa7, - /** PPPoE discovery PADM */ - PPPOE_CODE_PADM = 0xd3, - /** PPPoE discovery PADN */ - PPPOE_CODE_PADN = 0xd4 - }; - - ~PPPoELayer() {} - - /** - * Get a pointer to the PPPoE header. Notice this points directly to the data, so every change will change the actual packet data - * @return A pointer to the pppoe_header - */ - pppoe_header* getPPPoEHeader() const { return (pppoe_header*)m_Data; } - - // abstract methods implementation - - /** - * Calculate @ref pppoe_header#payloadLength field - */ - virtual void computeCalculateFields(); - - OsiModelLayer getOsiModelLayer() const { return OsiModelDataLinkLayer; } - - protected: - - // protected c'tor as this class shouldn't be instantiated - PPPoELayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) { } - - // protected c'tor as this class shouldn't be instantiated - PPPoELayer(uint8_t version, uint8_t type, PPPoELayer::PPPoECode code, uint16_t sessionId, size_t additionalBytesToAllocate = 0); - - }; - - - /** - * @class PPPoESessionLayer - * Describes the PPPoE session protocol - */ - class PPPoESessionLayer : public PPPoELayer - { - public: - - /** - * A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data (will be casted to @ref pppoe_header) - * @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 - */ - PPPoESessionLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : PPPoELayer(data, dataLen, prevLayer, packet) { m_Protocol = PPPoESession; } - - /** - * A constructor that allocates a new PPPoE Session header with version, type and session ID - * @param[in] version PPPoE version - * @param[in] type PPPoE type - * @param[in] sessionId PPPoE session ID - * @param[in] pppNextProtocol The next protocol to come after the PPPoE session header. Should be one of the PPP_* macros listed below - */ - PPPoESessionLayer(uint8_t version, uint8_t type, uint16_t sessionId, uint16_t pppNextProtocol) : PPPoELayer(version, type, PPPoELayer::PPPOE_CODE_SESSION, sessionId, sizeof(uint16_t)) { setPPPNextProtocol(pppNextProtocol); } - - virtual ~PPPoESessionLayer() {} - - /** - * @return The protocol after the PPPoE session header. The return value is one of the PPP_* macros listed below. This method is also - * used when parsing a packet (this way we know which layer comes after the PPPoE session) - */ - uint16_t getPPPNextProtocol() const; - - /** - * Set the field that describes which header comes after the PPPoE session header - * @param[in] nextProtocol The protocol value. Should be one of the PPP_* macros listed below - */ - void setPPPNextProtocol(uint16_t nextProtocol); - - /** - * A static method that validates the input data - * @param[in] data The pointer to the beginning of byte stream of a packet - * @param[in] dataLen The length of the byte stream - * @return True if the data is valid and can represent a PPPoES packet - */ - static inline bool isDataValid(const uint8_t* data, size_t dataLen); - - // abstract methods implementation - - /** - * Currently identifies the following next layers: IPv4Layer, IPv6Layer. Otherwise sets PayloadLayer - */ - virtual void parseNextLayer(); - - /** - * @return Size of @ref pppoe_header - */ - virtual size_t getHeaderLen() const { return sizeof(pppoe_header) + sizeof(uint16_t); } - - virtual std::string toString() const; - }; - - - - /** - * @class PPPoEDiscoveryLayer - * Describes the PPPoE discovery protocol - */ - class PPPoEDiscoveryLayer : public PPPoELayer - { - public: - /** - * PPPoE tag types - */ - enum PPPoETagTypes - { - /** End-Of-List tag type*/ - PPPOE_TAG_EOL = 0x0000, - /** Service-Name tag type*/ - PPPOE_TAG_SVC_NAME = 0x0101, - /** AC-Name tag type*/ - PPPOE_TAG_AC_NAME = 0x0102, - /** Host-Uniq tag type*/ - PPPOE_TAG_HOST_UNIQ = 0x0103, - /** AC-Cookie tag type*/ - PPPOE_TAG_AC_COOKIE = 0x0104, - /** Vendor-Specific tag type*/ - PPPOE_TAG_VENDOR = 0x0105, - /** Credits tag type*/ - PPPOE_TAG_CREDITS = 0x0106, - /** Metrics tag type*/ - PPPOE_TAG_METRICS = 0x0107, - /** Sequence Number tag type */ - PPPOE_TAG_SEQ_NUM = 0x0108, - /** Credit Scale Factor tag type */ - PPPOE_TAG_CRED_SCALE = 0x0109, - /** Relay-Session-Id tag type */ - PPPOE_TAG_RELAY_ID = 0x0110, - /** HURL tag type */ - PPPOE_TAG_HURL = 0x0111, - /** MOTM tag type */ - PPPOE_TAG_MOTM = 0x0112, - /** PPP-Max-Payload tag type */ - PPPOE_TAG_MAX_PAYLD = 0x0120, - /** IP_Route_Add tag type */ - PPPOE_TAG_IP_RT_ADD = 0x0121, - /** Service-Name-Error tag type */ - PPPOE_TAG_SVC_ERR = 0x0201, - /** AC-System-Error tag type */ - PPPOE_TAG_AC_ERR = 0x0202, - /** Generic-Error tag type */ - PPPOE_TAG_GENERIC_ERR= 0x0203 - }; - - /** - * @class PPPoETag - * Represents a PPPoE tag and its data - */ - class PPPoETag : public TLVRecord - { - public: - /** - * A c'tor that gets a pointer to the tag raw data (byte array) - * @param[in] tagRawData A pointer to the tag raw data - */ - explicit PPPoETag(uint8_t* tagRawData) : TLVRecord(tagRawData) { } - - /** - * A d'tor for this class, currently does nothing - */ - virtual ~PPPoETag() { } - - /** - * @return The tag type converted to PPPoEDiscoveryLayer#PPPoETagTypes enum - */ - PPPoEDiscoveryLayer::PPPoETagTypes getType() const; - - /** - * Retrieve the tag data as string. Relevant only if the tag value is indeed a string - * @return The tag data as string - */ - std::string getValueAsString() const - { - size_t dataSize = getDataSize(); - if (dataSize < 1) - return ""; - - return std::string((const char*)m_Data->recordValue, dataSize); - } - - // implement abstract methods - - size_t getTotalSize() const; - - size_t getDataSize() const; - }; - - - /** - * @class PPPoETagBuilder - * A class for building PPPoE Tags. This builder receives the tag parameters in its c'tor, - * builds the PPPoE Tag raw buffer and provides a build() method to get a PPPoETag object out of it - */ - class PPPoETagBuilder : public TLVRecordBuilder - { - public: - - /** - * A c'tor for building a PPPoE Tag which has no value (tag len is zero). The PPPoETag object can later - * be retrieved by calling build() - * @param[in] tagType Tag type - */ - explicit PPPoETagBuilder(PPPoETagTypes tagType) : - TLVRecordBuilder(static_cast(tagType), NULL, 0) { } - - /** - * A c'tor for building a PPPoE Tag which has a 4-byte value. The PPPoETag object can later - * be retrieved by calling build() - * @param[in] tagType Tag type - * @param[in] tagValue The tag's 4-byte value - */ - PPPoETagBuilder(PPPoETagTypes tagType, uint32_t tagValue) : - TLVRecordBuilder(static_cast(tagType), tagValue) { } - - /** - * A c'tor for building a PPPoE Tag which has some arbitrary value. The PPPoETag object can later - * be retrieved by calling build() - * @param[in] tagType Tag type - * @param[in] tagValue A byte array that contains the tag data - * @param[in] tagValueLen The length of the value byte array - */ - PPPoETagBuilder(PPPoETagTypes tagType, uint8_t* tagValue, uint8_t tagValueLen) : - TLVRecordBuilder(static_cast(tagType), tagValue, tagValueLen) { } - - /** - * Build the PPPoETag object out of the parameters defined in the c'tor - * @return The PPPoETag object - */ - PPPoETag build() const; - }; - - /** - * A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data (will be casted to @ref pppoe_header) - * @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 - */ - PPPoEDiscoveryLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : PPPoELayer(data, dataLen, prevLayer, packet) { m_Protocol = PPPoEDiscovery; m_DataLen = getHeaderLen(); } - - /** - * A constructor that allocates a new PPPoE Discovery header with version, type, PPPoE code and session ID - * @param[in] version PPPoE version - * @param[in] type PPPoE type - * @param[in] code PPPoE code enum - * @param[in] sessionId PPPoE session ID - */ - PPPoEDiscoveryLayer(uint8_t version, uint8_t type, PPPoELayer::PPPoECode code, uint16_t sessionId) : PPPoELayer(version, type, code, sessionId) { m_Protocol = PPPoEDiscovery; } - - /** - * Get a PPPoE Tag by tag type. - * @param[in] tagType The type of the tag to search - * @return A PPPoETag object that contains the first tag that matches this type, or logical NULL - * (PPPoETag#isNull() == true) if no such tag found - */ - PPPoETag getTag(PPPoEDiscoveryLayer::PPPoETagTypes tagType) const; - - /** - * @return The first tag in the PPPoE discovery layer. If the current layer contains no tags the returned value will contain - * a logical NULL (PPPoETag#isNull() == true) - */ - PPPoETag getFirstTag() const; - - /** - * Get the tag that comes right after the "tag" parameter. If the given tag is the last one, the returned value - * will contain a logical NULL (PPPoETag#isNull() == true) - * @param[in] tag A given tag - * @return A PPPoETag object containing the tag that comes next, or logical NULL if the given - * tag: (1) was the last one; (2) contains a logical NULL or (3) doesn't belong to this packet - */ - PPPoETag getNextTag(const PPPoETag& tag) const; - - /** - * @return The number of tags in this layer - */ - int getTagCount() const; - - /** - * Add a new PPPoE Tag at the end of the layer - * @param[in] tagBuilder A PPPoETagBuilder object that contains the requested tag data to add - * @return A PPPoETag object containing the newly added PPPoE Tag data or logical NULL - * (PPPoETag#isNull() == true) if addition failed - */ - PPPoETag addTag(const PPPoETagBuilder& tagBuilder); - - /** - * Add a new PPPoE Tag after an existing one - * @param[in] tagBuilder A PPPoETagBuilder object that contains the requested tag data to add - * @param[in] prevTagType The PPPoE Tag which the newly added tag will come after - * @return A PPPoETag object containing the newly added PPPoE Tag data or logical NULL - * (PPPoETag#isNull() == true) if addition failed - */ - PPPoETag addTagAfter(const PPPoETagBuilder& tagBuilder, PPPoETagTypes prevTagType); - - /** - * Remove an existing tag. Tag will be found by the tag type - * @param[in] tagType The tag type to remove - * @return True if tag was removed or false if tag wasn't found or if tag removal failed (in each case a proper error will be written - * to log) - */ - bool removeTag(PPPoEDiscoveryLayer::PPPoETagTypes tagType); - - /** - * Remove all tags in this layer - * @return True if all tags were successfully or false if removal failed for some reason (a proper error will be written to log) - */ - bool removeAllTags(); - - /** - * A static method that validates the input data - * @param[in] data The pointer to the beginning of byte stream of a packet - * @param[in] dataLen The length of the byte stream - * @return True if the data is valid and can represent a PPPoED packet - */ - static inline bool isDataValid(const uint8_t* data, size_t dataLen); - - // abstract methods implementation - - /** - * Does nothing for this layer (PPPoE discovery is always the last layer) - */ - virtual void parseNextLayer() {} - - /** - * @return The header length which is size of strcut pppoe_header plus the total size of tags - */ - virtual size_t getHeaderLen() const; - - virtual std::string toString() const { return "PPP-over-Ethernet Discovery (" + codeToString((PPPoELayer::PPPoECode)getPPPoEHeader()->code) + ")"; } - - private: - TLVRecordReader m_TagReader; - - PPPoETag addTagAt(const PPPoETagBuilder& tagBuilder, int offset); - - uint8_t* getTagBasePtr() const { return m_Data + sizeof(pppoe_header); } - - std::string codeToString(PPPoECode code) const; - }; - - - // implementation of inline methods - - bool PPPoESessionLayer::isDataValid(const uint8_t* data, size_t dataLen) - { - return data && dataLen >= sizeof(pppoe_header) + sizeof(uint16_t); - } - - bool PPPoEDiscoveryLayer::isDataValid(const uint8_t* data, size_t dataLen) - { - return data && dataLen >= sizeof(pppoe_header); - } - - // Copied from Wireshark: ppptypes.h - - /** Padding Protocol */ -#define PCPP_PPP_PADDING 0x1 - /** ROHC small-CID */ -#define PCPP_PPP_ROHC_SCID 0x3 - /** ROHC large-CID */ -#define PCPP_PPP_ROHC_LCID 0x5 - /** Internet Protocol version 4 */ -#define PCPP_PPP_IP 0x21 - /** OSI Network Layer */ -#define PCPP_PPP_OSI 0x23 - /** Xerox NS IDP */ -#define PCPP_PPP_XNSIDP 0x25 - /** DECnet Phase IV */ -#define PCPP_PPP_DEC4 0x27 - /** AppleTalk */ -#define PCPP_PPP_AT 0x29 - /** Novell IPX */ -#define PCPP_PPP_IPX 0x2b - /** Van Jacobson Compressed TCP/IP */ -#define PCPP_PPP_VJC_COMP 0x2d - /** Van Jacobson Uncompressed TCP/IP */ -#define PCPP_PPP_VJC_UNCOMP 0x2f - /** Bridging PDU */ -#define PCPP_PPP_BCP 0x31 - /** Stream Protocol (ST-II) */ -#define PCPP_PPP_ST 0x33 - /** Banyan Vines */ -#define PCPP_PPP_VINES 0x35 - /** AppleTalk EDDP */ -#define PCPP_PPP_AT_EDDP 0x39 - /** AppleTalk SmartBuffered */ -#define PCPP_PPP_AT_SB 0x3b - /** Multi-Link */ -#define PCPP_PPP_MP 0x3d - /** NETBIOS Framing */ -#define PCPP_PPP_NB 0x3f - /** Cisco Systems */ -#define PCPP_PPP_CISCO 0x41 - /** Ascom Timeplex */ -#define PCPP_PPP_ASCOM 0x43 - /** Fujitsu Link Backup and Load Balancing */ -#define PCPP_PPP_LBLB 0x45 - /** DCA Remote Lan */ -#define PCPP_PPP_RL 0x47 - /** Serial Data Transport Protocol */ -#define PCPP_PPP_SDTP 0x49 - /** SNA over 802.2 */ -#define PCPP_PPP_LLC 0x4b - /** SNA */ -#define PCPP_PPP_SNA 0x4d - /** IPv6 Header Compression */ -#define PCPP_PPP_IPV6HC 0x4f - /** KNX Bridging Data */ -#define PCPP_PPP_KNX 0x51 - /** Encryption */ -#define PCPP_PPP_ENCRYPT 0x53 - /** Individual Link Encryption */ -#define PCPP_PPP_ILE 0x55 - /** Internet Protocol version 6 */ -#define PCPP_PPP_IPV6 0x57 - /** PPP Muxing */ -#define PCPP_PPP_MUX 0x59 - /** Vendor-Specific Network Protocol (VSNP) */ -#define PCPP_PPP_VSNP 0x5b - /** TRILL Network Protocol (TNP) */ -#define PCPP_PPP_TNP 0x5d - /** RTP IPHC Full Header */ -#define PCPP_PPP_RTP_FH 0x61 - /** RTP IPHC Compressed TCP */ -#define PCPP_PPP_RTP_CTCP 0x63 - /** RTP IPHC Compressed Non TCP */ -#define PCPP_PPP_RTP_CNTCP 0x65 - /** RTP IPHC Compressed UDP 8 */ -#define PCPP_PPP_RTP_CUDP8 0x67 - /** RTP IPHC Compressed RTP 8 */ -#define PCPP_PPP_RTP_CRTP8 0x69 - /** Stampede Bridging */ -#define PCPP_PPP_STAMPEDE 0x6f - /** MP+ Protocol */ -#define PCPP_PPP_MPPLUS 0x73 - /** NTCITS IPI */ -#define PCPP_PPP_NTCITS_IPI 0xc1 - /** Single link compression in multilink */ -#define PCPP_PPP_ML_SLCOMP 0xfb - /** Compressed datagram */ -#define PCPP_PPP_COMP 0xfd - /** 802.1d Hello Packets */ -#define PCPP_PPP_STP_HELLO 0x0201 - /** IBM Source Routing BPDU */ -#define PCPP_PPP_IBM_SR 0x0203 - /** DEC LANBridge100 Spanning Tree */ -#define PCPP_PPP_DEC_LB 0x0205 - /** Cisco Discovery Protocol */ -#define PCPP_PPP_CDP 0x0207 - /** Netcs Twin Routing */ -#define PCPP_PPP_NETCS 0x0209 - /** STP - Scheduled Transfer Protocol */ -#define PCPP_PPP_STP 0x020b - /** EDP - Extreme Discovery Protocol */ -#define PCPP_PPP_EDP 0x020d - /** Optical Supervisory Channel Protocol */ -#define PCPP_PPP_OSCP 0x0211 - /** Optical Supervisory Channel Protocol */ -#define PCPP_PPP_OSCP2 0x0213 - /** Luxcom */ -#define PCPP_PPP_LUXCOM 0x0231 - /** Sigma Network Systems */ -#define PCPP_PPP_SIGMA 0x0233 - /** Apple Client Server Protocol */ -#define PCPP_PPP_ACSP 0x0235 - /** MPLS Unicast */ -#define PCPP_PPP_MPLS_UNI 0x0281 - /** MPLS Multicast */ -#define PCPP_PPP_MPLS_MULTI 0x0283 - /** IEEE p1284.4 standard - data packets */ -#define PCPP_PPP_P12844 0x0285 - /** ETSI TETRA Network Protocol Type 1 */ -#define PCPP_PPP_TETRA 0x0287 - /** Multichannel Flow Treatment Protocol */ -#define PCPP_PPP_MFTP 0x0289 - /** RTP IPHC Compressed TCP No Delta */ -#define PCPP_PPP_RTP_CTCPND 0x2063 - /** RTP IPHC Context State */ -#define PCPP_PPP_RTP_CS 0x2065 - /** RTP IPHC Compressed UDP 16 */ -#define PCPP_PPP_RTP_CUDP16 0x2067 - /** RTP IPHC Compressed RTP 16 */ -#define PCPP_PPP_RTP_CRDP16 0x2069 - /** Cray Communications Control Protocol */ -#define PCPP_PPP_CCCP 0x4001 - /** CDPD Mobile Network Registration Protocol */ -#define PCPP_PPP_CDPD_MNRP 0x4003 - /** Expand accelerator protocol */ -#define PCPP_PPP_EXPANDAP 0x4005 - /** ODSICP NCP */ -#define PCPP_PPP_ODSICP 0x4007 - /** DOCSIS DLL */ -#define PCPP_PPP_DOCSIS 0x4009 - /** Cetacean Network Detection Protocol */ -#define PCPP_PPP_CETACEANNDP 0x400b - /** Stacker LZS */ -#define PCPP_PPP_LZS 0x4021 - /** RefTek Protocol */ -#define PCPP_PPP_REFTEK 0x4023 - /** Fibre Channel */ -#define PCPP_PPP_FC 0x4025 - /** EMIT Protocols */ -#define PCPP_PPP_EMIT 0x4027 - /** Vendor-Specific Protocol (VSP) */ -#define PCPP_PPP_VSP 0x405b - /** TRILL Link State Protocol (TLSP) */ -#define PCPP_PPP_TLSP 0x405d - /** Internet Protocol Control Protocol */ -#define PCPP_PPP_IPCP 0x8021 - /** OSI Network Layer Control Protocol */ -#define PCPP_PPP_OSINLCP 0x8023 - /** Xerox NS IDP Control Protocol */ -#define PCPP_PPP_XNSIDPCP 0x8025 - /** DECnet Phase IV Control Protocol */ -#define PCPP_PPP_DECNETCP 0x8027 - /** AppleTalk Control Protocol */ -#define PCPP_PPP_ATCP 0x8029 - /** Novell IPX Control Protocol */ -#define PCPP_PPP_IPXCP 0x802b - /** Bridging NCP */ -#define PCPP_PPP_BRIDGENCP 0x8031 - /** Stream Protocol Control Protocol */ -#define PCPP_PPP_SPCP 0x8033 - /** Banyan Vines Control Protocol */ -#define PCPP_PPP_BVCP 0x8035 - /** Multi-Link Control Protocol */ -#define PCPP_PPP_MLCP 0x803d - /** NETBIOS Framing Control Protocol */ -#define PCPP_PPP_NBCP 0x803f - /** Cisco Systems Control Protocol */ -#define PCPP_PPP_CISCOCP 0x8041 - /** Ascom Timeplex Control Protocol (?) */ -#define PCPP_PPP_ASCOMCP 0x8043 - /** Fujitsu LBLB Control Protocol */ -#define PCPP_PPP_LBLBCP 0x8045 - /** DCA Remote Lan Network Control Protocol */ -#define PCPP_PPP_RLNCP 0x8047 - /** Serial Data Control Protocol */ -#define PCPP_PPP_SDCP 0x8049 - /** SNA over 802.2 Control Protocol */ -#define PCPP_PPP_LLCCP 0x804b - /** SNA Control Protocol */ -#define PCPP_PPP_SNACP 0x804d - /** IP6 Header Compression Control Protocol */ -#define PCPP_PPP_IP6HCCP 0x804f - /** KNX Bridging Control Protocol */ -#define PCPP_PPP_KNXCP 0x8051 - /** Encryption Control Protocol */ -#define PCPP_PPP_ECP 0x8053 - /** Individual Link Encryption Control Protocol */ -#define PCPP_PPP_ILECP 0x8055 - /** IPv6 Control Protocol */ -#define PCPP_PPP_IPV6CP 0x8057 - /** PPP Muxing Control Protocol */ -#define PCPP_PPP_MUXCP 0x8059 - /** Vendor-Specific Network Control Protocol (VSNCP) [RFC3772] */ -#define PCPP_PPP_VSNCP 0x805b - /** TRILL Network Control Protocol (TNCP) */ -#define PCPP_PPP_TNCP 0x805d - /** Stampede Bridging Control Protocol */ -#define PCPP_PPP_STAMPEDECP 0x806f - /** MP+ Contorol Protocol */ -#define PCPP_PPP_MPPCP 0x8073 - /** NTCITS IPI Control Protocol */ -#define PCPP_PPP_IPICP 0x80c1 - /** Single link compression in multilink control */ -#define PCPP_PPP_SLCC 0x80fb - /** Compression Control Protocol */ -#define PCPP_PPP_CCP 0x80fd - /** Cisco Discovery Protocol Control Protocol */ -#define PCPP_PPP_CDPCP 0x8207 - /** Netcs Twin Routing */ -#define PCPP_PPP_NETCSCP 0x8209 - /** STP - Control Protocol */ -#define PCPP_PPP_STPCP 0x820b - /** EDPCP - Extreme Discovery Protocol Control Protocol */ -#define PCPP_PPP_EDPCP 0x820d - /** Apple Client Server Protocol Control */ -#define PCPP_PPP_ACSPC 0x8235 - /** MPLS Control Protocol */ -#define PCPP_PPP_MPLSCP 0x8281 - /** IEEE p1284.4 standard - Protocol Control */ -#define PCPP_PPP_P12844CP 0x8285 - /** ETSI TETRA TNP1 Control Protocol */ -#define PCPP_PPP_TETRACP 0x8287 - /** Multichannel Flow Treatment Protocol */ -#define PCPP_PPP_MFTPCP 0x8289 - /** Link Control Protocol */ -#define PCPP_PPP_LCP 0xc021 - /** Password Authentication Protocol */ -#define PCPP_PPP_PAP 0xc023 - /** Link Quality Report */ -#define PCPP_PPP_LQR 0xc025 - /** Shiva Password Authentication Protocol */ -#define PCPP_PPP_SPAP 0xc027 - /** CallBack Control Protocol (CBCP) */ -#define PCPP_PPP_CBCP 0xc029 - /** BACP Bandwidth Allocation Control Protocol */ -#define PCPP_PPP_BACP 0xc02b - /** BAP Bandwidth Allocation Protocol */ -#define PCPP_PPP_BAP 0xc02d - /** Vendor-Specific Authentication Protocol (VSAP) */ -#define PCPP_PPP_VSAP 0xc05b - /** Container Control Protocol */ -#define PCPP_PPP_CONTCP 0xc081 - /** Challenge Handshake Authentication Protocol */ -#define PCPP_PPP_CHAP 0xc223 - /** RSA Authentication Protocol */ -#define PCPP_PPP_RSAAP 0xc225 - /** Extensible Authentication Protocol */ -#define PCPP_PPP_EAP 0xc227 - /** Mitsubishi Security Information Exchange Protocol (SIEP) */ -#define PCPP_PPP_SIEP 0xc229 - /** Stampede Bridging Authorization Protocol */ -#define PCPP_PPP_SBAP 0xc26f - /** Proprietary Authentication Protocol */ -#define PCPP_PPP_PRPAP 0xc281 - /** Proprietary Authentication Protocol */ -#define PCPP_PPP_PRPAP2 0xc283 - /** Proprietary Node ID Authentication Protocol */ -#define PCPP_PPP_PRPNIAP 0xc481 +/** + * @class PPPoESessionLayer + * Describes the PPPoE session protocol + */ +class PPPoESessionLayer : public PPPoELayer { + public: + /** + * A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data (will be casted to @ref + * pppoe_header) + * @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 + */ + PPPoESessionLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet) + : PPPoELayer(data, dataLen, prevLayer, packet) { + m_Protocol = PPPoESession; + } + + /** + * A constructor that allocates a new PPPoE Session header with version, type + * and session ID + * @param[in] version PPPoE version + * @param[in] type PPPoE type + * @param[in] sessionId PPPoE session ID + * @param[in] pppNextProtocol The next protocol to come after the PPPoE + * session header. Should be one of the PPP_* macros listed below + */ + PPPoESessionLayer(uint8_t version, uint8_t type, uint16_t sessionId, + uint16_t pppNextProtocol) + : PPPoELayer(version, type, PPPoELayer::PPPOE_CODE_SESSION, sessionId, + sizeof(uint16_t)) { + setPPPNextProtocol(pppNextProtocol); + } + + virtual ~PPPoESessionLayer() {} + + /** + * @return The protocol after the PPPoE session header. The return value is + * one of the PPP_* macros listed below. This method is also used when parsing + * a packet (this way we know which layer comes after the PPPoE session) + */ + uint16_t getPPPNextProtocol() const; + + /** + * Set the field that describes which header comes after the PPPoE session + * header + * @param[in] nextProtocol The protocol value. Should be one of the PPP_* + * macros listed below + */ + void setPPPNextProtocol(uint16_t nextProtocol); + + /** + * A static method that validates the input data + * @param[in] data The pointer to the beginning of byte stream of a packet + * @param[in] dataLen The length of the byte stream + * @return True if the data is valid and can represent a PPPoES packet + */ + static inline bool isDataValid(const uint8_t* data, size_t dataLen); + + // abstract methods implementation + + /** + * Currently identifies the following next layers: IPv4Layer, IPv6Layer. + * Otherwise sets PayloadLayer + */ + virtual void parseNextLayer(); + + /** + * @return Size of @ref pppoe_header + */ + virtual size_t getHeaderLen() const { + return sizeof(pppoe_header) + sizeof(uint16_t); + } + + virtual std::string toString() const; +}; + +/** + * @class PPPoEDiscoveryLayer + * Describes the PPPoE discovery protocol + */ +class PPPoEDiscoveryLayer : public PPPoELayer { + public: + /** + * PPPoE tag types + */ + enum PPPoETagTypes { + /** End-Of-List tag type*/ + PPPOE_TAG_EOL = 0x0000, + /** Service-Name tag type*/ + PPPOE_TAG_SVC_NAME = 0x0101, + /** AC-Name tag type*/ + PPPOE_TAG_AC_NAME = 0x0102, + /** Host-Uniq tag type*/ + PPPOE_TAG_HOST_UNIQ = 0x0103, + /** AC-Cookie tag type*/ + PPPOE_TAG_AC_COOKIE = 0x0104, + /** Vendor-Specific tag type*/ + PPPOE_TAG_VENDOR = 0x0105, + /** Credits tag type*/ + PPPOE_TAG_CREDITS = 0x0106, + /** Metrics tag type*/ + PPPOE_TAG_METRICS = 0x0107, + /** Sequence Number tag type */ + PPPOE_TAG_SEQ_NUM = 0x0108, + /** Credit Scale Factor tag type */ + PPPOE_TAG_CRED_SCALE = 0x0109, + /** Relay-Session-Id tag type */ + PPPOE_TAG_RELAY_ID = 0x0110, + /** HURL tag type */ + PPPOE_TAG_HURL = 0x0111, + /** MOTM tag type */ + PPPOE_TAG_MOTM = 0x0112, + /** PPP-Max-Payload tag type */ + PPPOE_TAG_MAX_PAYLD = 0x0120, + /** IP_Route_Add tag type */ + PPPOE_TAG_IP_RT_ADD = 0x0121, + /** Service-Name-Error tag type */ + PPPOE_TAG_SVC_ERR = 0x0201, + /** AC-System-Error tag type */ + PPPOE_TAG_AC_ERR = 0x0202, + /** Generic-Error tag type */ + PPPOE_TAG_GENERIC_ERR = 0x0203 + }; + + /** + * @class PPPoETag + * Represents a PPPoE tag and its data + */ + class PPPoETag : public TLVRecord { + public: + /** + * A c'tor that gets a pointer to the tag raw data (byte array) + * @param[in] tagRawData A pointer to the tag raw data + */ + explicit PPPoETag(uint8_t* tagRawData) : TLVRecord(tagRawData) {} + + /** + * A d'tor for this class, currently does nothing + */ + virtual ~PPPoETag() {} + + /** + * @return The tag type converted to PPPoEDiscoveryLayer#PPPoETagTypes enum + */ + PPPoEDiscoveryLayer::PPPoETagTypes getType() const; + + /** + * Retrieve the tag data as string. Relevant only if the tag value is indeed + * a string + * @return The tag data as string + */ + std::string getValueAsString() const { + size_t dataSize = getDataSize(); + if (dataSize < 1) + return ""; + + return std::string((const char*)m_Data->recordValue, dataSize); + } + + // implement abstract methods + + size_t getTotalSize() const; + + size_t getDataSize() const; + }; + + /** + * @class PPPoETagBuilder + * A class for building PPPoE Tags. This builder receives the tag parameters + * in its c'tor, builds the PPPoE Tag raw buffer and provides a build() method + * to get a PPPoETag object out of it + */ + class PPPoETagBuilder : public TLVRecordBuilder { + public: + /** + * A c'tor for building a PPPoE Tag which has no value (tag len is zero). + * The PPPoETag object can later be retrieved by calling build() + * @param[in] tagType Tag type + */ + explicit PPPoETagBuilder(PPPoETagTypes tagType) + : TLVRecordBuilder(static_cast(tagType), NULL, 0) {} + + /** + * A c'tor for building a PPPoE Tag which has a 4-byte value. The PPPoETag + * object can later be retrieved by calling build() + * @param[in] tagType Tag type + * @param[in] tagValue The tag's 4-byte value + */ + PPPoETagBuilder(PPPoETagTypes tagType, uint32_t tagValue) + : TLVRecordBuilder(static_cast(tagType), tagValue) {} + + /** + * A c'tor for building a PPPoE Tag which has some arbitrary value. The + * PPPoETag object can later be retrieved by calling build() + * @param[in] tagType Tag type + * @param[in] tagValue A byte array that contains the tag data + * @param[in] tagValueLen The length of the value byte array + */ + PPPoETagBuilder(PPPoETagTypes tagType, uint8_t* tagValue, + uint8_t tagValueLen) + : TLVRecordBuilder(static_cast(tagType), tagValue, + tagValueLen) {} + + /** + * Build the PPPoETag object out of the parameters defined in the c'tor + * @return The PPPoETag object + */ + PPPoETag build() const; + }; + + /** + * A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data (will be casted to @ref + * pppoe_header) + * @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 + */ + PPPoEDiscoveryLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet) + : PPPoELayer(data, dataLen, prevLayer, packet) { + m_Protocol = PPPoEDiscovery; + m_DataLen = getHeaderLen(); + } + + /** + * A constructor that allocates a new PPPoE Discovery header with version, + * type, PPPoE code and session ID + * @param[in] version PPPoE version + * @param[in] type PPPoE type + * @param[in] code PPPoE code enum + * @param[in] sessionId PPPoE session ID + */ + PPPoEDiscoveryLayer(uint8_t version, uint8_t type, PPPoELayer::PPPoECode code, + uint16_t sessionId) + : PPPoELayer(version, type, code, sessionId) { + m_Protocol = PPPoEDiscovery; + } + + /** + * Get a PPPoE Tag by tag type. + * @param[in] tagType The type of the tag to search + * @return A PPPoETag object that contains the first tag that matches this + * type, or logical NULL (PPPoETag#isNull() == true) if no such tag found + */ + PPPoETag getTag(PPPoEDiscoveryLayer::PPPoETagTypes tagType) const; + + /** + * @return The first tag in the PPPoE discovery layer. If the current layer + * contains no tags the returned value will contain a logical NULL + * (PPPoETag#isNull() == true) + */ + PPPoETag getFirstTag() const; + + /** + * Get the tag that comes right after the "tag" parameter. If the given tag is + * the last one, the returned value will contain a logical NULL + * (PPPoETag#isNull() == true) + * @param[in] tag A given tag + * @return A PPPoETag object containing the tag that comes next, or logical + * NULL if the given tag: (1) was the last one; (2) contains a logical NULL or + * (3) doesn't belong to this packet + */ + PPPoETag getNextTag(const PPPoETag& tag) const; + + /** + * @return The number of tags in this layer + */ + int getTagCount() const; + + /** + * Add a new PPPoE Tag at the end of the layer + * @param[in] tagBuilder A PPPoETagBuilder object that contains the requested + * tag data to add + * @return A PPPoETag object containing the newly added PPPoE Tag data or + * logical NULL (PPPoETag#isNull() == true) if addition failed + */ + PPPoETag addTag(const PPPoETagBuilder& tagBuilder); + + /** + * Add a new PPPoE Tag after an existing one + * @param[in] tagBuilder A PPPoETagBuilder object that contains the requested + * tag data to add + * @param[in] prevTagType The PPPoE Tag which the newly added tag will come + * after + * @return A PPPoETag object containing the newly added PPPoE Tag data or + * logical NULL (PPPoETag#isNull() == true) if addition failed + */ + PPPoETag addTagAfter(const PPPoETagBuilder& tagBuilder, + PPPoETagTypes prevTagType); + + /** + * Remove an existing tag. Tag will be found by the tag type + * @param[in] tagType The tag type to remove + * @return True if tag was removed or false if tag wasn't found or if tag + * removal failed (in each case a proper error will be written to log) + */ + bool removeTag(PPPoEDiscoveryLayer::PPPoETagTypes tagType); + + /** + * Remove all tags in this layer + * @return True if all tags were successfully or false if removal failed for + * some reason (a proper error will be written to log) + */ + bool removeAllTags(); + + /** + * A static method that validates the input data + * @param[in] data The pointer to the beginning of byte stream of a packet + * @param[in] dataLen The length of the byte stream + * @return True if the data is valid and can represent a PPPoED packet + */ + static inline bool isDataValid(const uint8_t* data, size_t dataLen); + + // abstract methods implementation + + /** + * Does nothing for this layer (PPPoE discovery is always the last layer) + */ + virtual void parseNextLayer() {} + + /** + * @return The header length which is size of strcut pppoe_header plus the + * total size of tags + */ + virtual size_t getHeaderLen() const; + + virtual std::string toString() const { + return "PPP-over-Ethernet Discovery (" + + codeToString((PPPoELayer::PPPoECode)getPPPoEHeader()->code) + ")"; + } + + private: + TLVRecordReader m_TagReader; + + PPPoETag addTagAt(const PPPoETagBuilder& tagBuilder, int offset); + + uint8_t* getTagBasePtr() const { return m_Data + sizeof(pppoe_header); } + + std::string codeToString(PPPoECode code) const; +}; + +// implementation of inline methods + +bool PPPoESessionLayer::isDataValid(const uint8_t* data, size_t dataLen) { + return data && dataLen >= sizeof(pppoe_header) + sizeof(uint16_t); +} + +bool PPPoEDiscoveryLayer::isDataValid(const uint8_t* data, size_t dataLen) { + return data && dataLen >= sizeof(pppoe_header); +} + +// Copied from Wireshark: ppptypes.h + +/** Padding Protocol */ +#define PCPP_PPP_PADDING 0x1 +/** ROHC small-CID */ +#define PCPP_PPP_ROHC_SCID 0x3 +/** ROHC large-CID */ +#define PCPP_PPP_ROHC_LCID 0x5 +/** Internet Protocol version 4 */ +#define PCPP_PPP_IP 0x21 +/** OSI Network Layer */ +#define PCPP_PPP_OSI 0x23 +/** Xerox NS IDP */ +#define PCPP_PPP_XNSIDP 0x25 +/** DECnet Phase IV */ +#define PCPP_PPP_DEC4 0x27 +/** AppleTalk */ +#define PCPP_PPP_AT 0x29 +/** Novell IPX */ +#define PCPP_PPP_IPX 0x2b +/** Van Jacobson Compressed TCP/IP */ +#define PCPP_PPP_VJC_COMP 0x2d +/** Van Jacobson Uncompressed TCP/IP */ +#define PCPP_PPP_VJC_UNCOMP 0x2f +/** Bridging PDU */ +#define PCPP_PPP_BCP 0x31 +/** Stream Protocol (ST-II) */ +#define PCPP_PPP_ST 0x33 +/** Banyan Vines */ +#define PCPP_PPP_VINES 0x35 +/** AppleTalk EDDP */ +#define PCPP_PPP_AT_EDDP 0x39 +/** AppleTalk SmartBuffered */ +#define PCPP_PPP_AT_SB 0x3b +/** Multi-Link */ +#define PCPP_PPP_MP 0x3d +/** NETBIOS Framing */ +#define PCPP_PPP_NB 0x3f +/** Cisco Systems */ +#define PCPP_PPP_CISCO 0x41 +/** Ascom Timeplex */ +#define PCPP_PPP_ASCOM 0x43 +/** Fujitsu Link Backup and Load Balancing */ +#define PCPP_PPP_LBLB 0x45 +/** DCA Remote Lan */ +#define PCPP_PPP_RL 0x47 +/** Serial Data Transport Protocol */ +#define PCPP_PPP_SDTP 0x49 +/** SNA over 802.2 */ +#define PCPP_PPP_LLC 0x4b +/** SNA */ +#define PCPP_PPP_SNA 0x4d +/** IPv6 Header Compression */ +#define PCPP_PPP_IPV6HC 0x4f +/** KNX Bridging Data */ +#define PCPP_PPP_KNX 0x51 +/** Encryption */ +#define PCPP_PPP_ENCRYPT 0x53 +/** Individual Link Encryption */ +#define PCPP_PPP_ILE 0x55 +/** Internet Protocol version 6 */ +#define PCPP_PPP_IPV6 0x57 +/** PPP Muxing */ +#define PCPP_PPP_MUX 0x59 +/** Vendor-Specific Network Protocol (VSNP) */ +#define PCPP_PPP_VSNP 0x5b +/** TRILL Network Protocol (TNP) */ +#define PCPP_PPP_TNP 0x5d +/** RTP IPHC Full Header */ +#define PCPP_PPP_RTP_FH 0x61 +/** RTP IPHC Compressed TCP */ +#define PCPP_PPP_RTP_CTCP 0x63 +/** RTP IPHC Compressed Non TCP */ +#define PCPP_PPP_RTP_CNTCP 0x65 +/** RTP IPHC Compressed UDP 8 */ +#define PCPP_PPP_RTP_CUDP8 0x67 +/** RTP IPHC Compressed RTP 8 */ +#define PCPP_PPP_RTP_CRTP8 0x69 +/** Stampede Bridging */ +#define PCPP_PPP_STAMPEDE 0x6f +/** MP+ Protocol */ +#define PCPP_PPP_MPPLUS 0x73 +/** NTCITS IPI */ +#define PCPP_PPP_NTCITS_IPI 0xc1 +/** Single link compression in multilink */ +#define PCPP_PPP_ML_SLCOMP 0xfb +/** Compressed datagram */ +#define PCPP_PPP_COMP 0xfd +/** 802.1d Hello Packets */ +#define PCPP_PPP_STP_HELLO 0x0201 +/** IBM Source Routing BPDU */ +#define PCPP_PPP_IBM_SR 0x0203 +/** DEC LANBridge100 Spanning Tree */ +#define PCPP_PPP_DEC_LB 0x0205 +/** Cisco Discovery Protocol */ +#define PCPP_PPP_CDP 0x0207 +/** Netcs Twin Routing */ +#define PCPP_PPP_NETCS 0x0209 +/** STP - Scheduled Transfer Protocol */ +#define PCPP_PPP_STP 0x020b +/** EDP - Extreme Discovery Protocol */ +#define PCPP_PPP_EDP 0x020d +/** Optical Supervisory Channel Protocol */ +#define PCPP_PPP_OSCP 0x0211 +/** Optical Supervisory Channel Protocol */ +#define PCPP_PPP_OSCP2 0x0213 +/** Luxcom */ +#define PCPP_PPP_LUXCOM 0x0231 +/** Sigma Network Systems */ +#define PCPP_PPP_SIGMA 0x0233 +/** Apple Client Server Protocol */ +#define PCPP_PPP_ACSP 0x0235 +/** MPLS Unicast */ +#define PCPP_PPP_MPLS_UNI 0x0281 +/** MPLS Multicast */ +#define PCPP_PPP_MPLS_MULTI 0x0283 +/** IEEE p1284.4 standard - data packets */ +#define PCPP_PPP_P12844 0x0285 +/** ETSI TETRA Network Protocol Type 1 */ +#define PCPP_PPP_TETRA 0x0287 +/** Multichannel Flow Treatment Protocol */ +#define PCPP_PPP_MFTP 0x0289 +/** RTP IPHC Compressed TCP No Delta */ +#define PCPP_PPP_RTP_CTCPND 0x2063 +/** RTP IPHC Context State */ +#define PCPP_PPP_RTP_CS 0x2065 +/** RTP IPHC Compressed UDP 16 */ +#define PCPP_PPP_RTP_CUDP16 0x2067 +/** RTP IPHC Compressed RTP 16 */ +#define PCPP_PPP_RTP_CRDP16 0x2069 +/** Cray Communications Control Protocol */ +#define PCPP_PPP_CCCP 0x4001 +/** CDPD Mobile Network Registration Protocol */ +#define PCPP_PPP_CDPD_MNRP 0x4003 +/** Expand accelerator protocol */ +#define PCPP_PPP_EXPANDAP 0x4005 +/** ODSICP NCP */ +#define PCPP_PPP_ODSICP 0x4007 +/** DOCSIS DLL */ +#define PCPP_PPP_DOCSIS 0x4009 +/** Cetacean Network Detection Protocol */ +#define PCPP_PPP_CETACEANNDP 0x400b +/** Stacker LZS */ +#define PCPP_PPP_LZS 0x4021 +/** RefTek Protocol */ +#define PCPP_PPP_REFTEK 0x4023 +/** Fibre Channel */ +#define PCPP_PPP_FC 0x4025 +/** EMIT Protocols */ +#define PCPP_PPP_EMIT 0x4027 +/** Vendor-Specific Protocol (VSP) */ +#define PCPP_PPP_VSP 0x405b +/** TRILL Link State Protocol (TLSP) */ +#define PCPP_PPP_TLSP 0x405d +/** Internet Protocol Control Protocol */ +#define PCPP_PPP_IPCP 0x8021 +/** OSI Network Layer Control Protocol */ +#define PCPP_PPP_OSINLCP 0x8023 +/** Xerox NS IDP Control Protocol */ +#define PCPP_PPP_XNSIDPCP 0x8025 +/** DECnet Phase IV Control Protocol */ +#define PCPP_PPP_DECNETCP 0x8027 +/** AppleTalk Control Protocol */ +#define PCPP_PPP_ATCP 0x8029 +/** Novell IPX Control Protocol */ +#define PCPP_PPP_IPXCP 0x802b +/** Bridging NCP */ +#define PCPP_PPP_BRIDGENCP 0x8031 +/** Stream Protocol Control Protocol */ +#define PCPP_PPP_SPCP 0x8033 +/** Banyan Vines Control Protocol */ +#define PCPP_PPP_BVCP 0x8035 +/** Multi-Link Control Protocol */ +#define PCPP_PPP_MLCP 0x803d +/** NETBIOS Framing Control Protocol */ +#define PCPP_PPP_NBCP 0x803f +/** Cisco Systems Control Protocol */ +#define PCPP_PPP_CISCOCP 0x8041 +/** Ascom Timeplex Control Protocol (?) */ +#define PCPP_PPP_ASCOMCP 0x8043 +/** Fujitsu LBLB Control Protocol */ +#define PCPP_PPP_LBLBCP 0x8045 +/** DCA Remote Lan Network Control Protocol */ +#define PCPP_PPP_RLNCP 0x8047 +/** Serial Data Control Protocol */ +#define PCPP_PPP_SDCP 0x8049 +/** SNA over 802.2 Control Protocol */ +#define PCPP_PPP_LLCCP 0x804b +/** SNA Control Protocol */ +#define PCPP_PPP_SNACP 0x804d +/** IP6 Header Compression Control Protocol */ +#define PCPP_PPP_IP6HCCP 0x804f +/** KNX Bridging Control Protocol */ +#define PCPP_PPP_KNXCP 0x8051 +/** Encryption Control Protocol */ +#define PCPP_PPP_ECP 0x8053 +/** Individual Link Encryption Control Protocol */ +#define PCPP_PPP_ILECP 0x8055 +/** IPv6 Control Protocol */ +#define PCPP_PPP_IPV6CP 0x8057 +/** PPP Muxing Control Protocol */ +#define PCPP_PPP_MUXCP 0x8059 +/** Vendor-Specific Network Control Protocol (VSNCP) [RFC3772] */ +#define PCPP_PPP_VSNCP 0x805b +/** TRILL Network Control Protocol (TNCP) */ +#define PCPP_PPP_TNCP 0x805d +/** Stampede Bridging Control Protocol */ +#define PCPP_PPP_STAMPEDECP 0x806f +/** MP+ Contorol Protocol */ +#define PCPP_PPP_MPPCP 0x8073 +/** NTCITS IPI Control Protocol */ +#define PCPP_PPP_IPICP 0x80c1 +/** Single link compression in multilink control */ +#define PCPP_PPP_SLCC 0x80fb +/** Compression Control Protocol */ +#define PCPP_PPP_CCP 0x80fd +/** Cisco Discovery Protocol Control Protocol */ +#define PCPP_PPP_CDPCP 0x8207 +/** Netcs Twin Routing */ +#define PCPP_PPP_NETCSCP 0x8209 +/** STP - Control Protocol */ +#define PCPP_PPP_STPCP 0x820b +/** EDPCP - Extreme Discovery Protocol Control Protocol */ +#define PCPP_PPP_EDPCP 0x820d +/** Apple Client Server Protocol Control */ +#define PCPP_PPP_ACSPC 0x8235 +/** MPLS Control Protocol */ +#define PCPP_PPP_MPLSCP 0x8281 +/** IEEE p1284.4 standard - Protocol Control */ +#define PCPP_PPP_P12844CP 0x8285 +/** ETSI TETRA TNP1 Control Protocol */ +#define PCPP_PPP_TETRACP 0x8287 +/** Multichannel Flow Treatment Protocol */ +#define PCPP_PPP_MFTPCP 0x8289 +/** Link Control Protocol */ +#define PCPP_PPP_LCP 0xc021 +/** Password Authentication Protocol */ +#define PCPP_PPP_PAP 0xc023 +/** Link Quality Report */ +#define PCPP_PPP_LQR 0xc025 +/** Shiva Password Authentication Protocol */ +#define PCPP_PPP_SPAP 0xc027 +/** CallBack Control Protocol (CBCP) */ +#define PCPP_PPP_CBCP 0xc029 +/** BACP Bandwidth Allocation Control Protocol */ +#define PCPP_PPP_BACP 0xc02b +/** BAP Bandwidth Allocation Protocol */ +#define PCPP_PPP_BAP 0xc02d +/** Vendor-Specific Authentication Protocol (VSAP) */ +#define PCPP_PPP_VSAP 0xc05b +/** Container Control Protocol */ +#define PCPP_PPP_CONTCP 0xc081 +/** Challenge Handshake Authentication Protocol */ +#define PCPP_PPP_CHAP 0xc223 +/** RSA Authentication Protocol */ +#define PCPP_PPP_RSAAP 0xc225 +/** Extensible Authentication Protocol */ +#define PCPP_PPP_EAP 0xc227 +/** Mitsubishi Security Information Exchange Protocol (SIEP) */ +#define PCPP_PPP_SIEP 0xc229 +/** Stampede Bridging Authorization Protocol */ +#define PCPP_PPP_SBAP 0xc26f +/** Proprietary Authentication Protocol */ +#define PCPP_PPP_PRPAP 0xc281 +/** Proprietary Authentication Protocol */ +#define PCPP_PPP_PRPAP2 0xc283 +/** Proprietary Node ID Authentication Protocol */ +#define PCPP_PPP_PRPNIAP 0xc481 } // namespace pcpp diff --git a/Packet++/header/Packet.h b/Packet++/header/Packet.h index 6e10e662b2..a8fabcbe5e 100644 --- a/Packet++/header/Packet.h +++ b/Packet++/header/Packet.h @@ -1,8 +1,8 @@ #ifndef PACKETPP_PACKET #define PACKETPP_PACKET -#include "RawPacket.h" #include "Layer.h" +#include "RawPacket.h" #include /// @file @@ -11,382 +11,465 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - - /** - * @class Packet - * This class represents a parsed packet. It contains the raw data (RawPacket instance), and a linked list of layers, each layer is a parsed - * protocol that this packet contains. The layers linked list is ordered where the first layer is the lowest in the packet (currently it's always - * Ethernet protocol as PcapPlusPlus supports only Ethernet packets), the next layer will be L2.5 or L3 (e.g VLAN, IPv4, IPv6, etc.), and so on. - * etc.), etc. The last layer in the linked list will be the highest in the packet. - * For example: for a standard HTTP request packet the layer will look like this: EthLayer -> IPv4Layer -> TcpLayer -> HttpRequestLayer
- * Packet instance isn't read only. The user can add or remove layers, update current layer, etc. - */ - class Packet - { - friend class Layer; - private: - RawPacket* m_RawPacket; - Layer* m_FirstLayer; - Layer* m_LastLayer; - uint64_t m_ProtocolTypes; - size_t m_MaxPacketLen; - bool m_FreeRawPacket; - bool m_CanReallocateData; - - public: - - /** - * A constructor for creating a new packet (with no layers). - * When using this constructor an empty raw buffer is allocated (with the size of maxPacketLen) and a new RawPacket is created - * @param[in] maxPacketLen The expected packet length in bytes - */ - explicit Packet(size_t maxPacketLen = 1); - - /** - * A constructor for creating a new packet with a buffer that is pre-allocated by the user. - * The packet is created empty (with no layers), which means the constructor doesn't parse the data in the buffer. - * Instead, all of the raw data of this packet it written to this buffer: whenever a layer is added, it's data is written to this buffer. - * The buffer isn't freed and it's content isn't erased when the packet object is deleted. - * This constructor is useful when you already have a memory buffer and you want to create packet data in it. - * @param[in] buffer A pointer to a pre-allocated memory buffer - * @param[in] bufferSize The size of the buffer - */ - Packet(uint8_t* buffer, size_t bufferSize); - - /** - * A constructor for creating a packet out of already allocated RawPacket. Very useful when parsing packets that came from the network. - * When using this constructor a pointer to the RawPacket is saved (data isn't copied) and the RawPacket is parsed, meaning all layers - * are created and linked to each other in the right order. In this overload of the constructor the user can specify whether to free - * the instance of raw packet when the Packet is free or not - * @param[in] rawPacket A pointer to the raw packet - * @param[in] freeRawPacket Optional parameter. A flag indicating if the destructor should also call the raw packet destructor or not. Default value is false - * @param[in] parseUntil Optional parameter. Parse the packet until you reach a certain protocol (inclusive). Can be useful for cases when you need to parse only up to a - * certain layer and want to avoid the performance impact and memory consumption of parsing the whole packet. Default value is ::UnknownProtocol which means don't take this - * parameter into account - * @param[in] parseUntilLayer Optional parameter. Parse the packet until you reach a certain layer in the OSI model (inclusive). Can be useful for cases when you need to - * parse only up to a certain OSI layer (for example transport layer) and want to avoid the performance impact and memory consumption of parsing the whole packet. - * Default value is ::OsiModelLayerUnknown which means don't take this parameter into account - */ - explicit Packet(RawPacket* rawPacket, bool freeRawPacket = false, ProtocolType parseUntil = UnknownProtocol, OsiModelLayer parseUntilLayer = OsiModelLayerUnknown); - - /** - * A constructor for creating a packet out of already allocated RawPacket. Very useful when parsing packets that came from the network. - * When using this constructor a pointer to the RawPacket is saved (data isn't copied) and the RawPacket is parsed, meaning all layers - * are created and linked to each other in the right order. In this overload of the constructor the user can specify whether to free - * the instance of raw packet when the Packet is free or not. This constructor should be used to parse the packet up to a certain layer - * @param[in] rawPacket A pointer to the raw packet - * @param[in] parseUntil Optional parameter. Parse the packet until you reach a certain protocol (inclusive). Can be useful for cases when you need to parse only up to a - * certain layer and want to avoid the performance impact and memory consumption of parsing the whole packet - */ - Packet(RawPacket* rawPacket, ProtocolType parseUntil); - - /** - * A constructor for creating a packet out of already allocated RawPacket. Very useful when parsing packets that came from the network. - * When using this constructor a pointer to the RawPacket is saved (data isn't copied) and the RawPacket is parsed, meaning all layers - * are created and linked to each other in the right order. In this overload of the constructor the user can specify whether to free - * the instance of raw packet when the Packet is free or not. . This constructor should be used to parse the packet up to a certain layer in the OSI model - * @param[in] rawPacket A pointer to the raw packet - * @param[in] parseUntilLayer Optional parameter. Parse the packet until you reach a certain layer in the OSI model (inclusive). Can be useful for cases when you need to - * parse only up to a certain OSI layer (for example transport layer) and want to avoid the performance impact and memory consumption of parsing the whole packet - */ - Packet(RawPacket* rawPacket, OsiModelLayer parseUntilLayer); - - /** - * A destructor for this class. Frees all layers allocated by this instance (Notice: it doesn't free layers that weren't allocated by this - * class, for example layers that were added by addLayer() or insertLayer() ). In addition it frees the raw packet if it was allocated by - * this instance (meaning if it was allocated by this instance constructor) - */ - virtual ~Packet() { destructPacketData(); } - - /** - * A copy constructor for this class. This copy constructor copies all the raw data and re-create all layers. So when the original Packet - * is being freed, no data will be lost in the copied instance - * @param[in] other The instance to copy from - */ - Packet(const Packet& other) { copyDataFrom(other); } - - /** - * Assignment operator overloading. It first frees all layers allocated by this instance (Notice: it doesn't free layers that weren't allocated by this - * class, for example layers that were added by addLayer() or insertLayer() ). In addition it frees the raw packet if it was allocated by - * this instance (meaning if it was allocated by this instance constructor). - * Afterwards it copies the data from the other packet in the same way used in the copy constructor. - * @param[in] other The instance to copy from - */ - Packet& operator=(const Packet& other); - - /** - * Get a pointer to the Packet's RawPacket - * @return A pointer to the Packet's RawPacket - */ - RawPacket* getRawPacket() const { return m_RawPacket; } - - /** - * Set a RawPacket and re-construct all packet layers - * @param[in] rawPacket Raw packet to set - * @param[in] freeRawPacket A flag indicating if the destructor should also call the raw packet destructor or not - * @param[in] parseUntil Parse the packet until it reaches this protocol. Can be useful for cases when you need to parse only up to a certain layer and want to avoid the - * performance impact and memory consumption of parsing the whole packet. Default value is ::UnknownProtocol which means don't take this parameter into account - * @param[in] parseUntilLayer Parse the packet until certain layer in OSI model. Can be useful for cases when you need to parse only up to a certain layer and want to avoid the - * performance impact and memory consumption of parsing the whole packet. Default value is ::OsiModelLayerUnknown which means don't take this parameter into account - */ - void setRawPacket(RawPacket* rawPacket, bool freeRawPacket, ProtocolType parseUntil = UnknownProtocol, OsiModelLayer parseUntilLayer = OsiModelLayerUnknown); - - /** - * Get a pointer to the Packet's RawPacket in a read-only manner - * @return A pointer to the Packet's RawPacket - */ - RawPacket* getRawPacketReadOnly() const { return m_RawPacket; } - - /** - * Get a pointer to the first (lowest) layer in the packet - * @return A pointer to the first (lowest) layer in the packet - */ - Layer* getFirstLayer() const { return m_FirstLayer; } - - /** - * Get a pointer to the last (highest) layer in the packet - * @return A pointer to the last (highest) layer in the packet - */ - Layer* getLastLayer() const { return m_LastLayer; } - - /** - * Add a new layer as the last layer in the packet. This method gets a pointer to the new layer as a parameter - * and attaches it to the packet. Notice after calling this method the input layer is attached to the packet so - * every change you make in it affect the packet; Also it cannot be attached to other packets - * @param[in] newLayer A pointer to the new layer to be added to the packet - * @param[in] ownInPacket If true, Packet fully owns newLayer, including memory deletion upon destruct. Default is false. - * @return True if everything went well or false otherwise (an appropriate error log message will be printed in - * such cases) - */ - bool addLayer(Layer* newLayer, bool ownInPacket = false) { return insertLayer(m_LastLayer, newLayer, ownInPacket); } - - /** - * Insert a new layer after an existing layer in the packet. This method gets a pointer to the new layer as a - * parameter and attaches it to the packet. Notice after calling this method the input layer is attached to the - * packet so every change you make in it affect the packet; Also it cannot be attached to other packets - * @param[in] prevLayer A pointer to an existing layer in the packet which the new layer should followed by. If - * this layer isn't attached to a packet and error will be printed to log and false will be returned - * @param[in] newLayer A pointer to the new layer to be added to the packet - * @param[in] ownInPacket If true, Packet fully owns newLayer, including memory deletion upon destruct. Default is false. - * @return True if everything went well or false otherwise (an appropriate error log message will be printed in - * such cases) - */ - bool insertLayer(Layer* prevLayer, Layer* newLayer, bool ownInPacket = false); - - - /** - * Remove an existing layer from the packet. The layer to removed is identified by its type (protocol). If the - * packet has multiple layers of the same type in the packet the user may specify the index of the layer to remove - * (the default index is 0 - remove the first layer of this type). If the layer was allocated during packet creation - * it will be deleted and any pointer to it will get invalid. However if the layer was allocated by the user and - * manually added to the packet it will simply get detached from the packet, meaning the pointer to it will stay - * valid and its data (that was removed from the packet) will be copied back to the layer. In that case it's - * the user's responsibility to delete the layer instance - * @param[in] layerType The layer type (protocol) to remove - * @param[in] index If there are multiple layers of the same type, indicate which instance to remove. The default - * value is 0, meaning remove the first layer of this type - * @return True if everything went well or false otherwise (an appropriate error log message will be printed in - * such cases) - */ - bool removeLayer(ProtocolType layerType, int index = 0); - - /** - * Remove the first layer in the packet. The layer will be deleted if it was allocated during packet creation, or detached - * if was allocated outside of the packet. Please refer to removeLayer() to get more info - * @return True if layer removed successfully, or false if removing the layer failed or if there are no layers in the - * packet. In any case of failure an appropriate error log message will be printed - */ - bool removeFirstLayer(); - - /** - * Remove the last layer in the packet. The layer will be deleted if it was allocated during packet creation, or detached - * if was allocated outside of the packet. Please refer to removeLayer() to get more info - * @return True if layer removed successfully, or false if removing the layer failed or if there are no layers in the - * packet. In any case of failure an appropriate error log message will be printed - */ - bool removeLastLayer(); - - /** - * Remove all layers that come after a certain layer. All layers removed will be deleted if they were allocated during - * packet creation or detached if were allocated outside of the packet, please refer to removeLayer() to get more info - * @param[in] layer A pointer to the layer to begin removing from. Please note this layer will not be removed, only the - * layers that come after it will be removed. Also, if removal of one layer failed, the method will return immediately and - * the following layers won't be deleted - * @return True if all layers were removed successfully, or false if failed to remove at least one layer. In any case of - * failure an appropriate error log message will be printed - */ - bool removeAllLayersAfter(Layer* layer); - - /** - * Detach a layer from the packet. Detaching means the layer instance will not be deleted, but rather separated from the - * packet - e.g it will be removed from the layer chain of the packet and its data will be copied from the packet buffer - * into an internal layer buffer. After a layer is detached, it can be added into another packet (but it's impossible to - * attach a layer to multiple packets in the same time). After layer is detached, it's the user's responsibility to - * delete it when it's not needed anymore - * @param[in] layerType The layer type (protocol) to detach from the packet - * @param[in] index If there are multiple layers of the same type, indicate which instance to detach. The default - * value is 0, meaning detach the first layer of this type - * @return A pointer to the detached layer or NULL if detaching process failed. In any case of failure an - * appropriate error log message will be printed - */ - Layer* detachLayer(ProtocolType layerType, int index = 0); - - /** - * Detach a layer from the packet. Detaching means the layer instance will not be deleted, but rather separated from the - * packet - e.g it will be removed from the layer chain of the packet and its data will be copied from the packet buffer - * into an internal layer buffer. After a layer is detached, it can be added into another packet (but it's impossible to - * attach a layer to multiple packets at the same time). After layer is detached, it's the user's responsibility to - * delete it when it's not needed anymore - * @param[in] layer A pointer to the layer to detach - * @return True if the layer was detached successfully, or false if something went wrong. In any case of failure an - * appropriate error log message will be printed - */ - bool detachLayer(Layer* layer) { return removeLayer(layer, false); } - - /** - * Get a pointer to the layer of a certain type (protocol). This method goes through the layers and returns a layer - * that matches the give protocol type - * @param[in] layerType The layer type (protocol) to fetch - * @param[in] index If there are multiple layers of the same type, indicate which instance to fetch. The default - * value is 0, meaning fetch the first layer of this type - * @return A pointer to the layer or NULL if no such layer was found - */ - Layer* getLayerOfType(ProtocolType layerType, int index = 0) const; - - /** - * A templated method to get a layer of a certain type (protocol). If no layer of such type is found, NULL is returned - * @param[in] reverseOrder The optional parameter that indicates that the lookup should run in reverse order, the default value is false - * @return A pointer to the layer of the requested type, NULL if not found - */ - template - TLayer* getLayerOfType(bool reverseOrder = false) const; - - /** - * A templated method to get the first layer of a certain type (protocol), start searching from a certain layer. - * For example: if a packet looks like: EthLayer -> VlanLayer(1) -> VlanLayer(2) -> VlanLayer(3) -> IPv4Layer - * and the user put VlanLayer(2) as a parameter and wishes to search for a VlanLayer, VlanLayer(3) will be returned - * If no layer of such type is found, NULL is returned - * @param[in] startLayer A pointer to the layer to start search from - * @return A pointer to the layer of the requested type, NULL if not found - */ - template - TLayer* getNextLayerOfType(Layer* startLayer) const; - - /** - * A templated method to get the first layer of a certain type (protocol), start searching from a certain layer. - * For example: if a packet looks like: EthLayer -> VlanLayer(1) -> VlanLayer(2) -> VlanLayer(3) -> IPv4Layer - * and the user put VlanLayer(2) as a parameter and wishes to search for a VlanLayer, VlanLayer(1) will be returned - * If no layer of such type is found, NULL is returned - * @param[in] startLayer A pointer to the layer to start search from - * @return A pointer to the layer of the requested type, NULL if not found - */ - template - TLayer* getPrevLayerOfType(Layer* startLayer) const; - - /** - * Check whether the packet contains a certain protocol - * @param[in] protocolType The protocol type to search - * @return True if the packet contains the protocol, false otherwise - */ - bool isPacketOfType(ProtocolType protocolType) const { return m_ProtocolTypes & protocolType; } - - /** - * Each layer can have fields that can be calculate automatically from other fields using Layer#computeCalculateFields(). This method forces all layers to calculate these - * fields values - */ - void computeCalculateFields(); - - /** - * Each layer can print a string representation of the layer most important data using Layer#toString(). This method aggregates this string from all layers and - * print it to a complete string containing all packet's relevant data - * @param[in] timeAsLocalTime Print time as local time or GMT. Default (true value) is local time, for GMT set to false - * @return A string containing most relevant data from all layers (looks like the packet description in Wireshark) - */ - std::string toString(bool timeAsLocalTime = true) const; - - /** - * Similar to toString(), but instead of one string it outputs a list of strings, one string for every layer - * @param[out] result A string vector that will contain all strings - * @param[in] timeAsLocalTime Print time as local time or GMT. Default (true value) is local time, for GMT set to false - */ - void toStringList(std::vector& result, bool timeAsLocalTime = true) const; - - private: - void copyDataFrom(const Packet& other); - - void destructPacketData(); - - bool extendLayer(Layer* layer, int offsetInLayer, size_t numOfBytesToExtend); - bool shortenLayer(Layer* layer, int offsetInLayer, size_t numOfBytesToShorten); - - void reallocateRawData(size_t newSize); - - bool removeLayer(Layer* layer, bool tryToDelete); - - std::string printPacketInfo(bool timeAsLocalTime) const; - - Layer* createFirstLayer(LinkLayerType linkType); - }; // class Packet - - - // implementation of inline methods - - template - TLayer* Packet::getLayerOfType(bool reverse) const - { - if (!reverse) - { - if (dynamic_cast(getFirstLayer()) != NULL) - return dynamic_cast(getFirstLayer()); - - return getNextLayerOfType(getFirstLayer()); - } - - // lookup in reverse order - if (dynamic_cast(getLastLayer()) != NULL) - return dynamic_cast(getLastLayer()); - - return getPrevLayerOfType(getLastLayer()); - } - - template - TLayer* Packet::getNextLayerOfType(Layer* curLayer) const - { - if (curLayer == NULL) - return NULL; - - curLayer = curLayer->getNextLayer(); - while ((curLayer != NULL) && (dynamic_cast(curLayer) == NULL)) - { - curLayer = curLayer->getNextLayer(); - } - - return dynamic_cast(curLayer); - } - - template - TLayer* Packet::getPrevLayerOfType(Layer* curLayer) const - { - if (curLayer == NULL) - return NULL; - - curLayer = curLayer->getPrevLayer(); - while (curLayer != NULL && dynamic_cast(curLayer) == NULL) - { - curLayer = curLayer->getPrevLayer(); - } - - return dynamic_cast(curLayer); - } +namespace pcpp { + +/** + * @class Packet + * This class represents a parsed packet. It contains the raw data (RawPacket + * instance), and a linked list of layers, each layer is a parsed protocol that + * this packet contains. The layers linked list is ordered where the first layer + * is the lowest in the packet (currently it's always Ethernet protocol as + * PcapPlusPlus supports only Ethernet packets), the next layer will be L2.5 or + * L3 (e.g VLAN, IPv4, IPv6, etc.), and so on. etc.), etc. The last layer in the + * linked list will be the highest in the packet. For example: for a standard + * HTTP request packet the layer will look like this: EthLayer -> IPv4Layer -> + * TcpLayer -> HttpRequestLayer
Packet instance isn't read only. The user + * can add or remove layers, update current layer, etc. + */ +class Packet { + friend class Layer; + + private: + RawPacket* m_RawPacket; + Layer* m_FirstLayer; + Layer* m_LastLayer; + uint64_t m_ProtocolTypes; + size_t m_MaxPacketLen; + bool m_FreeRawPacket; + bool m_CanReallocateData; + + public: + /** + * A constructor for creating a new packet (with no layers). + * When using this constructor an empty raw buffer is allocated (with the size + * of maxPacketLen) and a new RawPacket is created + * @param[in] maxPacketLen The expected packet length in bytes + */ + explicit Packet(size_t maxPacketLen = 1); + + /** + * A constructor for creating a new packet with a buffer that is pre-allocated + * by the user. The packet is created empty (with no layers), which means the + * constructor doesn't parse the data in the buffer. Instead, all of the raw + * data of this packet it written to this buffer: whenever a layer is added, + * it's data is written to this buffer. The buffer isn't freed and it's + * content isn't erased when the packet object is deleted. This constructor is + * useful when you already have a memory buffer and you want to create packet + * data in it. + * @param[in] buffer A pointer to a pre-allocated memory buffer + * @param[in] bufferSize The size of the buffer + */ + Packet(uint8_t* buffer, size_t bufferSize); + + /** + * A constructor for creating a packet out of already allocated RawPacket. + * Very useful when parsing packets that came from the network. When using + * this constructor a pointer to the RawPacket is saved (data isn't copied) + * and the RawPacket is parsed, meaning all layers are created and linked to + * each other in the right order. In this overload of the constructor the user + * can specify whether to free the instance of raw packet when the Packet is + * free or not + * @param[in] rawPacket A pointer to the raw packet + * @param[in] freeRawPacket Optional parameter. A flag indicating if the + * destructor should also call the raw packet destructor or not. Default value + * is false + * @param[in] parseUntil Optional parameter. Parse the packet until you reach + * a certain protocol (inclusive). Can be useful for cases when you need to + * parse only up to a certain layer and want to avoid the performance impact + * and memory consumption of parsing the whole packet. Default value is + * ::UnknownProtocol which means don't take this parameter into account + * @param[in] parseUntilLayer Optional parameter. Parse the packet until you + * reach a certain layer in the OSI model (inclusive). Can be useful for cases + * when you need to parse only up to a certain OSI layer (for example + * transport layer) and want to avoid the performance impact and memory + * consumption of parsing the whole packet. Default value is + * ::OsiModelLayerUnknown which means don't take this parameter into account + */ + explicit Packet(RawPacket* rawPacket, bool freeRawPacket = false, + ProtocolType parseUntil = UnknownProtocol, + OsiModelLayer parseUntilLayer = OsiModelLayerUnknown); + + /** + * A constructor for creating a packet out of already allocated RawPacket. + * Very useful when parsing packets that came from the network. When using + * this constructor a pointer to the RawPacket is saved (data isn't copied) + * and the RawPacket is parsed, meaning all layers are created and linked to + * each other in the right order. In this overload of the constructor the user + * can specify whether to free the instance of raw packet when the Packet is + * free or not. This constructor should be used to parse the packet up to a + * certain layer + * @param[in] rawPacket A pointer to the raw packet + * @param[in] parseUntil Optional parameter. Parse the packet until you reach + * a certain protocol (inclusive). Can be useful for cases when you need to + * parse only up to a certain layer and want to avoid the performance impact + * and memory consumption of parsing the whole packet + */ + Packet(RawPacket* rawPacket, ProtocolType parseUntil); + + /** + * A constructor for creating a packet out of already allocated RawPacket. + * Very useful when parsing packets that came from the network. When using + * this constructor a pointer to the RawPacket is saved (data isn't copied) + * and the RawPacket is parsed, meaning all layers are created and linked to + * each other in the right order. In this overload of the constructor the user + * can specify whether to free the instance of raw packet when the Packet is + * free or not. . This constructor should be used to parse the packet up to a + * certain layer in the OSI model + * @param[in] rawPacket A pointer to the raw packet + * @param[in] parseUntilLayer Optional parameter. Parse the packet until you + * reach a certain layer in the OSI model (inclusive). Can be useful for cases + * when you need to parse only up to a certain OSI layer (for example + * transport layer) and want to avoid the performance impact and memory + * consumption of parsing the whole packet + */ + Packet(RawPacket* rawPacket, OsiModelLayer parseUntilLayer); + + /** + * A destructor for this class. Frees all layers allocated by this instance + * (Notice: it doesn't free layers that weren't allocated by this class, for + * example layers that were added by addLayer() or insertLayer() ). In + * addition it frees the raw packet if it was allocated by this instance + * (meaning if it was allocated by this instance constructor) + */ + virtual ~Packet() { destructPacketData(); } + + /** + * A copy constructor for this class. This copy constructor copies all the raw + * data and re-create all layers. So when the original Packet is being freed, + * no data will be lost in the copied instance + * @param[in] other The instance to copy from + */ + Packet(const Packet& other) { copyDataFrom(other); } + + /** + * Assignment operator overloading. It first frees all layers allocated by + * this instance (Notice: it doesn't free layers that weren't allocated by + * this class, for example layers that were added by addLayer() or + * insertLayer() ). In addition it frees the raw packet if it was allocated by + * this instance (meaning if it was allocated by this instance constructor). + * Afterwards it copies the data from the other packet in the same way used in + * the copy constructor. + * @param[in] other The instance to copy from + */ + Packet& operator=(const Packet& other); + + /** + * Get a pointer to the Packet's RawPacket + * @return A pointer to the Packet's RawPacket + */ + RawPacket* getRawPacket() const { return m_RawPacket; } + + /** + * Set a RawPacket and re-construct all packet layers + * @param[in] rawPacket Raw packet to set + * @param[in] freeRawPacket A flag indicating if the destructor should also + * call the raw packet destructor or not + * @param[in] parseUntil Parse the packet until it reaches this protocol. Can + * be useful for cases when you need to parse only up to a certain layer and + * want to avoid the performance impact and memory consumption of parsing the + * whole packet. Default value is ::UnknownProtocol which means don't take + * this parameter into account + * @param[in] parseUntilLayer Parse the packet until certain layer in OSI + * model. Can be useful for cases when you need to parse only up to a certain + * layer and want to avoid the performance impact and memory consumption of + * parsing the whole packet. Default value is ::OsiModelLayerUnknown which + * means don't take this parameter into account + */ + void setRawPacket(RawPacket* rawPacket, bool freeRawPacket, + ProtocolType parseUntil = UnknownProtocol, + OsiModelLayer parseUntilLayer = OsiModelLayerUnknown); + + /** + * Get a pointer to the Packet's RawPacket in a read-only manner + * @return A pointer to the Packet's RawPacket + */ + RawPacket* getRawPacketReadOnly() const { return m_RawPacket; } + + /** + * Get a pointer to the first (lowest) layer in the packet + * @return A pointer to the first (lowest) layer in the packet + */ + Layer* getFirstLayer() const { return m_FirstLayer; } + + /** + * Get a pointer to the last (highest) layer in the packet + * @return A pointer to the last (highest) layer in the packet + */ + Layer* getLastLayer() const { return m_LastLayer; } + + /** + * Add a new layer as the last layer in the packet. This method gets a pointer + * to the new layer as a parameter and attaches it to the packet. Notice after + * calling this method the input layer is attached to the packet so every + * change you make in it affect the packet; Also it cannot be attached to + * other packets + * @param[in] newLayer A pointer to the new layer to be added to the packet + * @param[in] ownInPacket If true, Packet fully owns newLayer, including + * memory deletion upon destruct. Default is false. + * @return True if everything went well or false otherwise (an appropriate + * error log message will be printed in such cases) + */ + bool addLayer(Layer* newLayer, bool ownInPacket = false) { + return insertLayer(m_LastLayer, newLayer, ownInPacket); + } + + /** + * Insert a new layer after an existing layer in the packet. This method gets + * a pointer to the new layer as a parameter and attaches it to the packet. + * Notice after calling this method the input layer is attached to the packet + * so every change you make in it affect the packet; Also it cannot be + * attached to other packets + * @param[in] prevLayer A pointer to an existing layer in the packet which the + * new layer should followed by. If this layer isn't attached to a packet and + * error will be printed to log and false will be returned + * @param[in] newLayer A pointer to the new layer to be added to the packet + * @param[in] ownInPacket If true, Packet fully owns newLayer, including + * memory deletion upon destruct. Default is false. + * @return True if everything went well or false otherwise (an appropriate + * error log message will be printed in such cases) + */ + bool insertLayer(Layer* prevLayer, Layer* newLayer, bool ownInPacket = false); + + /** + * Remove an existing layer from the packet. The layer to removed is + * identified by its type (protocol). If the packet has multiple layers of the + * same type in the packet the user may specify the index of the layer to + * remove (the default index is 0 - remove the first layer of this type). If + * the layer was allocated during packet creation it will be deleted and any + * pointer to it will get invalid. However if the layer was allocated by the + * user and manually added to the packet it will simply get detached from the + * packet, meaning the pointer to it will stay valid and its data (that was + * removed from the packet) will be copied back to the layer. In that case + * it's the user's responsibility to delete the layer instance + * @param[in] layerType The layer type (protocol) to remove + * @param[in] index If there are multiple layers of the same type, indicate + * which instance to remove. The default value is 0, meaning remove the first + * layer of this type + * @return True if everything went well or false otherwise (an appropriate + * error log message will be printed in such cases) + */ + bool removeLayer(ProtocolType layerType, int index = 0); + + /** + * Remove the first layer in the packet. The layer will be deleted if it was + * allocated during packet creation, or detached if was allocated outside of + * the packet. Please refer to removeLayer() to get more info + * @return True if layer removed successfully, or false if removing the layer + * failed or if there are no layers in the packet. In any case of failure an + * appropriate error log message will be printed + */ + bool removeFirstLayer(); + + /** + * Remove the last layer in the packet. The layer will be deleted if it was + * allocated during packet creation, or detached if was allocated outside of + * the packet. Please refer to removeLayer() to get more info + * @return True if layer removed successfully, or false if removing the layer + * failed or if there are no layers in the packet. In any case of failure an + * appropriate error log message will be printed + */ + bool removeLastLayer(); + + /** + * Remove all layers that come after a certain layer. All layers removed will + * be deleted if they were allocated during packet creation or detached if + * were allocated outside of the packet, please refer to removeLayer() to get + * more info + * @param[in] layer A pointer to the layer to begin removing from. Please note + * this layer will not be removed, only the layers that come after it will be + * removed. Also, if removal of one layer failed, the method will return + * immediately and the following layers won't be deleted + * @return True if all layers were removed successfully, or false if failed to + * remove at least one layer. In any case of failure an appropriate error log + * message will be printed + */ + bool removeAllLayersAfter(Layer* layer); + + /** + * Detach a layer from the packet. Detaching means the layer instance will not + * be deleted, but rather separated from the packet - e.g it will be removed + * from the layer chain of the packet and its data will be copied from the + * packet buffer into an internal layer buffer. After a layer is detached, it + * can be added into another packet (but it's impossible to attach a layer to + * multiple packets in the same time). After layer is detached, it's the + * user's responsibility to delete it when it's not needed anymore + * @param[in] layerType The layer type (protocol) to detach from the packet + * @param[in] index If there are multiple layers of the same type, indicate + * which instance to detach. The default value is 0, meaning detach the first + * layer of this type + * @return A pointer to the detached layer or NULL if detaching process + * failed. In any case of failure an appropriate error log message will be + * printed + */ + Layer* detachLayer(ProtocolType layerType, int index = 0); + + /** + * Detach a layer from the packet. Detaching means the layer instance will not + * be deleted, but rather separated from the packet - e.g it will be removed + * from the layer chain of the packet and its data will be copied from the + * packet buffer into an internal layer buffer. After a layer is detached, it + * can be added into another packet (but it's impossible to attach a layer to + * multiple packets at the same time). After layer is detached, it's the + * user's responsibility to delete it when it's not needed anymore + * @param[in] layer A pointer to the layer to detach + * @return True if the layer was detached successfully, or false if something + * went wrong. In any case of failure an appropriate error log message will be + * printed + */ + bool detachLayer(Layer* layer) { return removeLayer(layer, false); } + + /** + * Get a pointer to the layer of a certain type (protocol). This method goes + * through the layers and returns a layer that matches the give protocol type + * @param[in] layerType The layer type (protocol) to fetch + * @param[in] index If there are multiple layers of the same type, indicate + * which instance to fetch. The default value is 0, meaning fetch the first + * layer of this type + * @return A pointer to the layer or NULL if no such layer was found + */ + Layer* getLayerOfType(ProtocolType layerType, int index = 0) const; + + /** + * A templated method to get a layer of a certain type (protocol). If no layer + * of such type is found, NULL is returned + * @param[in] reverseOrder The optional parameter that indicates that the + * lookup should run in reverse order, the default value is false + * @return A pointer to the layer of the requested type, NULL if not found + */ + template + TLayer* getLayerOfType(bool reverseOrder = false) const; + + /** + * A templated method to get the first layer of a certain type (protocol), + * start searching from a certain layer. For example: if a packet looks like: + * EthLayer -> VlanLayer(1) -> VlanLayer(2) -> VlanLayer(3) -> IPv4Layer and + * the user put VlanLayer(2) as a parameter and wishes to search for a + * VlanLayer, VlanLayer(3) will be returned If no layer of such type is found, + * NULL is returned + * @param[in] startLayer A pointer to the layer to start search from + * @return A pointer to the layer of the requested type, NULL if not found + */ + template + TLayer* getNextLayerOfType(Layer* startLayer) const; + + /** + * A templated method to get the first layer of a certain type (protocol), + * start searching from a certain layer. For example: if a packet looks like: + * EthLayer -> VlanLayer(1) -> VlanLayer(2) -> VlanLayer(3) -> IPv4Layer and + * the user put VlanLayer(2) as a parameter and wishes to search for a + * VlanLayer, VlanLayer(1) will be returned If no layer of such type is found, + * NULL is returned + * @param[in] startLayer A pointer to the layer to start search from + * @return A pointer to the layer of the requested type, NULL if not found + */ + template + TLayer* getPrevLayerOfType(Layer* startLayer) const; + + /** + * Check whether the packet contains a certain protocol + * @param[in] protocolType The protocol type to search + * @return True if the packet contains the protocol, false otherwise + */ + bool isPacketOfType(ProtocolType protocolType) const { + return m_ProtocolTypes & protocolType; + } + + /** + * Each layer can have fields that can be calculate automatically from other + * fields using Layer#computeCalculateFields(). This method forces all layers + * to calculate these fields values + */ + void computeCalculateFields(); + + /** + * Each layer can print a string representation of the layer most important + * data using Layer#toString(). This method aggregates this string from all + * layers and print it to a complete string containing all packet's relevant + * data + * @param[in] timeAsLocalTime Print time as local time or GMT. Default (true + * value) is local time, for GMT set to false + * @return A string containing most relevant data from all layers (looks like + * the packet description in Wireshark) + */ + std::string toString(bool timeAsLocalTime = true) const; + + /** + * Similar to toString(), but instead of one string it outputs a list of + * strings, one string for every layer + * @param[out] result A string vector that will contain all strings + * @param[in] timeAsLocalTime Print time as local time or GMT. Default (true + * value) is local time, for GMT set to false + */ + void toStringList(std::vector& result, + bool timeAsLocalTime = true) const; + + private: + void copyDataFrom(const Packet& other); + + void destructPacketData(); + + bool extendLayer(Layer* layer, int offsetInLayer, size_t numOfBytesToExtend); + bool shortenLayer(Layer* layer, int offsetInLayer, + size_t numOfBytesToShorten); + + void reallocateRawData(size_t newSize); + + bool removeLayer(Layer* layer, bool tryToDelete); + + std::string printPacketInfo(bool timeAsLocalTime) const; + + Layer* createFirstLayer(LinkLayerType linkType); +}; // class Packet + +// implementation of inline methods + +template +TLayer* Packet::getLayerOfType(bool reverse) const { + if (!reverse) { + if (dynamic_cast(getFirstLayer()) != NULL) + return dynamic_cast(getFirstLayer()); + + return getNextLayerOfType(getFirstLayer()); + } + + // lookup in reverse order + if (dynamic_cast(getLastLayer()) != NULL) + return dynamic_cast(getLastLayer()); + + return getPrevLayerOfType(getLastLayer()); +} + +template +TLayer* Packet::getNextLayerOfType(Layer* curLayer) const { + if (curLayer == NULL) + return NULL; + + curLayer = curLayer->getNextLayer(); + while ((curLayer != NULL) && (dynamic_cast(curLayer) == NULL)) { + curLayer = curLayer->getNextLayer(); + } + + return dynamic_cast(curLayer); +} + +template +TLayer* Packet::getPrevLayerOfType(Layer* curLayer) const { + if (curLayer == NULL) + return NULL; + + curLayer = curLayer->getPrevLayer(); + while (curLayer != NULL && dynamic_cast(curLayer) == NULL) { + curLayer = curLayer->getPrevLayer(); + } + + return dynamic_cast(curLayer); +} } // namespace pcpp -inline std::ostream& operator<<(std::ostream& os, const pcpp::Packet& packet) -{ - os << packet.toString(); - return os; +inline std::ostream& operator<<(std::ostream& os, const pcpp::Packet& packet) { + os << packet.toString(); + return os; } #endif /* PACKETPP_PACKET */ diff --git a/Packet++/header/PacketTrailerLayer.h b/Packet++/header/PacketTrailerLayer.h index 12f12ddead..ad33db8c0c 100644 --- a/Packet++/header/PacketTrailerLayer.h +++ b/Packet++/header/PacketTrailerLayer.h @@ -5,80 +5,91 @@ #include "Layer.h" -namespace pcpp -{ - /** - * @class PacketTrailerLayer - * A class for representing packet tailer (a.k.a footer or padding) which refers to supplemental data placed at the end of a block of data - * being stored or transmitted, which may contain information for the handling of the data block, or just mark its end - * (taken from Wikipedia: https://en.wikipedia.org/wiki/Trailer_(computing) ) - * - * There are various reasons for adding a packet trailer, one of the most famous is FCS (Frame check sequence) which refers to the extra - * error-detecting code added to a frame. Another usage is padding which means adding data to reach a minimum required packet length. - * - * Although this layer inherits from the Layer class, it is not a standard layer in the sense that it can't be constructed by the user. - * This layer may be only be constructed in the Packet class, in the process of parsing the packet and creating the layers; if at the end - * of the parsing process there is data left that is not allocated to any layer, it's assumed to be the packet trailer and an instance of - * this class is created. This means this layer can only exist as the last layer in a packet, if a packet trailer indeed exists. - * - * No layer can be added by the user after this layer (trying to do that will result with an error). - * - * This layer can be removed by the user or extended/shortened, as any layer. - * - * It also contains method to extract the trailer data - */ - class PacketTrailerLayer : public Layer - { - public: - /** 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 - */ - PacketTrailerLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) { m_Protocol = PacketTrailer; } +namespace pcpp { +/** + * @class PacketTrailerLayer + * A class for representing packet tailer (a.k.a footer or padding) which refers + * to supplemental data placed at the end of a block of data being stored or + * transmitted, which may contain information for the handling of the data + * block, or just mark its end (taken from Wikipedia: + * https://en.wikipedia.org/wiki/Trailer_(computing) ) + * + * There are various reasons for adding a packet trailer, one of the most famous + * is FCS (Frame check sequence) which refers to the extra error-detecting code + * added to a frame. Another usage is padding which means adding data to reach a + * minimum required packet length. + * + * Although this layer inherits from the Layer class, it is not a standard layer + * in the sense that it can't be constructed by the user. This layer may be only + * be constructed in the Packet class, in the process of parsing the packet and + * creating the layers; if at the end of the parsing process there is data left + * that is not allocated to any layer, it's assumed to be the packet trailer and + * an instance of this class is created. This means this layer can only exist as + * the last layer in a packet, if a packet trailer indeed exists. + * + * No layer can be added by the user after this layer (trying to do that will + * result with an error). + * + * This layer can be removed by the user or extended/shortened, as any layer. + * + * It also contains method to extract the trailer data + */ +class PacketTrailerLayer : public Layer { + public: + /** 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 + */ + PacketTrailerLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet) + : Layer(data, dataLen, prevLayer, packet) { + m_Protocol = PacketTrailer; + } - ~PacketTrailerLayer() {} + ~PacketTrailerLayer() {} - /** - * Get a pointer to the trailer data - * @return A pointer to the trailer data - */ - uint8_t* getTrailerData() const { return m_Data; } + /** + * Get a pointer to the trailer data + * @return A pointer to the trailer data + */ + uint8_t* getTrailerData() const { return m_Data; } - /** - * @return Trailer data as hex string - */ - std::string getTrailerDataAsHexString() const; + /** + * @return Trailer data as hex string + */ + std::string getTrailerDataAsHexString() const; - /** - * Get the trailer data length - * @return The trailer data length in bytes - */ - size_t getTrailerLen() const { return m_DataLen; } + /** + * Get the trailer data length + * @return The trailer data length in bytes + */ + size_t getTrailerLen() const { return m_DataLen; } - // implement abstract methods + // implement abstract methods - /** - * Does nothing for this layer (PacketTrailerLayer is always last) - */ - void parseNextLayer() {} + /** + * Does nothing for this layer (PacketTrailerLayer is always last) + */ + void parseNextLayer() {} - /** - * @return trailer data length in bytes - */ - size_t getHeaderLen() const { return m_DataLen; } + /** + * @return trailer data length in bytes + */ + size_t getHeaderLen() const { return m_DataLen; } - /** - * Does nothing for this layer - */ - void computeCalculateFields() {} + /** + * Does nothing for this layer + */ + void computeCalculateFields() {} - std::string toString() const; + std::string toString() const; - OsiModelLayer getOsiModelLayer() const { return OsiModelDataLinkLayer; } - }; + OsiModelLayer getOsiModelLayer() const { return OsiModelDataLinkLayer; } +}; -} +} // namespace pcpp #endif // PACKETPP_PACKET_TRAILER_LAYER diff --git a/Packet++/header/PacketUtils.h b/Packet++/header/PacketUtils.h index 650b41f441..2a48ae0e58 100644 --- a/Packet++/header/PacketUtils.h +++ b/Packet++/header/PacketUtils.h @@ -1,8 +1,8 @@ #ifndef PACKETPP_PACKET_UTILS #define PACKETPP_PACKET_UTILS -#include "Packet.h" #include "IpAddress.h" +#include "Packet.h" /// @file @@ -10,81 +10,86 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - /** - * A struct that represent a single buffer - */ - template - struct ScalarBuffer - { - /** - * The pointer to the buffer - */ - T* buffer; +namespace pcpp { +/** + * A struct that represent a single buffer + */ +template +struct ScalarBuffer { + /** + * The pointer to the buffer + */ + T* buffer; - /** - * Buffer length - */ - size_t len; - }; + /** + * Buffer length + */ + size_t len; +}; - /** - * Computes the checksum for a vector of buffers - * @param[in] vec The vector of buffers - * @param[in] vecSize Number of ScalarBuffers in vector - * @return The checksum result - */ - uint16_t computeChecksum(ScalarBuffer vec[], size_t vecSize); +/** + * Computes the checksum for a vector of buffers + * @param[in] vec The vector of buffers + * @param[in] vecSize Number of ScalarBuffers in vector + * @return The checksum result + */ +uint16_t computeChecksum(ScalarBuffer vec[], size_t vecSize); - /** - * Computes the checksum for Pseudo header - * @param[in] dataPtr Data pointer - * @param[in] dataLen Data length - * @param[in] ipAddrType IP address type(IPv4/IPv6) type @ref IPAddress::AddressType - * @param[in] protocolType Current protocol type @ref IPProtocolTypes - * @param[in] srcIPAddress Source IP Address - * @param[in] dstIPAddress Destination IP Address - * @return The checksum result - */ - uint16_t computePseudoHdrChecksum(uint8_t *dataPtr, size_t dataLen, - IPAddress::AddressType ipAddrType, uint8_t protocolType, - IPAddress srcIPAddress, IPAddress dstIPAddress); +/** + * Computes the checksum for Pseudo header + * @param[in] dataPtr Data pointer + * @param[in] dataLen Data length + * @param[in] ipAddrType IP address type(IPv4/IPv6) type @ref + * IPAddress::AddressType + * @param[in] protocolType Current protocol type @ref IPProtocolTypes + * @param[in] srcIPAddress Source IP Address + * @param[in] dstIPAddress Destination IP Address + * @return The checksum result + */ +uint16_t computePseudoHdrChecksum(uint8_t* dataPtr, size_t dataLen, + IPAddress::AddressType ipAddrType, + uint8_t protocolType, IPAddress srcIPAddress, + IPAddress dstIPAddress); - /** - * Computes Fowler-Noll-Vo (FNV-1) 32bit hash function on an array of byte buffers. The hash is calculated on each - * byte in each byte buffer, as if all byte buffers were one long byte buffer - * @param[in] vec An array of byte buffers (ScalarBuffer of type uint8_t) - * @param[in] vecSize The length of vec - * @return The 32bit hash value - */ - uint32_t fnvHash(ScalarBuffer vec[], size_t vecSize); +/** + * Computes Fowler-Noll-Vo (FNV-1) 32bit hash function on an array of byte + * buffers. The hash is calculated on each byte in each byte buffer, as if all + * byte buffers were one long byte buffer + * @param[in] vec An array of byte buffers (ScalarBuffer of type uint8_t) + * @param[in] vecSize The length of vec + * @return The 32bit hash value + */ +uint32_t fnvHash(ScalarBuffer vec[], size_t vecSize); - /** - * Computes Fowler-Noll-Vo (FNV-1) 32bit hash function on a byte buffer - * @param[in] buffer The byte buffer - * @param[in] bufSize The size of the byte buffer - * @return The 32bit hash value - */ - uint32_t fnvHash(uint8_t* buffer, size_t bufSize); +/** + * Computes Fowler-Noll-Vo (FNV-1) 32bit hash function on a byte buffer + * @param[in] buffer The byte buffer + * @param[in] bufSize The size of the byte buffer + * @return The 32bit hash value + */ +uint32_t fnvHash(uint8_t* buffer, size_t bufSize); - /** - * A method that is given a packet and calculates a hash value by the packet's 5-tuple. Supports IPv4, IPv6, - * TCP and UDP. For packets which doesn't have 5-tuple (for example: packets which aren't IPv4/6 or aren't - * TCP/UDP) the value of 0 will be returned - * @param[in] packet The packet to calculate hash for - * @param[in] directionUnique Make hash value unique for each direction - * @return The hash value calculated for this packet or 0 if the packet doesn't contain 5-tuple - */ - uint32_t hash5Tuple(Packet* packet, bool const& directionUnique = false); +/** + * A method that is given a packet and calculates a hash value by the packet's + * 5-tuple. Supports IPv4, IPv6, TCP and UDP. For packets which doesn't have + * 5-tuple (for example: packets which aren't IPv4/6 or aren't TCP/UDP) the + * value of 0 will be returned + * @param[in] packet The packet to calculate hash for + * @param[in] directionUnique Make hash value unique for each direction + * @return The hash value calculated for this packet or 0 if the packet doesn't + * contain 5-tuple + */ +uint32_t hash5Tuple(Packet* packet, bool const& directionUnique = false); - /** - * A method that is given a packet and calculates a hash value by the packet's 2-tuple (IP src + IP dst). Supports - * IPv4 and IPv6. For packets which aren't IPv4/6 the value of 0 will be returned - * @param[in] packet The packet to calculate hash for - * @return The hash value calculated for this packet or 0 if the packet isn't IPv4/6 - */ - uint32_t hash2Tuple(Packet* packet); +/** + * A method that is given a packet and calculates a hash value by the packet's + * 2-tuple (IP src + IP dst). Supports IPv4 and IPv6. For packets which aren't + * IPv4/6 the value of 0 will be returned + * @param[in] packet The packet to calculate hash for + * @return The hash value calculated for this packet or 0 if the packet isn't + * IPv4/6 + */ +uint32_t hash2Tuple(Packet* packet); } // namespace pcpp diff --git a/Packet++/header/PayloadLayer.h b/Packet++/header/PayloadLayer.h index 3d8b89963f..c3ce5ff07b 100644 --- a/Packet++/header/PayloadLayer.h +++ b/Packet++/header/PayloadLayer.h @@ -9,85 +9,92 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - - /** - * @class PayloadLayer - * Represents a generic or unknown layer or a packet payload - */ - class PayloadLayer : public Layer - { - public: - /** 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 - */ - PayloadLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) { m_Protocol = GenericPayload; } - - /** - * A constructor that allocates a new payload - * @param[in] data A raw buffer that will be used as a payload. This data will be copied to the layer - * @param[in] dataLen The raw buffer length - * @param[in] dummy A dummy parameter to separate the constructor signature from the other constructor. Its value isn't used anywhere - * @todo dummy is probably not necessary anymore. Remove it - */ - PayloadLayer(const uint8_t* data, size_t dataLen, bool dummy); - - /** - * A constructor that allocates a new payload from an hex stream - * @param[in] payloadAsHexStream A string that represents an hex stream of the payload. For example: 0001080006040002842b2b774c56c0a80078000000000000c0a8. - * In order for the hex stream to be valid it has to contain valid hex chars only (which means, for example, that it can't begin with "0x") and it also has - * to have an even number of chars (each char represents one nibble). If the string is not a valid hex stream an error will be printed to log and the payload - * layer will be empty (no data) - */ - explicit PayloadLayer(const std::string& payloadAsHexStream); - - ~PayloadLayer() {} - - /** - * Get a pointer to the payload data - * @return A pointer to the payload data - */ - uint8_t* getPayload() const { return m_Data; } - - /** - * Get the payload data length - * @return The payload data length in bytes - */ - size_t getPayloadLen() const { return m_DataLen; } - - // implement abstract methods - - /** - * Does nothing for this layer (PayloadLayer is always last) - */ - void parseNextLayer() {} - - /** - * @return Payload data length in bytes - */ - size_t getHeaderLen() const { return m_DataLen; } - - /** - * Does nothing for this layer - */ - void computeCalculateFields() {} - - /** - * Sets the payload of the PayloadLayer to the given pointer. This will resize (extend/shorten) the underlying packet respectively if there is one. - * @param[in] newPayload New payload that shall be set - * @param[in] newPayloadLength New length of payload - */ - void setPayload(const uint8_t* newPayload, size_t newPayloadLength); - - std::string toString() const; - - OsiModelLayer getOsiModelLayer() const { return OsiModelApplicationLayer; } - - }; +namespace pcpp { + +/** + * @class PayloadLayer + * Represents a generic or unknown layer or a packet payload + */ +class PayloadLayer : public Layer { + public: + /** 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 + */ + PayloadLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : Layer(data, dataLen, prevLayer, packet) { + m_Protocol = GenericPayload; + } + + /** + * A constructor that allocates a new payload + * @param[in] data A raw buffer that will be used as a payload. This data will + * be copied to the layer + * @param[in] dataLen The raw buffer length + * @param[in] dummy A dummy parameter to separate the constructor signature + * from the other constructor. Its value isn't used anywhere + * @todo dummy is probably not necessary anymore. Remove it + */ + PayloadLayer(const uint8_t* data, size_t dataLen, bool dummy); + + /** + * A constructor that allocates a new payload from an hex stream + * @param[in] payloadAsHexStream A string that represents an hex stream of the + * payload. For example: 0001080006040002842b2b774c56c0a80078000000000000c0a8. + * In order for the hex stream to be valid it has to contain valid hex chars + * only (which means, for example, that it can't begin with "0x") and it also + * has to have an even number of chars (each char represents one nibble). If + * the string is not a valid hex stream an error will be printed to log and + * the payload layer will be empty (no data) + */ + explicit PayloadLayer(const std::string& payloadAsHexStream); + + ~PayloadLayer() {} + + /** + * Get a pointer to the payload data + * @return A pointer to the payload data + */ + uint8_t* getPayload() const { return m_Data; } + + /** + * Get the payload data length + * @return The payload data length in bytes + */ + size_t getPayloadLen() const { return m_DataLen; } + + // implement abstract methods + + /** + * Does nothing for this layer (PayloadLayer is always last) + */ + void parseNextLayer() {} + + /** + * @return Payload data length in bytes + */ + size_t getHeaderLen() const { return m_DataLen; } + + /** + * Does nothing for this layer + */ + void computeCalculateFields() {} + + /** + * Sets the payload of the PayloadLayer to the given pointer. This will resize + * (extend/shorten) the underlying packet respectively if there is one. + * @param[in] newPayload New payload that shall be set + * @param[in] newPayloadLength New length of payload + */ + void setPayload(const uint8_t* newPayload, size_t newPayloadLength); + + std::string toString() const; + + OsiModelLayer getOsiModelLayer() const { return OsiModelApplicationLayer; } +}; } // namespace pcpp diff --git a/Packet++/header/ProtocolType.h b/Packet++/header/ProtocolType.h index 31dc821ed2..5df4aea67e 100644 --- a/Packet++/header/ProtocolType.h +++ b/Packet++/header/ProtocolType.h @@ -9,352 +9,352 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - /** - * @typedef ProtocolType - * Representing all protocols supported by PcapPlusPlus - */ - typedef uint64_t ProtocolType; - - /** - * Unknown protocol (or unsupported by PcapPlusPlus) - */ - const ProtocolType UnknownProtocol = 0x00; - - /** - * Ethernet protocol - */ - const ProtocolType Ethernet = 0x01; - - /** - * IPv4 protocol - */ - const ProtocolType IPv4 = 0x02; - - /** - * IPv6 protocol - */ - const ProtocolType IPv6 = 0x04; - - /** - * IP protocol (aggregation bitmask of IPv4 and IPv6 protocols) - */ - const ProtocolType IP = 0x06; - - /** - * TCP protocol - */ - const ProtocolType TCP = 0x08; - - /** - * UDP protocol - */ - const ProtocolType UDP = 0x10; - - /** - * HTTP request protocol - */ - const ProtocolType HTTPRequest = 0x20; - - /** - * HTTP response protocol - */ - const ProtocolType HTTPResponse = 0x40; - - /** - * HTTP protocol (aggregation bitmask of HTTP request and HTTP response protocols) - */ - const ProtocolType HTTP = 0x60; - - /** - * ARP protocol - */ - const ProtocolType ARP = 0x80; - - /** - * VLAN protocol - */ - const ProtocolType VLAN = 0x100; - - /** - * ICMP protocol - */ - const ProtocolType ICMP = 0x200; - - /** - * PPPoE session protocol - */ - const ProtocolType PPPoESession = 0x400; - - /** - * PPPoE discovery protocol - */ - const ProtocolType PPPoEDiscovery = 0x800; - - /** - * PPPoE protocol (aggregation bitmask of PPPoESession and PPPoEDiscovery protocols) - */ - const ProtocolType PPPoE = 0xc00; - - /** - * DNS protocol - */ - const ProtocolType DNS = 0x1000; - - /** - * MPLS protocol - */ - const ProtocolType MPLS = 0x2000; - - /** - * GRE version 0 protocol - */ - const ProtocolType GREv0 = 0x4000; - - /** - * GRE version 1 protocol - */ - const ProtocolType GREv1 = 0x8000; - - /** - * GRE protocol (aggregation bitmask of GREv0 and GREv1 protocols) - */ - const ProtocolType GRE = 0xc000; - - /** - * PPP for PPTP protocol - */ - const ProtocolType PPP_PPTP = 0x10000; - - /** - * SSL/TLS protocol - */ - const ProtocolType SSL = 0x20000; - - /** - * SLL (Linux cooked capture) protocol - */ - const ProtocolType SLL = 0x40000; - - /** - * DHCP/BOOTP protocol - */ - const ProtocolType DHCP = 0x80000; - - /** - * Null/Loopback protocol - */ - const ProtocolType NULL_LOOPBACK = 0x100000; - - /** - * IGMP protocol - */ - const ProtocolType IGMP = 0xE00000; - - /** - * IGMPv1 protocol - */ - const ProtocolType IGMPv1 = 0x200000; - - /** - * IGMPv2 protocol - */ - const ProtocolType IGMPv2 = 0x400000; - - /** - * IGMPv3 protocol - */ - const ProtocolType IGMPv3 = 0x800000; - - /** - * Generic payload (no specific protocol) - */ - const ProtocolType GenericPayload = 0x1000000; - - /** - * VXLAN protocol - */ - const ProtocolType VXLAN = 0x2000000; - - /** - * SIP request protocol - */ - const ProtocolType SIPRequest = 0x4000000; - - /** - * SIP response protocol - */ - const ProtocolType SIPResponse = 0x8000000; - - /** - * SIP protocol (aggregation bitmask of SIPRequest and SIPResponse protocols) - */ - const ProtocolType SIP = 0xc000000; - - /** - * SDP protocol - */ - const ProtocolType SDP = 0x10000000; - - /** - * Packet trailer - */ - const ProtocolType PacketTrailer = 0x20000000; - - /** - * RADIUS protocol - */ - const ProtocolType Radius = 0x40000000; - - /** - * GTPv1 protocol - */ - const ProtocolType GTPv1 = 0x80000000; - - /** - * GTP protocol (currently the same as GTPv1) - */ - const ProtocolType GTP = 0x80000000; - - /** - * IEEE 802.3 Ethernet protocol - */ - const ProtocolType EthernetDot3 = 0x100000000; - - /** - * Border Gateway Protocol (BGP) version 4 protocol - */ - const ProtocolType BGP = 0x200000000; - - /** - * SSH version 2 protocol - */ - const ProtocolType SSH = 0x400000000; - - /** - * IPSec Authentication Header (AH) protocol - */ - const ProtocolType AuthenticationHeader = 0x800000000; - - /** - * IPSec Encapsulating Security Payload (ESP) protocol - */ - const ProtocolType ESP = 0x1000000000; - - /** - * IPSec protocol (aggregation bitmask of AH and ESP protocols) - */ - const ProtocolType IPSec = 0x1800000000; - - /** - * Dynamic Host Configuration Protocol version 6 (DHCPv6) protocol - */ - const ProtocolType DHCPv6 = 0x2000000000; - - /** - * Network Time (NTP) Protocol - */ - const ProtocolType NTP = 0x4000000000; - - /** - * Telnet Protocol - */ - const ProtocolType Telnet = 0x8000000000; - - /** - * File Transfer (FTP) Protocol - */ - const ProtocolType FTP = 0x10000000000; - - /** - * ICMPv6 protocol - */ - const ProtocolType ICMPv6 = 0x20000000000; - - /** - * Spanning Tree Protocol - */ - const ProtocolType STP = 0x40000000000; - - /** - * Logical Link Control (LLC) - */ - const ProtocolType LLC = 0x80000000000; - - /** - * SOME/IP Base protocol - */ - const ProtocolType SomeIP = 0x100000000000; - - /** - * Wake On LAN (WOL) Protocol - */ - const ProtocolType WakeOnLan = 0x200000000000; - - /** - * NFLOG (Linux Netfilter NFLOG) Protocol - */ - const ProtocolType NFLOG = 0x400000000000; - - /** - * TPKT protocol - */ - const ProtocolType TPKT = 0x800000000000; - - /** - * VRRP protocol - */ - const ProtocolType VRRP = 0x3000000000000; - - /** - * VRRP version 2 protocol - */ - const ProtocolType VRRPv2 = 0x1000000000000; - - /** - * VRRP version 3 protocol - */ - const ProtocolType VRRPv3 = 0x2000000000000; - - /** - * COTP protocol - */ - const ProtocolType COTP = 0x4000000000000; - - /** - * SLL2 protocol - */ - const ProtocolType SLL2 = 0x8000000000000; - - /** - * S7COMM protocol - */ - const ProtocolType S7COMM = 0x10000000000000; - - /** - * An enum representing OSI model layers - */ - enum OsiModelLayer - { - /** Physical layer (layer 1) */ - OsiModelPhysicalLayer = 1, - /** Data link layer (layer 2) */ - OsiModelDataLinkLayer = 2, - /** Network layer (layer 3) */ - OsiModelNetworkLayer = 3, - /** Transport layer (layer 4) */ - OsiModelTransportLayer = 4, - /** Session layer (layer 5) */ - OsiModelSesionLayer = 5, - /** Presentation layer (layer 6) */ - OsiModelPresentationLayer = 6, - /** Application layer (layer 7) */ - OsiModelApplicationLayer = 7, - /** Unknown / null layer */ - OsiModelLayerUnknown = 8 - }; - -} //namespace pcpp +namespace pcpp { +/** + * @typedef ProtocolType + * Representing all protocols supported by PcapPlusPlus + */ +typedef uint64_t ProtocolType; + +/** + * Unknown protocol (or unsupported by PcapPlusPlus) + */ +const ProtocolType UnknownProtocol = 0x00; + +/** + * Ethernet protocol + */ +const ProtocolType Ethernet = 0x01; + +/** + * IPv4 protocol + */ +const ProtocolType IPv4 = 0x02; + +/** + * IPv6 protocol + */ +const ProtocolType IPv6 = 0x04; + +/** + * IP protocol (aggregation bitmask of IPv4 and IPv6 protocols) + */ +const ProtocolType IP = 0x06; + +/** + * TCP protocol + */ +const ProtocolType TCP = 0x08; + +/** + * UDP protocol + */ +const ProtocolType UDP = 0x10; + +/** + * HTTP request protocol + */ +const ProtocolType HTTPRequest = 0x20; + +/** + * HTTP response protocol + */ +const ProtocolType HTTPResponse = 0x40; + +/** + * HTTP protocol (aggregation bitmask of HTTP request and HTTP response + * protocols) + */ +const ProtocolType HTTP = 0x60; + +/** + * ARP protocol + */ +const ProtocolType ARP = 0x80; + +/** + * VLAN protocol + */ +const ProtocolType VLAN = 0x100; + +/** + * ICMP protocol + */ +const ProtocolType ICMP = 0x200; + +/** + * PPPoE session protocol + */ +const ProtocolType PPPoESession = 0x400; + +/** + * PPPoE discovery protocol + */ +const ProtocolType PPPoEDiscovery = 0x800; + +/** + * PPPoE protocol (aggregation bitmask of PPPoESession and PPPoEDiscovery + * protocols) + */ +const ProtocolType PPPoE = 0xc00; + +/** + * DNS protocol + */ +const ProtocolType DNS = 0x1000; + +/** + * MPLS protocol + */ +const ProtocolType MPLS = 0x2000; + +/** + * GRE version 0 protocol + */ +const ProtocolType GREv0 = 0x4000; + +/** + * GRE version 1 protocol + */ +const ProtocolType GREv1 = 0x8000; + +/** + * GRE protocol (aggregation bitmask of GREv0 and GREv1 protocols) + */ +const ProtocolType GRE = 0xc000; + +/** + * PPP for PPTP protocol + */ +const ProtocolType PPP_PPTP = 0x10000; + +/** + * SSL/TLS protocol + */ +const ProtocolType SSL = 0x20000; + +/** + * SLL (Linux cooked capture) protocol + */ +const ProtocolType SLL = 0x40000; + +/** + * DHCP/BOOTP protocol + */ +const ProtocolType DHCP = 0x80000; + +/** + * Null/Loopback protocol + */ +const ProtocolType NULL_LOOPBACK = 0x100000; + +/** + * IGMP protocol + */ +const ProtocolType IGMP = 0xE00000; + +/** + * IGMPv1 protocol + */ +const ProtocolType IGMPv1 = 0x200000; + +/** + * IGMPv2 protocol + */ +const ProtocolType IGMPv2 = 0x400000; + +/** + * IGMPv3 protocol + */ +const ProtocolType IGMPv3 = 0x800000; + +/** + * Generic payload (no specific protocol) + */ +const ProtocolType GenericPayload = 0x1000000; + +/** + * VXLAN protocol + */ +const ProtocolType VXLAN = 0x2000000; + +/** + * SIP request protocol + */ +const ProtocolType SIPRequest = 0x4000000; + +/** + * SIP response protocol + */ +const ProtocolType SIPResponse = 0x8000000; + +/** + * SIP protocol (aggregation bitmask of SIPRequest and SIPResponse protocols) + */ +const ProtocolType SIP = 0xc000000; + +/** + * SDP protocol + */ +const ProtocolType SDP = 0x10000000; + +/** + * Packet trailer + */ +const ProtocolType PacketTrailer = 0x20000000; + +/** + * RADIUS protocol + */ +const ProtocolType Radius = 0x40000000; + +/** + * GTPv1 protocol + */ +const ProtocolType GTPv1 = 0x80000000; + +/** + * GTP protocol (currently the same as GTPv1) + */ +const ProtocolType GTP = 0x80000000; + +/** + * IEEE 802.3 Ethernet protocol + */ +const ProtocolType EthernetDot3 = 0x100000000; + +/** + * Border Gateway Protocol (BGP) version 4 protocol + */ +const ProtocolType BGP = 0x200000000; + +/** + * SSH version 2 protocol + */ +const ProtocolType SSH = 0x400000000; + +/** + * IPSec Authentication Header (AH) protocol + */ +const ProtocolType AuthenticationHeader = 0x800000000; + +/** + * IPSec Encapsulating Security Payload (ESP) protocol + */ +const ProtocolType ESP = 0x1000000000; + +/** + * IPSec protocol (aggregation bitmask of AH and ESP protocols) + */ +const ProtocolType IPSec = 0x1800000000; + +/** + * Dynamic Host Configuration Protocol version 6 (DHCPv6) protocol + */ +const ProtocolType DHCPv6 = 0x2000000000; + +/** + * Network Time (NTP) Protocol + */ +const ProtocolType NTP = 0x4000000000; + +/** + * Telnet Protocol + */ +const ProtocolType Telnet = 0x8000000000; + +/** + * File Transfer (FTP) Protocol + */ +const ProtocolType FTP = 0x10000000000; + +/** + * ICMPv6 protocol + */ +const ProtocolType ICMPv6 = 0x20000000000; + +/** + * Spanning Tree Protocol + */ +const ProtocolType STP = 0x40000000000; + +/** + * Logical Link Control (LLC) + */ +const ProtocolType LLC = 0x80000000000; + +/** + * SOME/IP Base protocol + */ +const ProtocolType SomeIP = 0x100000000000; + +/** + * Wake On LAN (WOL) Protocol + */ +const ProtocolType WakeOnLan = 0x200000000000; + +/** + * NFLOG (Linux Netfilter NFLOG) Protocol + */ +const ProtocolType NFLOG = 0x400000000000; + +/** + * TPKT protocol + */ +const ProtocolType TPKT = 0x800000000000; + +/** + * VRRP protocol + */ +const ProtocolType VRRP = 0x3000000000000; + +/** + * VRRP version 2 protocol + */ +const ProtocolType VRRPv2 = 0x1000000000000; + +/** + * VRRP version 3 protocol + */ +const ProtocolType VRRPv3 = 0x2000000000000; + +/** + * COTP protocol + */ +const ProtocolType COTP = 0x4000000000000; + +/** + * SLL2 protocol + */ +const ProtocolType SLL2 = 0x8000000000000; + +/** + * S7COMM protocol + */ +const ProtocolType S7COMM = 0x10000000000000; + +/** + * An enum representing OSI model layers + */ +enum OsiModelLayer { + /** Physical layer (layer 1) */ + OsiModelPhysicalLayer = 1, + /** Data link layer (layer 2) */ + OsiModelDataLinkLayer = 2, + /** Network layer (layer 3) */ + OsiModelNetworkLayer = 3, + /** Transport layer (layer 4) */ + OsiModelTransportLayer = 4, + /** Session layer (layer 5) */ + OsiModelSesionLayer = 5, + /** Presentation layer (layer 6) */ + OsiModelPresentationLayer = 6, + /** Application layer (layer 7) */ + OsiModelApplicationLayer = 7, + /** Unknown / null layer */ + OsiModelLayerUnknown = 8 +}; + +} // namespace pcpp #endif diff --git a/Packet++/header/RadiusLayer.h b/Packet++/header/RadiusLayer.h index 6759e2779b..bf418be552 100644 --- a/Packet++/header/RadiusLayer.h +++ b/Packet++/header/RadiusLayer.h @@ -10,345 +10,364 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ +namespace pcpp { - /** - * @struct radius_header - * Represents a RADIUS protocol header - */ +/** + * @struct radius_header + * Represents a RADIUS protocol header + */ #pragma pack(push, 1) - struct radius_header - { - /** RADIUS message code */ - uint8_t code; - /** RADIUS message ID */ - uint8_t id; - /** RADIUS message length */ - uint16_t length; - /** Used to authenticate the reply from the RADIUS server and to encrypt passwords */ - uint8_t authenticator[16]; - }; +struct radius_header { + /** RADIUS message code */ + uint8_t code; + /** RADIUS message ID */ + uint8_t id; + /** RADIUS message length */ + uint16_t length; + /** Used to authenticate the reply from the RADIUS server and to encrypt + * passwords */ + uint8_t authenticator[16]; +}; #pragma pack(pop) +/** + * @class RadiusAttribute + * A wrapper class for RADIUS attributes. This class does not create or modify + * RADIUS attribute records, but rather serves as a wrapper and provides useful + * methods for retrieving data from them + */ +class RadiusAttribute : public TLVRecord { + public: + /** + * A c'tor for this class that gets a pointer to the attribute raw data (byte + * array) + * @param[in] attrRawData A pointer to the attribute raw data + */ + explicit RadiusAttribute(uint8_t* attrRawData) : TLVRecord(attrRawData) {} + + /** + * A d'tor for this class, currently does nothing + */ + virtual ~RadiusAttribute() {} + + // implement abstract methods + + size_t getTotalSize() const { + if (m_Data == nullptr) + return 0; + + return (size_t)m_Data->recordLen; + } - /** - * @class RadiusAttribute - * A wrapper class for RADIUS attributes. This class does not create or modify RADIUS attribute records, but rather - * serves as a wrapper and provides useful methods for retrieving data from them - */ - class RadiusAttribute : public TLVRecord - { - public: - - /** - * A c'tor for this class that gets a pointer to the attribute raw data (byte array) - * @param[in] attrRawData A pointer to the attribute raw data - */ - explicit RadiusAttribute(uint8_t* attrRawData) : TLVRecord(attrRawData) { } - - /** - * A d'tor for this class, currently does nothing - */ - virtual ~RadiusAttribute() { } - - // implement abstract methods - - size_t getTotalSize() const - { - if (m_Data == nullptr) - return 0; - - return (size_t)m_Data->recordLen; - } - - size_t getDataSize() const - { - if (m_Data == nullptr) - return 0; - - return (size_t)m_Data->recordLen - 2*sizeof(uint8_t); - } - }; - - - /** - * @class RadiusAttributeBuilder - * A class for building RADIUS attributes. This builder receives the attribute parameters in its c'tor, - * builds the RADIUS attribute raw buffer and provides a build() method to get a RadiusAttribute object out of it - */ - class RadiusAttributeBuilder : public TLVRecordBuilder - { - public: - - /** - * A c'tor for building RADIUS attributes which their value is a byte array. The RadiusAttribute object can later - * be retrieved by calling build() - * @param[in] attrType RADIUS attribute type - * @param[in] attrValue A buffer containing the attribute value. This buffer is read-only and isn't modified in any way - * @param[in] attrValueLen Attribute value length in bytes - */ - RadiusAttributeBuilder(uint8_t attrType, const uint8_t* attrValue, uint8_t attrValueLen) : - TLVRecordBuilder(attrType, attrValue, attrValueLen) { } - - /** - * A c'tor for building RADIUS attributes which have a 1-byte value. The RadiusAttribute object can later be retrieved - * by calling build() - * @param[in] attrType RADIUS attribute type - * @param[in] attrValue A 1-byte attribute value - */ - RadiusAttributeBuilder(uint8_t attrType, uint8_t attrValue) : - TLVRecordBuilder(attrType, attrValue) { } - - /** - * A c'tor for building RADIUS attributes which have a 2-byte value. The RadiusAttribute object can later be retrieved - * by calling build() - * @param[in] attrType RADIUS attribute type - * @param[in] attrValue A 2-byte attribute value - */ - RadiusAttributeBuilder(uint8_t attrType, uint16_t attrValue) : - TLVRecordBuilder(attrType, attrValue) { } - - /** - * A c'tor for building RADIUS attributes which have a 4-byte value. The RadiusAttribute object can later be retrieved - * by calling build() - * @param[in] attrType RADIUS attribute type - * @param[in] attrValue A 4-byte attribute value - */ - RadiusAttributeBuilder(uint8_t attrType, uint32_t attrValue) : - TLVRecordBuilder(attrType, attrValue) { } - - /** - * A c'tor for building RADIUS attributes which have an IPv4Address value. The RadiusAttribute object can later be - * retrieved by calling build() - * @param[in] attrType RADIUS attribute type - * @param[in] attrValue The IPv4 address attribute value - */ - RadiusAttributeBuilder(uint8_t attrType, const IPv4Address& attrValue) : - TLVRecordBuilder(attrType, attrValue) { } - - /** - * A c'tor for building RADIUS attributes which have a string value. The RadiusAttribute object can later be retrieved - * by calling build() - * @param[in] attrType RADIUS attribute type - * @param[in] attrValue The string attribute value - */ - RadiusAttributeBuilder(uint8_t attrType, const std::string& attrValue) : - TLVRecordBuilder(attrType, attrValue) { } - - /** - * A copy c'tor which copies all the data from another instance of RadiusAttributeBuilder - * @param[in] other The instance to copy from - */ - RadiusAttributeBuilder(const RadiusAttributeBuilder& other) : - TLVRecordBuilder(other) { } - - /** - * Assignment operator that copies all data from another instance of RadiusAttributeBuilder - * @param[in] other The instance to assign from - */ - RadiusAttributeBuilder& operator=(const RadiusAttributeBuilder& other) - { - TLVRecordBuilder::operator=(other); - return *this; - } - - /** - * Build the RadiusAttribute object out of the parameters defined in the c'tor - * @return The RadiusAttribute object - */ - RadiusAttribute build() const; - }; - - - /** - * @class RadiusLayer - * Represents a RADIUS (Remote Authentication Dial-In User Service) protocol layer - */ - class RadiusLayer : public Layer - { - private: - - TLVRecordReader m_AttributeReader; - - uint8_t* getAttributesBasePtr() const { return m_Data + sizeof(radius_header); } - - RadiusAttribute addAttrAt(const RadiusAttributeBuilder& attrBuilder, int offset); - - public: - - /** - * 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 - */ - RadiusLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : - Layer(data, dataLen, prevLayer, packet) - { m_Protocol = Radius; } - - /** - * A constructor that creates a new layer from scratch - * @param[in] code The RADIUS message code - * @param[in] id The RADIUS message ID - * @param[in] authenticator A pointer to a byte array containing the authenticator value - * @param[in] authenticatorArrSize The authenticator byte array size. A valid size of the authenticator field is - * 16 bytes. If the provided size is less than that then the byte array will be copied to the packet but the missing - * bytes will stay zero. If the size is more than 16 bytes, only the first 16 bytes will be copied to the packet - */ - RadiusLayer(uint8_t code, uint8_t id, const uint8_t* authenticator, uint8_t authenticatorArrSize); - - /** - * A constructor that creates a new layer from scratch - * @param[in] code The RADIUS message code - * @param[in] id The RADIUS message ID - * @param[in] authenticator A hex string representing the authenticator value. A valid size of the authenticator - * field is 16 bytes. If the hex string represents an array that is smaller than this then the missing bytes in the - * packet's authenticator field will stay zero. If the hex string represents an array that is larger than 16 bytes, - * only the first 16 bytes will be copied to the packet - */ - RadiusLayer(uint8_t code, uint8_t id, const std::string &authenticator); - - /** - * A d'tor for this layer, currently does nothing - */ - ~RadiusLayer() {} - - /** - * Get a pointer to the RADIUS header. Notice this points directly to the data, so every change will change the actual packet data - * @return A pointer to the radius_header object - */ - radius_header* getRadiusHeader() const { return (radius_header*)m_Data; } - - /** - * @return A hex string representation of the radius_header#authenticator byte array value - */ - std::string getAuthenticatorValue() const; - - /** - * Setter for radius_header#authenticator - * @param[in] authValue A hex string representing the requested authenticator value - */ - void setAuthenticatorValue(const std::string& authValue); - - /** - * A static method that returns the RADIUS message string for a give message code. For example: the string - * "Access-Request" will be returned for code 1 - * @param[in] radiusMessageCode RADIUS message code - * @return RADIUS message string - */ - static std::string getRadiusMessageString(uint8_t radiusMessageCode); - - /** - * @return The first RADIUS attribute in the packet. If there are no attributes the returned value will contain - * a logical NULL (RadiusAttribute#isNull() == true) - */ - RadiusAttribute getFirstAttribute() const; - - /** - * Get the RADIUS attribute that comes after a given attribute. If the given attribute was the last one, the - * returned value will contain a logical NULL (RadiusAttribute#isNull() == true) - * @param[in] attr A given attribute - * @return A RadiusAttribute object containing the attribute data that comes next, or logical NULL if the given - * attribute: (1) was the last one; (2) contains a logical NULL or (3) doesn't belong to this packet - */ - RadiusAttribute getNextAttribute(RadiusAttribute& attr) const; - - /** - * Get a RADIUS attribute by attribute type - * @param[in] attrType RADIUS attribute type - * @return A RadiusAttribute object containing the first attribute data that matches this type, or logical NULL - * (RadiusAttribute#isNull() == true) if no such attribute found - */ - RadiusAttribute getAttribute(uint8_t attrType) const; - - /** - * @return The number of RADIUS attributes in the packet - */ - size_t getAttributeCount() const; - - /** - * Add a new RADIUS attribute at the end of the layer - * @param[in] attrBuilder A RadiusAttributeBuilder object that contains the requested attribute data to add - * @return A RadiusAttribute object containing the newly added RADIUS attribute data or logical NULL - * (RadiusAttribute#isNull() == true) if addition failed - */ - RadiusAttribute addAttribute(const RadiusAttributeBuilder& attrBuilder); - - /** - * Add a new RADIUS attribute after an existing one - * @param[in] attrBuilder A RadiusAttributeBuilder object that contains the requested attribute data to add - * @param[in] prevAttrType The RADIUS attribute which the newly added attribute will come after - * @return A RadiusAttribute object containing the newly added RADIUS attribute data or logical NULL - * (RadiusAttribute#isNull() == true) if addition failed - */ - RadiusAttribute addAttributeAfter(const RadiusAttributeBuilder& attrBuilder, uint8_t prevAttrType); - - /** - * Remove an existing RADIUS attribute from the layer - * @param[in] attrType The RADIUS attribute type to remove - * @return True if the RADIUS attribute was successfully removed or false if type wasn't found or if removal failed - */ - bool removeAttribute(uint8_t attrType); - - /** - * Remove all RADIUS attributes in this layer - * @return True if all attributes were successfully removed or false if removal failed for some reason - */ - bool removeAllAttributes(); - - /** - * The static method makes validation of UDP data - * @param[in] udpData The pointer to the UDP payload data. It points to the first byte of RADIUS header. - * @param[in] udpDataLen The payload data size - * @return True if the data is valid and can represent the RADIUS packet - */ - static bool isDataValid(const uint8_t* udpData, size_t udpDataLen); - - /** - * A static method that checks whether the port is considered as RADIUS - * @param[in] port The port number to be checked - */ - static inline bool isRadiusPort(uint16_t port); - - // implement abstract methods - - /** - * @return The size written in radius_header#length - */ - size_t getHeaderLen() const; - - /** - * Does nothing for this layer, RADIUS is always last - */ - void parseNextLayer() {} - - /** - * Calculate and store the value of radius_header#length according to the layer size - */ - void computeCalculateFields(); - - std::string toString() const; - - OsiModelLayer getOsiModelLayer() const { return OsiModelApplicationLayer; } - }; - - - // implementation of inline methods - - bool RadiusLayer::isRadiusPort(uint16_t port) - { - switch (port) - { - case 1812: - case 1813: - case 3799: - return true; - default: - return false; - } - } // isRadiusPort + size_t getDataSize() const { + if (m_Data == nullptr) + return 0; + + return (size_t)m_Data->recordLen - 2 * sizeof(uint8_t); + } +}; + +/** + * @class RadiusAttributeBuilder + * A class for building RADIUS attributes. This builder receives the attribute + * parameters in its c'tor, builds the RADIUS attribute raw buffer and provides + * a build() method to get a RadiusAttribute object out of it + */ +class RadiusAttributeBuilder : public TLVRecordBuilder { + public: + /** + * A c'tor for building RADIUS attributes which their value is a byte array. + * The RadiusAttribute object can later be retrieved by calling build() + * @param[in] attrType RADIUS attribute type + * @param[in] attrValue A buffer containing the attribute value. This buffer + * is read-only and isn't modified in any way + * @param[in] attrValueLen Attribute value length in bytes + */ + RadiusAttributeBuilder(uint8_t attrType, const uint8_t* attrValue, + uint8_t attrValueLen) + : TLVRecordBuilder(attrType, attrValue, attrValueLen) {} + + /** + * A c'tor for building RADIUS attributes which have a 1-byte value. The + * RadiusAttribute object can later be retrieved by calling build() + * @param[in] attrType RADIUS attribute type + * @param[in] attrValue A 1-byte attribute value + */ + RadiusAttributeBuilder(uint8_t attrType, uint8_t attrValue) + : TLVRecordBuilder(attrType, attrValue) {} + + /** + * A c'tor for building RADIUS attributes which have a 2-byte value. The + * RadiusAttribute object can later be retrieved by calling build() + * @param[in] attrType RADIUS attribute type + * @param[in] attrValue A 2-byte attribute value + */ + RadiusAttributeBuilder(uint8_t attrType, uint16_t attrValue) + : TLVRecordBuilder(attrType, attrValue) {} + + /** + * A c'tor for building RADIUS attributes which have a 4-byte value. The + * RadiusAttribute object can later be retrieved by calling build() + * @param[in] attrType RADIUS attribute type + * @param[in] attrValue A 4-byte attribute value + */ + RadiusAttributeBuilder(uint8_t attrType, uint32_t attrValue) + : TLVRecordBuilder(attrType, attrValue) {} + + /** + * A c'tor for building RADIUS attributes which have an IPv4Address value. The + * RadiusAttribute object can later be retrieved by calling build() + * @param[in] attrType RADIUS attribute type + * @param[in] attrValue The IPv4 address attribute value + */ + RadiusAttributeBuilder(uint8_t attrType, const IPv4Address& attrValue) + : TLVRecordBuilder(attrType, attrValue) {} + + /** + * A c'tor for building RADIUS attributes which have a string value. The + * RadiusAttribute object can later be retrieved by calling build() + * @param[in] attrType RADIUS attribute type + * @param[in] attrValue The string attribute value + */ + RadiusAttributeBuilder(uint8_t attrType, const std::string& attrValue) + : TLVRecordBuilder(attrType, attrValue) {} + + /** + * A copy c'tor which copies all the data from another instance of + * RadiusAttributeBuilder + * @param[in] other The instance to copy from + */ + RadiusAttributeBuilder(const RadiusAttributeBuilder& other) + : TLVRecordBuilder(other) {} + + /** + * Assignment operator that copies all data from another instance of + * RadiusAttributeBuilder + * @param[in] other The instance to assign from + */ + RadiusAttributeBuilder& operator=(const RadiusAttributeBuilder& other) { + TLVRecordBuilder::operator=(other); + return *this; + } + + /** + * Build the RadiusAttribute object out of the parameters defined in the c'tor + * @return The RadiusAttribute object + */ + RadiusAttribute build() const; +}; + +/** + * @class RadiusLayer + * Represents a RADIUS (Remote Authentication Dial-In User Service) protocol + * layer + */ +class RadiusLayer : public Layer { + private: + TLVRecordReader m_AttributeReader; + + uint8_t* getAttributesBasePtr() const { + return m_Data + sizeof(radius_header); + } + + RadiusAttribute addAttrAt(const RadiusAttributeBuilder& attrBuilder, + int offset); + + public: + /** + * 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 + */ + RadiusLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : Layer(data, dataLen, prevLayer, packet) { + m_Protocol = Radius; + } + + /** + * A constructor that creates a new layer from scratch + * @param[in] code The RADIUS message code + * @param[in] id The RADIUS message ID + * @param[in] authenticator A pointer to a byte array containing the + * authenticator value + * @param[in] authenticatorArrSize The authenticator byte array size. A valid + * size of the authenticator field is 16 bytes. If the provided size is less + * than that then the byte array will be copied to the packet but the missing + * bytes will stay zero. If the size is more than 16 bytes, only the first 16 + * bytes will be copied to the packet + */ + RadiusLayer(uint8_t code, uint8_t id, const uint8_t* authenticator, + uint8_t authenticatorArrSize); + + /** + * A constructor that creates a new layer from scratch + * @param[in] code The RADIUS message code + * @param[in] id The RADIUS message ID + * @param[in] authenticator A hex string representing the authenticator value. + * A valid size of the authenticator field is 16 bytes. If the hex string + * represents an array that is smaller than this then the missing bytes in the + * packet's authenticator field will stay zero. If the hex string represents + * an array that is larger than 16 bytes, only the first 16 bytes will be + * copied to the packet + */ + RadiusLayer(uint8_t code, uint8_t id, const std::string& authenticator); + + /** + * A d'tor for this layer, currently does nothing + */ + ~RadiusLayer() {} + + /** + * Get a pointer to the RADIUS header. Notice this points directly to the + * data, so every change will change the actual packet data + * @return A pointer to the radius_header object + */ + radius_header* getRadiusHeader() const { return (radius_header*)m_Data; } + + /** + * @return A hex string representation of the radius_header#authenticator byte + * array value + */ + std::string getAuthenticatorValue() const; + + /** + * Setter for radius_header#authenticator + * @param[in] authValue A hex string representing the requested authenticator + * value + */ + void setAuthenticatorValue(const std::string& authValue); + + /** + * A static method that returns the RADIUS message string for a give message + * code. For example: the string "Access-Request" will be returned for code 1 + * @param[in] radiusMessageCode RADIUS message code + * @return RADIUS message string + */ + static std::string getRadiusMessageString(uint8_t radiusMessageCode); + + /** + * @return The first RADIUS attribute in the packet. If there are no + * attributes the returned value will contain a logical NULL + * (RadiusAttribute#isNull() == true) + */ + RadiusAttribute getFirstAttribute() const; + + /** + * Get the RADIUS attribute that comes after a given attribute. If the given + * attribute was the last one, the returned value will contain a logical NULL + * (RadiusAttribute#isNull() == true) + * @param[in] attr A given attribute + * @return A RadiusAttribute object containing the attribute data that comes + * next, or logical NULL if the given attribute: (1) was the last one; (2) + * contains a logical NULL or (3) doesn't belong to this packet + */ + RadiusAttribute getNextAttribute(RadiusAttribute& attr) const; + + /** + * Get a RADIUS attribute by attribute type + * @param[in] attrType RADIUS attribute type + * @return A RadiusAttribute object containing the first attribute data that + * matches this type, or logical NULL (RadiusAttribute#isNull() == true) if no + * such attribute found + */ + RadiusAttribute getAttribute(uint8_t attrType) const; + + /** + * @return The number of RADIUS attributes in the packet + */ + size_t getAttributeCount() const; + + /** + * Add a new RADIUS attribute at the end of the layer + * @param[in] attrBuilder A RadiusAttributeBuilder object that contains the + * requested attribute data to add + * @return A RadiusAttribute object containing the newly added RADIUS + * attribute data or logical NULL (RadiusAttribute#isNull() == true) if + * addition failed + */ + RadiusAttribute addAttribute(const RadiusAttributeBuilder& attrBuilder); + + /** + * Add a new RADIUS attribute after an existing one + * @param[in] attrBuilder A RadiusAttributeBuilder object that contains the + * requested attribute data to add + * @param[in] prevAttrType The RADIUS attribute which the newly added + * attribute will come after + * @return A RadiusAttribute object containing the newly added RADIUS + * attribute data or logical NULL (RadiusAttribute#isNull() == true) if + * addition failed + */ + RadiusAttribute addAttributeAfter(const RadiusAttributeBuilder& attrBuilder, + uint8_t prevAttrType); + + /** + * Remove an existing RADIUS attribute from the layer + * @param[in] attrType The RADIUS attribute type to remove + * @return True if the RADIUS attribute was successfully removed or false if + * type wasn't found or if removal failed + */ + bool removeAttribute(uint8_t attrType); + + /** + * Remove all RADIUS attributes in this layer + * @return True if all attributes were successfully removed or false if + * removal failed for some reason + */ + bool removeAllAttributes(); + + /** + * The static method makes validation of UDP data + * @param[in] udpData The pointer to the UDP payload data. It points to the + * first byte of RADIUS header. + * @param[in] udpDataLen The payload data size + * @return True if the data is valid and can represent the RADIUS packet + */ + static bool isDataValid(const uint8_t* udpData, size_t udpDataLen); + + /** + * A static method that checks whether the port is considered as RADIUS + * @param[in] port The port number to be checked + */ + static inline bool isRadiusPort(uint16_t port); + + // implement abstract methods + + /** + * @return The size written in radius_header#length + */ + size_t getHeaderLen() const; + + /** + * Does nothing for this layer, RADIUS is always last + */ + void parseNextLayer() {} + + /** + * Calculate and store the value of radius_header#length according to the + * layer size + */ + void computeCalculateFields(); + + std::string toString() const; + + OsiModelLayer getOsiModelLayer() const { return OsiModelApplicationLayer; } +}; + +// implementation of inline methods + +bool RadiusLayer::isRadiusPort(uint16_t port) { + switch (port) { + case 1812: + case 1813: + case 3799: + return true; + default: + return false; + } +} // isRadiusPort } // namespace pcpp diff --git a/Packet++/header/RawPacket.h b/Packet++/header/RawPacket.h index 7df25657e2..08b232b786 100644 --- a/Packet++/header/RawPacket.h +++ b/Packet++/header/RawPacket.h @@ -16,453 +16,540 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ +namespace pcpp { - /** - * An enum describing all known link layer type. Taken from: http://www.tcpdump.org/linktypes.html . - */ - enum LinkLayerType - { - /** BSD loopback encapsulation */ - LINKTYPE_NULL = 0, - /** IEEE 802.3 Ethernet */ - LINKTYPE_ETHERNET = 1, - /** AX.25 packet */ - LINKTYPE_AX25 = 3, - /** IEEE 802.5 Token Ring */ - LINKTYPE_IEEE802_5 = 6, - /** ARCNET Data Packets */ - LINKTYPE_ARCNET_BSD = 7, - /** SLIP, encapsulated with a LINKTYPE_SLIP header */ - LINKTYPE_SLIP = 8, - /** PPP, as per RFC 1661 and RFC 1662 */ - LINKTYPE_PPP = 9, - /** FDDI, as specified by ANSI INCITS 239-1994 */ - LINKTYPE_FDDI = 10, - /** Raw IP */ - LINKTYPE_DLT_RAW1 = 12, - /** Raw IP (OpenBSD) */ - LINKTYPE_DLT_RAW2 = 14, - /** PPP in HDLC-like framing, as per RFC 1662, or Cisco PPP with HDLC framing, as per section 4.3.1 of RFC 1547 */ - LINKTYPE_PPP_HDLC = 50, - /** PPPoE */ - LINKTYPE_PPP_ETHER = 51, - /** RFC 1483 LLC/SNAP-encapsulated ATM */ - LINKTYPE_ATM_RFC1483 = 100, - /** Raw IP */ - LINKTYPE_RAW = 101, - /** Cisco PPP with HDLC framing */ - LINKTYPE_C_HDLC = 104, - /** IEEE 802.11 wireless LAN */ - LINKTYPE_IEEE802_11 = 105, - /** Frame Relay */ - LINKTYPE_FRELAY = 107, - /** OpenBSD loopback encapsulation */ - LINKTYPE_LOOP = 108, - /** Linux "cooked" capture encapsulation */ - LINKTYPE_LINUX_SLL = 113, - /** Apple LocalTalk */ - LINKTYPE_LTALK = 114, - /** OpenBSD pflog */ - LINKTYPE_PFLOG = 117, - /** Prism monitor mode information followed by an 802.11 header */ - LINKTYPE_IEEE802_11_PRISM = 119, - /** RFC 2625 IP-over-Fibre Channel */ - LINKTYPE_IP_OVER_FC = 122, - /** ATM traffic, encapsulated as per the scheme used by SunATM devices */ - LINKTYPE_SUNATM = 123, - /** Radiotap link-layer information followed by an 802.11 header */ - LINKTYPE_IEEE802_11_RADIOTAP = 127, - /** ARCNET Data Packets, as described by the ARCNET Trade Association standard ATA 878.1-1999 */ - LINKTYPE_ARCNET_LINUX = 129, - /** Apple IP-over-IEEE 1394 cooked header */ - LINKTYPE_APPLE_IP_OVER_IEEE1394 = 138, - /** Signaling System 7 Message Transfer Part Level 2 */ - LINKTYPE_MTP2_WITH_PHDR = 139, - /** Signaling System 7 Message Transfer Part Level 2 */ - LINKTYPE_MTP2 = 140, - /** Signaling System 7 Message Transfer Part Level 3 */ - LINKTYPE_MTP3 = 141, - /** Signaling System 7 Signalling Connection Control Part */ - LINKTYPE_SCCP = 142, - /** Signaling System 7 Signalling Connection Control Part */ - LINKTYPE_DOCSIS = 143, - /** Linux-IrDA packets */ - LINKTYPE_LINUX_IRDA = 144, - /** Reserved for private use */ - LINKTYPE_USER0 = 147, - /** Reserved for private use */ - LINKTYPE_USER1 = 148, - /** Reserved for private use */ - LINKTYPE_USER2 = 149, - /** Reserved for private use */ - LINKTYPE_USER3 = 150, - /** Reserved for private use */ - LINKTYPE_USER4 = 151, - /** Reserved for private use */ - LINKTYPE_USER5 = 152, - /** Reserved for private use */ - LINKTYPE_USER6 = 153, - /** Reserved for private use */ - LINKTYPE_USER7 = 154, - /** Reserved for private use */ - LINKTYPE_USER8 = 155, - /** Reserved for private use */ - LINKTYPE_USER9 = 156, - /** Reserved for private use */ - LINKTYPE_USER10 = 157, - /** Reserved for private use */ - LINKTYPE_USER11 = 158, - /** Reserved for private use */ - LINKTYPE_USER12 = 159, - /** Reserved for private use */ - LINKTYPE_USER13 = 160, - /** Reserved for private use */ - LINKTYPE_USER14 = 161, - /** Reserved for private use */ - LINKTYPE_USER15 = 162, - /** AVS monitor mode information followed by an 802.11 header */ - LINKTYPE_IEEE802_11_AVS = 163, - /** BACnet MS/TP frames */ - LINKTYPE_BACNET_MS_TP = 165, - /** PPP in HDLC-like encapsulation, like LINKTYPE_PPP_HDLC, but with the 0xff address byte replaced by a direction indication - 0x00 for incoming and 0x01 for outgoing */ - LINKTYPE_PPP_PPPD = 166, - /** General Packet Radio Service Logical Link Control */ - LINKTYPE_GPRS_LLC = 169, - /** Transparent-mapped generic framing procedure */ - LINKTYPE_GPF_T = 170, - /** Frame-mapped generic framing procedure */ - LINKTYPE_GPF_F = 171, - /** Link Access Procedures on the D Channel (LAPD) frames */ - LINKTYPE_LINUX_LAPD = 177, - /** Bluetooth HCI UART transport layer */ - LINKTYPE_BLUETOOTH_HCI_H4 = 187, - /** USB packets, beginning with a Linux USB header */ - LINKTYPE_USB_LINUX = 189, - /** Per-Packet Information information */ - LINKTYPE_PPI = 192, - /** IEEE 802.15.4 wireless Personal Area Network */ - LINKTYPE_IEEE802_15_4 = 195, - /** Various link-layer types, with a pseudo-header, for SITA */ - LINKTYPE_SITA = 196, - /** Various link-layer types, with a pseudo-header, for Endace DAG cards; encapsulates Endace ERF record */ - LINKTYPE_ERF = 197, - /** Bluetooth HCI UART transport layer */ - LINKTYPE_BLUETOOTH_HCI_H4_WITH_PHDR = 201, - /** AX.25 packet, with a 1-byte KISS header containing a type indicator */ - LINKTYPE_AX25_KISS = 202, - /** Link Access Procedures on the D Channel (LAPD) frames */ - LINKTYPE_LAPD = 203, - /** PPP, as per RFC 1661 and RFC 1662, preceded with a one-byte pseudo-header with a zero value meaning "received by this host" and a non-zero value meaning "sent by this host" */ - LINKTYPE_PPP_WITH_DIR = 204, - /** Cisco PPP with HDLC framing */ - LINKTYPE_C_HDLC_WITH_DIR = 205, - /** Frame Relay */ - LINKTYPE_FRELAY_WITH_DIR = 206, - /** IPMB over an I2C circuit */ - LINKTYPE_IPMB_LINUX = 209, - /** IEEE 802.15.4 wireless Personal Area Network */ - LINKTYPE_IEEE802_15_4_NONASK_PHY = 215, - /** USB packets, beginning with a Linux USB header */ - LINKTYPE_USB_LINUX_MMAPPED = 220, - /** Fibre Channel FC-2 frames, beginning with a Frame_Header */ - LINKTYPE_FC_2 = 224, - /** Fibre Channel FC-2 frames */ - LINKTYPE_FC_2_WITH_FRAME_DELIMS = 225, - /** Solaris ipnet pseudo-header */ - LINKTYPE_IPNET = 226, - /** CAN (Controller Area Network) frames, with a pseudo-header as supplied by Linux SocketCAN */ - LINKTYPE_CAN_SOCKETCAN = 227, - /** Raw IPv4; the packet begins with an IPv4 header */ - LINKTYPE_IPV4 = 228, - /** Raw IPv6; the packet begins with an IPv6 header */ - LINKTYPE_IPV6 = 229, - /** IEEE 802.15.4 wireless Personal Area Network, without the FCS at the end of the frame */ - LINKTYPE_IEEE802_15_4_NOFCS = 230, - /** Raw D-Bus messages, starting with the endianness flag, followed by the message type, etc., but without the authentication handshake before the message sequence */ - LINKTYPE_DBUS = 231, - /** DVB-CI (DVB Common Interface for communication between a PC Card module and a DVB receiver), with the message format specified by the PCAP format for DVB-CI specification */ - LINKTYPE_DVB_CI = 235, - /** Variant of 3GPP TS 27.010 multiplexing protocol (similar to, but not the same as, 27.010) */ - LINKTYPE_MUX27010 = 236, - /** D_PDUs as described by NATO standard STANAG 5066, starting with the synchronization sequence, and including both header and data CRCs */ - LINKTYPE_STANAG_5066_D_PDU = 237, - /** Linux netlink NETLINK NFLOG socket log messages */ - LINKTYPE_NFLOG = 239, - /** Pseudo-header for Hilscher Gesellschaft für Systemautomation mbH netANALYZER devices, followed by an Ethernet frame, beginning with the MAC header and ending with the FCS */ - LINKTYPE_NETANALYZER = 240, - /** Pseudo-header for Hilscher Gesellschaft für Systemautomation mbH netANALYZER devices, followed by an Ethernet frame, beginning with the preamble, SFD, and MAC header, and ending with the FCS */ - LINKTYPE_NETANALYZER_TRANSPARENT = 241, - /** IP-over-InfiniBand, as specified by RFC 4391 section 6 */ - LINKTYPE_IPOIB = 242, - /** MPEG-2 Transport Stream transport packets, as specified by ISO 13818-1/ITU-T Recommendation H.222.0 */ - LINKTYPE_MPEG_2_TS = 243, - /** Pseudo-header for ng4T GmbH's UMTS Iub/Iur-over-ATM and Iub/Iur-over-IP format as used by their ng40 protocol tester */ - LINKTYPE_NG40 = 244, - /** Pseudo-header for NFC LLCP packet captures, followed by frame data for the LLCP Protocol as specified by NFCForum-TS-LLCP_1.1 */ - LINKTYPE_NFC_LLCP = 245, - /** Raw InfiniBand frames, starting with the Local Routing Header */ - LINKTYPE_INFINIBAND = 247, - /** SCTP packets, as defined by RFC 4960, with no lower-level protocols such as IPv4 or IPv6 */ - LINKTYPE_SCTP = 248, - /** USB packets, beginning with a USBPcap header */ - LINKTYPE_USBPCAP = 249, - /** Serial-line packet header for the Schweitzer Engineering Laboratories "RTAC" product */ - LINKTYPE_RTAC_SERIAL = 250, - /** Bluetooth Low Energy air interface Link Layer packets */ - LINKTYPE_BLUETOOTH_LE_LL = 251, - /** Linux Netlink capture encapsulation */ - LINKTYPE_NETLINK = 253, - /** Bluetooth Linux Monitor encapsulation of traffic for the BlueZ stack */ - LINKTYPE_BLUETOOTH_LINUX_MONITOR = 254, - /** Bluetooth Basic Rate and Enhanced Data Rate baseband packets */ - LINKTYPE_BLUETOOTH_BREDR_BB = 255, - /** Bluetooth Low Energy link-layer packets */ - LINKTYPE_BLUETOOTH_LE_LL_WITH_PHDR = 256, - /** PROFIBUS data link layer packets, as specified by IEC standard 61158-6-3 */ - LINKTYPE_PROFIBUS_DL = 257, - /** Apple PKTAP capture encapsulation */ - LINKTYPE_PKTAP = 258, - /** Ethernet-over-passive-optical-network packets */ - LINKTYPE_EPON = 259, - /** IPMI trace packets, as specified by Table 3-20 "Trace Data Block Format" in the PICMG HPM.2 specification */ - LINKTYPE_IPMI_HPM_2 = 260, - /** Per Joshua Wright , formats for Z-Wave RF profiles R1 and R2 captures */ - LINKTYPE_ZWAVE_R1_R2 = 261, - /** Per Joshua Wright , formats for Z-Wave RF profile R3 captures */ - LINKTYPE_ZWAVE_R3 = 262, - /** Formats for WattStopper Digital Lighting Management (DLM) and Legrand Nitoo Open protocol common packet structure captures */ - LINKTYPE_WATTSTOPPER_DLM = 263, - /** Messages between ISO 14443 contactless smartcards (Proximity Integrated Circuit Card, PICC) and card readers (Proximity Coupling Device, PCD), with the message format specified by the PCAP format for ISO14443 specification */ - LINKTYPE_ISO_14443 = 264, - /** Linux "cooked" capture encapsulation v2 */ - LINKTYPE_LINUX_SLL2 = 276 - }; +/** + * An enum describing all known link layer type. Taken from: + * http://www.tcpdump.org/linktypes.html . + */ +enum LinkLayerType { + /** BSD loopback encapsulation */ + LINKTYPE_NULL = 0, + /** IEEE 802.3 Ethernet */ + LINKTYPE_ETHERNET = 1, + /** AX.25 packet */ + LINKTYPE_AX25 = 3, + /** IEEE 802.5 Token Ring */ + LINKTYPE_IEEE802_5 = 6, + /** ARCNET Data Packets */ + LINKTYPE_ARCNET_BSD = 7, + /** SLIP, encapsulated with a LINKTYPE_SLIP header */ + LINKTYPE_SLIP = 8, + /** PPP, as per RFC 1661 and RFC 1662 */ + LINKTYPE_PPP = 9, + /** FDDI, as specified by ANSI INCITS 239-1994 */ + LINKTYPE_FDDI = 10, + /** Raw IP */ + LINKTYPE_DLT_RAW1 = 12, + /** Raw IP (OpenBSD) */ + LINKTYPE_DLT_RAW2 = 14, + /** PPP in HDLC-like framing, as per RFC 1662, or Cisco PPP with HDLC framing, + as per section 4.3.1 of RFC 1547 */ + LINKTYPE_PPP_HDLC = 50, + /** PPPoE */ + LINKTYPE_PPP_ETHER = 51, + /** RFC 1483 LLC/SNAP-encapsulated ATM */ + LINKTYPE_ATM_RFC1483 = 100, + /** Raw IP */ + LINKTYPE_RAW = 101, + /** Cisco PPP with HDLC framing */ + LINKTYPE_C_HDLC = 104, + /** IEEE 802.11 wireless LAN */ + LINKTYPE_IEEE802_11 = 105, + /** Frame Relay */ + LINKTYPE_FRELAY = 107, + /** OpenBSD loopback encapsulation */ + LINKTYPE_LOOP = 108, + /** Linux "cooked" capture encapsulation */ + LINKTYPE_LINUX_SLL = 113, + /** Apple LocalTalk */ + LINKTYPE_LTALK = 114, + /** OpenBSD pflog */ + LINKTYPE_PFLOG = 117, + /** Prism monitor mode information followed by an 802.11 header */ + LINKTYPE_IEEE802_11_PRISM = 119, + /** RFC 2625 IP-over-Fibre Channel */ + LINKTYPE_IP_OVER_FC = 122, + /** ATM traffic, encapsulated as per the scheme used by SunATM devices */ + LINKTYPE_SUNATM = 123, + /** Radiotap link-layer information followed by an 802.11 header */ + LINKTYPE_IEEE802_11_RADIOTAP = 127, + /** ARCNET Data Packets, as described by the ARCNET Trade Association standard + ATA 878.1-1999 */ + LINKTYPE_ARCNET_LINUX = 129, + /** Apple IP-over-IEEE 1394 cooked header */ + LINKTYPE_APPLE_IP_OVER_IEEE1394 = 138, + /** Signaling System 7 Message Transfer Part Level 2 */ + LINKTYPE_MTP2_WITH_PHDR = 139, + /** Signaling System 7 Message Transfer Part Level 2 */ + LINKTYPE_MTP2 = 140, + /** Signaling System 7 Message Transfer Part Level 3 */ + LINKTYPE_MTP3 = 141, + /** Signaling System 7 Signalling Connection Control Part */ + LINKTYPE_SCCP = 142, + /** Signaling System 7 Signalling Connection Control Part */ + LINKTYPE_DOCSIS = 143, + /** Linux-IrDA packets */ + LINKTYPE_LINUX_IRDA = 144, + /** Reserved for private use */ + LINKTYPE_USER0 = 147, + /** Reserved for private use */ + LINKTYPE_USER1 = 148, + /** Reserved for private use */ + LINKTYPE_USER2 = 149, + /** Reserved for private use */ + LINKTYPE_USER3 = 150, + /** Reserved for private use */ + LINKTYPE_USER4 = 151, + /** Reserved for private use */ + LINKTYPE_USER5 = 152, + /** Reserved for private use */ + LINKTYPE_USER6 = 153, + /** Reserved for private use */ + LINKTYPE_USER7 = 154, + /** Reserved for private use */ + LINKTYPE_USER8 = 155, + /** Reserved for private use */ + LINKTYPE_USER9 = 156, + /** Reserved for private use */ + LINKTYPE_USER10 = 157, + /** Reserved for private use */ + LINKTYPE_USER11 = 158, + /** Reserved for private use */ + LINKTYPE_USER12 = 159, + /** Reserved for private use */ + LINKTYPE_USER13 = 160, + /** Reserved for private use */ + LINKTYPE_USER14 = 161, + /** Reserved for private use */ + LINKTYPE_USER15 = 162, + /** AVS monitor mode information followed by an 802.11 header */ + LINKTYPE_IEEE802_11_AVS = 163, + /** BACnet MS/TP frames */ + LINKTYPE_BACNET_MS_TP = 165, + /** PPP in HDLC-like encapsulation, like LINKTYPE_PPP_HDLC, but with the 0xff + address byte replaced by a direction indication - 0x00 for incoming and + 0x01 for outgoing */ + LINKTYPE_PPP_PPPD = 166, + /** General Packet Radio Service Logical Link Control */ + LINKTYPE_GPRS_LLC = 169, + /** Transparent-mapped generic framing procedure */ + LINKTYPE_GPF_T = 170, + /** Frame-mapped generic framing procedure */ + LINKTYPE_GPF_F = 171, + /** Link Access Procedures on the D Channel (LAPD) frames */ + LINKTYPE_LINUX_LAPD = 177, + /** Bluetooth HCI UART transport layer */ + LINKTYPE_BLUETOOTH_HCI_H4 = 187, + /** USB packets, beginning with a Linux USB header */ + LINKTYPE_USB_LINUX = 189, + /** Per-Packet Information information */ + LINKTYPE_PPI = 192, + /** IEEE 802.15.4 wireless Personal Area Network */ + LINKTYPE_IEEE802_15_4 = 195, + /** Various link-layer types, with a pseudo-header, for SITA */ + LINKTYPE_SITA = 196, + /** Various link-layer types, with a pseudo-header, for Endace DAG cards; + encapsulates Endace ERF record */ + LINKTYPE_ERF = 197, + /** Bluetooth HCI UART transport layer */ + LINKTYPE_BLUETOOTH_HCI_H4_WITH_PHDR = 201, + /** AX.25 packet, with a 1-byte KISS header containing a type indicator */ + LINKTYPE_AX25_KISS = 202, + /** Link Access Procedures on the D Channel (LAPD) frames */ + LINKTYPE_LAPD = 203, + /** PPP, as per RFC 1661 and RFC 1662, preceded with a one-byte pseudo-header + with a zero value meaning "received by this host" and a non-zero value + meaning "sent by this host" */ + LINKTYPE_PPP_WITH_DIR = 204, + /** Cisco PPP with HDLC framing */ + LINKTYPE_C_HDLC_WITH_DIR = 205, + /** Frame Relay */ + LINKTYPE_FRELAY_WITH_DIR = 206, + /** IPMB over an I2C circuit */ + LINKTYPE_IPMB_LINUX = 209, + /** IEEE 802.15.4 wireless Personal Area Network */ + LINKTYPE_IEEE802_15_4_NONASK_PHY = 215, + /** USB packets, beginning with a Linux USB header */ + LINKTYPE_USB_LINUX_MMAPPED = 220, + /** Fibre Channel FC-2 frames, beginning with a Frame_Header */ + LINKTYPE_FC_2 = 224, + /** Fibre Channel FC-2 frames */ + LINKTYPE_FC_2_WITH_FRAME_DELIMS = 225, + /** Solaris ipnet pseudo-header */ + LINKTYPE_IPNET = 226, + /** CAN (Controller Area Network) frames, with a pseudo-header as supplied by + Linux SocketCAN */ + LINKTYPE_CAN_SOCKETCAN = 227, + /** Raw IPv4; the packet begins with an IPv4 header */ + LINKTYPE_IPV4 = 228, + /** Raw IPv6; the packet begins with an IPv6 header */ + LINKTYPE_IPV6 = 229, + /** IEEE 802.15.4 wireless Personal Area Network, without the FCS at the end + of the frame */ + LINKTYPE_IEEE802_15_4_NOFCS = 230, + /** Raw D-Bus messages, starting with the endianness flag, followed by the + message type, etc., but without the authentication handshake before the + message sequence */ + LINKTYPE_DBUS = 231, + /** DVB-CI (DVB Common Interface for communication between a PC Card module + and a DVB receiver), with the message format specified by the PCAP format + for DVB-CI specification */ + LINKTYPE_DVB_CI = 235, + /** Variant of 3GPP TS 27.010 multiplexing protocol (similar to, but not the + same as, 27.010) */ + LINKTYPE_MUX27010 = 236, + /** D_PDUs as described by NATO standard STANAG 5066, starting with the + synchronization sequence, and including both header and data CRCs */ + LINKTYPE_STANAG_5066_D_PDU = 237, + /** Linux netlink NETLINK NFLOG socket log messages */ + LINKTYPE_NFLOG = 239, + /** Pseudo-header for Hilscher Gesellschaft für Systemautomation mbH + netANALYZER devices, followed by an Ethernet frame, beginning with the MAC + header and ending with the FCS */ + LINKTYPE_NETANALYZER = 240, + /** Pseudo-header for Hilscher Gesellschaft für Systemautomation mbH + netANALYZER devices, followed by an Ethernet frame, beginning with the + preamble, SFD, and MAC header, and ending with the FCS */ + LINKTYPE_NETANALYZER_TRANSPARENT = 241, + /** IP-over-InfiniBand, as specified by RFC 4391 section 6 */ + LINKTYPE_IPOIB = 242, + /** MPEG-2 Transport Stream transport packets, as specified by ISO + 13818-1/ITU-T Recommendation H.222.0 */ + LINKTYPE_MPEG_2_TS = 243, + /** Pseudo-header for ng4T GmbH's UMTS Iub/Iur-over-ATM and Iub/Iur-over-IP + format as used by their ng40 protocol tester */ + LINKTYPE_NG40 = 244, + /** Pseudo-header for NFC LLCP packet captures, followed by frame data for the + LLCP Protocol as specified by NFCForum-TS-LLCP_1.1 */ + LINKTYPE_NFC_LLCP = 245, + /** Raw InfiniBand frames, starting with the Local Routing Header */ + LINKTYPE_INFINIBAND = 247, + /** SCTP packets, as defined by RFC 4960, with no lower-level protocols such + as IPv4 or IPv6 */ + LINKTYPE_SCTP = 248, + /** USB packets, beginning with a USBPcap header */ + LINKTYPE_USBPCAP = 249, + /** Serial-line packet header for the Schweitzer Engineering Laboratories + "RTAC" product */ + LINKTYPE_RTAC_SERIAL = 250, + /** Bluetooth Low Energy air interface Link Layer packets */ + LINKTYPE_BLUETOOTH_LE_LL = 251, + /** Linux Netlink capture encapsulation */ + LINKTYPE_NETLINK = 253, + /** Bluetooth Linux Monitor encapsulation of traffic for the BlueZ stack */ + LINKTYPE_BLUETOOTH_LINUX_MONITOR = 254, + /** Bluetooth Basic Rate and Enhanced Data Rate baseband packets */ + LINKTYPE_BLUETOOTH_BREDR_BB = 255, + /** Bluetooth Low Energy link-layer packets */ + LINKTYPE_BLUETOOTH_LE_LL_WITH_PHDR = 256, + /** PROFIBUS data link layer packets, as specified by IEC standard 61158-6-3 + */ + LINKTYPE_PROFIBUS_DL = 257, + /** Apple PKTAP capture encapsulation */ + LINKTYPE_PKTAP = 258, + /** Ethernet-over-passive-optical-network packets */ + LINKTYPE_EPON = 259, + /** IPMI trace packets, as specified by Table 3-20 "Trace Data Block Format" + in the PICMG HPM.2 specification */ + LINKTYPE_IPMI_HPM_2 = 260, + /** Per Joshua Wright , formats for Z-Wave RF profiles R1 + and R2 captures */ + LINKTYPE_ZWAVE_R1_R2 = 261, + /** Per Joshua Wright , formats for Z-Wave RF profile R3 + captures */ + LINKTYPE_ZWAVE_R3 = 262, + /** Formats for WattStopper Digital Lighting Management (DLM) and Legrand + Nitoo Open protocol common packet structure captures */ + LINKTYPE_WATTSTOPPER_DLM = 263, + /** Messages between ISO 14443 contactless smartcards (Proximity Integrated + Circuit Card, PICC) and card readers (Proximity Coupling Device, PCD), with + the message format specified by the PCAP format for ISO14443 specification + */ + LINKTYPE_ISO_14443 = 264, + /** Linux "cooked" capture encapsulation v2 */ + LINKTYPE_LINUX_SLL2 = 276 +}; - /** - * Max packet size supported - */ +/** + * Max packet size supported + */ #define PCPP_MAX_PACKET_SIZE 65536 - /** - * @class RawPacket - * This class holds the packet as raw (not parsed) data. The data is held as byte array. In addition to the data itself - * every instance also holds a timestamp representing the time the packet was received by the NIC. - * RawPacket instance isn't read only. The user can change the packet data, add or remove data, etc. - */ - class RawPacket - { - protected: - uint8_t* m_RawData; - int m_RawDataLen; - int m_FrameLength; - timespec m_TimeStamp; - bool m_DeleteRawDataAtDestructor; - bool m_RawPacketSet; - LinkLayerType m_LinkLayerType; - void init(bool deleteRawDataAtDestructor = true); - void copyDataFrom(const RawPacket& other, bool allocateData = true); - public: - /** - * A constructor that receives a pointer to the raw data (allocated elsewhere). This constructor is usually used when packet - * is captured using a packet capturing engine (like libPcap. WinPcap, Npcap, PF_RING, etc.). The capturing engine allocates the raw data - * memory and give the user a pointer to it + a timestamp it has arrived to the device - * @param[in] pRawData A pointer to the raw data - * @param[in] rawDataLen The raw data length in bytes - * @param[in] timestamp The timestamp packet was received by the NIC (in usec precision) - * @param[in] deleteRawDataAtDestructor An indicator whether raw data pointer should be freed when the instance is freed or not. If set - * to 'true' than pRawData will be freed when instanced is being freed - * @param[in] layerType The link layer type of this raw packet. The default is Ethernet - */ - RawPacket(const uint8_t* pRawData, int rawDataLen, timeval timestamp, bool deleteRawDataAtDestructor, LinkLayerType layerType = LINKTYPE_ETHERNET); +/** + * @class RawPacket + * This class holds the packet as raw (not parsed) data. The data is held as + * byte array. In addition to the data itself every instance also holds a + * timestamp representing the time the packet was received by the NIC. RawPacket + * instance isn't read only. The user can change the packet data, add or remove + * data, etc. + */ +class RawPacket { + protected: + uint8_t* m_RawData; + int m_RawDataLen; + int m_FrameLength; + timespec m_TimeStamp; + bool m_DeleteRawDataAtDestructor; + bool m_RawPacketSet; + LinkLayerType m_LinkLayerType; + void init(bool deleteRawDataAtDestructor = true); + void copyDataFrom(const RawPacket& other, bool allocateData = true); + + public: + /** + * A constructor that receives a pointer to the raw data (allocated + * elsewhere). This constructor is usually used when packet is captured using + * a packet capturing engine (like libPcap. WinPcap, Npcap, PF_RING, etc.). + * The capturing engine allocates the raw data memory and give the user a + * pointer to it + a timestamp it has arrived to the device + * @param[in] pRawData A pointer to the raw data + * @param[in] rawDataLen The raw data length in bytes + * @param[in] timestamp The timestamp packet was received by the NIC (in usec + * precision) + * @param[in] deleteRawDataAtDestructor An indicator whether raw data pointer + * should be freed when the instance is freed or not. If set to 'true' than + * pRawData will be freed when instanced is being freed + * @param[in] layerType The link layer type of this raw packet. The default is + * Ethernet + */ + RawPacket(const uint8_t* pRawData, int rawDataLen, timeval timestamp, + bool deleteRawDataAtDestructor, + LinkLayerType layerType = LINKTYPE_ETHERNET); - /** - * A constructor that receives a pointer to the raw data (allocated elsewhere). This constructor is usually used when packet - * is captured using a packet capturing engine (like libPcap. WinPcap, Npcap, PF_RING, etc.). The capturing engine allocates the raw data - * memory and give the user a pointer to it + a timestamp it has arrived to the device - * @param[in] pRawData A pointer to the raw data - * @param[in] rawDataLen The raw data length in bytes - * @param[in] timestamp The timestamp packet was received by the NIC (in nsec precision) - * @param[in] deleteRawDataAtDestructor An indicator whether raw data pointer should be freed when the instance is freed or not. If set - * to 'true' than pRawData will be freed when instanced is being freed - * @param[in] layerType The link layer type of this raw packet. The default is Ethernet - */ - RawPacket(const uint8_t* pRawData, int rawDataLen, timespec timestamp, bool deleteRawDataAtDestructor, LinkLayerType layerType = LINKTYPE_ETHERNET); + /** + * A constructor that receives a pointer to the raw data (allocated + * elsewhere). This constructor is usually used when packet is captured using + * a packet capturing engine (like libPcap. WinPcap, Npcap, PF_RING, etc.). + * The capturing engine allocates the raw data memory and give the user a + * pointer to it + a timestamp it has arrived to the device + * @param[in] pRawData A pointer to the raw data + * @param[in] rawDataLen The raw data length in bytes + * @param[in] timestamp The timestamp packet was received by the NIC (in nsec + * precision) + * @param[in] deleteRawDataAtDestructor An indicator whether raw data pointer + * should be freed when the instance is freed or not. If set to 'true' than + * pRawData will be freed when instanced is being freed + * @param[in] layerType The link layer type of this raw packet. The default is + * Ethernet + */ + RawPacket(const uint8_t* pRawData, int rawDataLen, timespec timestamp, + bool deleteRawDataAtDestructor, + LinkLayerType layerType = LINKTYPE_ETHERNET); - /** - * A default constructor that initializes class'es attributes to default value: - * - data pointer is set to NULL - * - data length is set to 0 - * - deleteRawDataAtDestructor is set to 'true' - * @todo timestamp isn't set here to a default value - */ - RawPacket(); + /** + * A default constructor that initializes class'es attributes to default + * value: + * - data pointer is set to NULL + * - data length is set to 0 + * - deleteRawDataAtDestructor is set to 'true' + * @todo timestamp isn't set here to a default value + */ + RawPacket(); - /** - * A destructor for this class. Frees the raw data if deleteRawDataAtDestructor was set to 'true' - */ - virtual ~RawPacket(); + /** + * A destructor for this class. Frees the raw data if + * deleteRawDataAtDestructor was set to 'true' + */ + virtual ~RawPacket(); - /** - * A copy constructor that copies all data from another instance. Notice all raw data is copied (using memcpy), so when the original or - * the other instance are freed, the other won't be affected - * @param[in] other The instance to copy from - */ - RawPacket(const RawPacket& other); + /** + * A copy constructor that copies all data from another instance. Notice all + * raw data is copied (using memcpy), so when the original or the other + * instance are freed, the other won't be affected + * @param[in] other The instance to copy from + */ + RawPacket(const RawPacket& other); - /** - * Assignment operator overload for this class. When using this operator on an already initialized RawPacket instance, - * the original raw data is freed first. Then the other instance is copied to this instance, the same way the copy constructor works - * @todo free raw data only if deleteRawDataAtDestructor was set to 'true' - * @param[in] other The instance to copy from - */ - RawPacket& operator=(const RawPacket& other); + /** + * Assignment operator overload for this class. When using this operator on an + * already initialized RawPacket instance, the original raw data is freed + * first. Then the other instance is copied to this instance, the same way the + * copy constructor works + * @todo free raw data only if deleteRawDataAtDestructor was set to 'true' + * @param[in] other The instance to copy from + */ + RawPacket& operator=(const RawPacket& other); - /** - * @return RawPacket object type. Each derived class should return a different value - */ - virtual uint8_t getObjectType() const { return 0; } + /** + * @return RawPacket object type. Each derived class should return a different + * value + */ + virtual uint8_t getObjectType() const { return 0; } - /** - * Set a raw data. If data was already set and deleteRawDataAtDestructor was set to 'true' the old data will be freed first - * @param[in] pRawData A pointer to the new raw data - * @param[in] rawDataLen The new raw data length in bytes - * @param[in] timestamp The timestamp packet was received by the NIC (in usec precision) - * @param[in] layerType The link layer type for this raw data - * @param[in] frameLength When reading from pcap files, sometimes the captured length is different from the actual packet length. This parameter represents the packet - * length. This parameter is optional, if not set or set to -1 it is assumed both lengths are equal - * @return True if raw data was set successfully, false otherwise - */ - virtual bool setRawData(const uint8_t* pRawData, int rawDataLen, timeval timestamp, LinkLayerType layerType = LINKTYPE_ETHERNET, int frameLength = -1); + /** + * Set a raw data. If data was already set and deleteRawDataAtDestructor was + * set to 'true' the old data will be freed first + * @param[in] pRawData A pointer to the new raw data + * @param[in] rawDataLen The new raw data length in bytes + * @param[in] timestamp The timestamp packet was received by the NIC (in usec + * precision) + * @param[in] layerType The link layer type for this raw data + * @param[in] frameLength When reading from pcap files, sometimes the captured + * length is different from the actual packet length. This parameter + * represents the packet length. This parameter is optional, if not set or set + * to -1 it is assumed both lengths are equal + * @return True if raw data was set successfully, false otherwise + */ + virtual bool setRawData(const uint8_t* pRawData, int rawDataLen, + timeval timestamp, + LinkLayerType layerType = LINKTYPE_ETHERNET, + int frameLength = -1); - /** - * Set a raw data. If data was already set and deleteRawDataAtDestructor was set to 'true' the old data will be freed first - * @param[in] pRawData A pointer to the new raw data - * @param[in] rawDataLen The new raw data length in bytes - * @param[in] timestamp The timestamp packet was received by the NIC (in nsec precision) - * @param[in] layerType The link layer type for this raw data - * @param[in] frameLength When reading from pcap files, sometimes the captured length is different from the actual packet length. This parameter represents the packet - * length. This parameter is optional, if not set or set to -1 it is assumed both lengths are equal - * @return True if raw data was set successfully, false otherwise - */ - virtual bool setRawData(const uint8_t* pRawData, int rawDataLen, timespec timestamp, LinkLayerType layerType = LINKTYPE_ETHERNET, int frameLength = -1); + /** + * Set a raw data. If data was already set and deleteRawDataAtDestructor was + * set to 'true' the old data will be freed first + * @param[in] pRawData A pointer to the new raw data + * @param[in] rawDataLen The new raw data length in bytes + * @param[in] timestamp The timestamp packet was received by the NIC (in nsec + * precision) + * @param[in] layerType The link layer type for this raw data + * @param[in] frameLength When reading from pcap files, sometimes the captured + * length is different from the actual packet length. This parameter + * represents the packet length. This parameter is optional, if not set or set + * to -1 it is assumed both lengths are equal + * @return True if raw data was set successfully, false otherwise + */ + virtual bool setRawData(const uint8_t* pRawData, int rawDataLen, + timespec timestamp, + LinkLayerType layerType = LINKTYPE_ETHERNET, + int frameLength = -1); - /** - * Initialize a raw packet with data. The main difference between this method and setRawData() is that setRawData() - * is meant for replacing the data in an existing raw packet, whereas this method is meant to be used right after - * constructing a raw packet using the default c'tor, before setting any data - * @param pRawData A pointer to the new raw data - * @param rawDataLen The new raw data length in bytes - * @param timestamp The timestamp packet was received by the NIC (in nsec precision) - * @param layerType The link layer type for this raw data - * @return True if raw data was set successfully, false otherwise - */ - bool initWithRawData(const uint8_t* pRawData, int rawDataLen, timespec timestamp, LinkLayerType layerType = LINKTYPE_ETHERNET); + /** + * Initialize a raw packet with data. The main difference between this method + * and setRawData() is that setRawData() is meant for replacing the data in an + * existing raw packet, whereas this method is meant to be used right after + * constructing a raw packet using the default c'tor, before setting any data + * @param pRawData A pointer to the new raw data + * @param rawDataLen The new raw data length in bytes + * @param timestamp The timestamp packet was received by the NIC (in nsec + * precision) + * @param layerType The link layer type for this raw data + * @return True if raw data was set successfully, false otherwise + */ + bool initWithRawData(const uint8_t* pRawData, int rawDataLen, + timespec timestamp, + LinkLayerType layerType = LINKTYPE_ETHERNET); - /** - * Get raw data pointer - * @return A read-only pointer to the raw data - */ - const uint8_t* getRawData() const { return m_RawData; } + /** + * Get raw data pointer + * @return A read-only pointer to the raw data + */ + const uint8_t* getRawData() const { return m_RawData; } - /** - * Get the link layer type - * @return the type of the link layer - */ - LinkLayerType getLinkLayerType() const { return m_LinkLayerType; } + /** + * Get the link layer type + * @return the type of the link layer + */ + LinkLayerType getLinkLayerType() const { return m_LinkLayerType; } - /** - * This static method validates whether a link type integer value is valid - * @param[in] linkTypeValue Link type integer value - * @return True if the link type value is valid and can be casted into LinkLayerType enum, false otherwise - */ - static bool isLinkTypeValid(int linkTypeValue); + /** + * This static method validates whether a link type integer value is valid + * @param[in] linkTypeValue Link type integer value + * @return True if the link type value is valid and can be casted into + * LinkLayerType enum, false otherwise + */ + static bool isLinkTypeValid(int linkTypeValue); - /** - * Get raw data length in bytes - * @return Raw data length in bytes - */ - int getRawDataLen() const { return m_RawDataLen; } + /** + * Get raw data length in bytes + * @return Raw data length in bytes + */ + int getRawDataLen() const { return m_RawDataLen; } - /** - * Get frame length in bytes - * @return frame length in bytes - */ - int getFrameLength() const { return m_FrameLength; } - /** - * Get raw data timestamp - * @return Raw data timestamp - */ - timespec getPacketTimeStamp() const { return m_TimeStamp; } + /** + * Get frame length in bytes + * @return frame length in bytes + */ + int getFrameLength() const { return m_FrameLength; } + /** + * Get raw data timestamp + * @return Raw data timestamp + */ + timespec getPacketTimeStamp() const { return m_TimeStamp; } - /** - * Set raw packet timestamp with usec precision - * @param[in] timestamp The timestamp to set (with usec precision) - * @return True if timestamp was set successfully, false otherwise - */ - virtual bool setPacketTimeStamp(timeval timestamp); + /** + * Set raw packet timestamp with usec precision + * @param[in] timestamp The timestamp to set (with usec precision) + * @return True if timestamp was set successfully, false otherwise + */ + virtual bool setPacketTimeStamp(timeval timestamp); - /** - * Set raw packet timestamp with nsec precision - * @param[in] timestamp The timestamp to set (with nsec precision) - * @return True if timestamp was set successfully, false otherwise - */ - virtual bool setPacketTimeStamp(timespec timestamp); + /** + * Set raw packet timestamp with nsec precision + * @param[in] timestamp The timestamp to set (with nsec precision) + * @return True if timestamp was set successfully, false otherwise + */ + virtual bool setPacketTimeStamp(timespec timestamp); - /** - * Get an indication whether raw data was already set for this instance. - * @return True if raw data was set for this instance. Raw data can be set using the non-default constructor, using setRawData(), using - * the copy constructor or using the assignment operator. Returns false otherwise, for example: if the instance was created using the - * default constructor or clear() was called - */ - bool isPacketSet() const { return m_RawPacketSet; } + /** + * Get an indication whether raw data was already set for this instance. + * @return True if raw data was set for this instance. Raw data can be set + * using the non-default constructor, using setRawData(), using the copy + * constructor or using the assignment operator. Returns false otherwise, for + * example: if the instance was created using the default constructor or + * clear() was called + */ + bool isPacketSet() const { return m_RawPacketSet; } - /** - * Clears all members of this instance, meaning setting raw data to NULL, raw data length to 0, etc. Currently raw data is always freed, - * even if deleteRawDataAtDestructor was set to 'false' - * @todo deleteRawDataAtDestructor was set to 'true', don't free the raw data - * @todo set timestamp to a default value as well - */ - virtual void clear(); + /** + * Clears all members of this instance, meaning setting raw data to NULL, raw + * data length to 0, etc. Currently raw data is always freed, even if + * deleteRawDataAtDestructor was set to 'false' + * @todo deleteRawDataAtDestructor was set to 'true', don't free the raw data + * @todo set timestamp to a default value as well + */ + virtual void clear(); - /** - * Append data to the end of current data. This method works without allocating more memory, it just uses memcpy() to copy dataToAppend at - * the end of the current data. This means that the method assumes this memory was already allocated by the user. If it isn't the case then - * this method will cause memory corruption - * @param[in] dataToAppend A pointer to the data to append to current raw data - * @param[in] dataToAppendLen Length in bytes of dataToAppend - */ - virtual void appendData(const uint8_t* dataToAppend, size_t dataToAppendLen); + /** + * Append data to the end of current data. This method works without + * allocating more memory, it just uses memcpy() to copy dataToAppend at the + * end of the current data. This means that the method assumes this memory was + * already allocated by the user. If it isn't the case then this method will + * cause memory corruption + * @param[in] dataToAppend A pointer to the data to append to current raw data + * @param[in] dataToAppendLen Length in bytes of dataToAppend + */ + virtual void appendData(const uint8_t* dataToAppend, size_t dataToAppendLen); - /** - * Insert new data at some index of the current data and shift the remaining old data to the end. This method works without allocating more memory, - * it just copies dataToAppend at the relevant index and shifts the remaining data to the end. This means that the method assumes this memory was - * already allocated by the user. If it isn't the case then this method will cause memory corruption - * @param[in] atIndex The index to insert the new data to - * @param[in] dataToInsert A pointer to the new data to insert - * @param[in] dataToInsertLen Length in bytes of dataToInsert - */ - virtual void insertData(int atIndex, const uint8_t* dataToInsert, size_t dataToInsertLen); + /** + * Insert new data at some index of the current data and shift the remaining + * old data to the end. This method works without allocating more memory, it + * just copies dataToAppend at the relevant index and shifts the remaining + * data to the end. This means that the method assumes this memory was already + * allocated by the user. If it isn't the case then this method will cause + * memory corruption + * @param[in] atIndex The index to insert the new data to + * @param[in] dataToInsert A pointer to the new data to insert + * @param[in] dataToInsertLen Length in bytes of dataToInsert + */ + virtual void insertData(int atIndex, const uint8_t* dataToInsert, + size_t dataToInsertLen); - /** - * Remove certain number of bytes from current raw data buffer. All data after the removed bytes will be shifted back - * @param[in] atIndex The index to start removing bytes from - * @param[in] numOfBytesToRemove Number of bytes to remove - * @return True if all bytes were removed successfully, or false if atIndex+numOfBytesToRemove is out-of-bounds of the raw data buffer - */ - virtual bool removeData(int atIndex, size_t numOfBytesToRemove); + /** + * Remove certain number of bytes from current raw data buffer. All data after + * the removed bytes will be shifted back + * @param[in] atIndex The index to start removing bytes from + * @param[in] numOfBytesToRemove Number of bytes to remove + * @return True if all bytes were removed successfully, or false if + * atIndex+numOfBytesToRemove is out-of-bounds of the raw data buffer + */ + virtual bool removeData(int atIndex, size_t numOfBytesToRemove); - /** - * Re-allocate raw packet buffer meaning add size to it without losing the current packet data. This method allocates the required buffer size as instructed - * by the use and then copies the raw data from the current allocated buffer to the new one. This method can become useful if the user wants to insert or - * append data to the raw data, and the previous allocated buffer is too small, so the user wants to allocate a larger buffer and get RawPacket instance to - * point to it - * @param[in] newBufferLength The new buffer length as required by the user. The method is responsible to allocate the memory - * @return True if data was reallocated successfully, false otherwise - */ - virtual bool reallocateData(size_t newBufferLength); - }; + /** + * Re-allocate raw packet buffer meaning add size to it without losing the + * current packet data. This method allocates the required buffer size as + * instructed by the use and then copies the raw data from the current + * allocated buffer to the new one. This method can become useful if the user + * wants to insert or append data to the raw data, and the previous allocated + * buffer is too small, so the user wants to allocate a larger buffer and get + * RawPacket instance to point to it + * @param[in] newBufferLength The new buffer length as required by the user. + * The method is responsible to allocate the memory + * @return True if data was reallocated successfully, false otherwise + */ + virtual bool reallocateData(size_t newBufferLength); +}; } // namespace pcpp diff --git a/Packet++/header/S7CommLayer.h b/Packet++/header/S7CommLayer.h index e5cdfbb3f5..c733ef807b 100644 --- a/Packet++/header/S7CommLayer.h +++ b/Packet++/header/S7CommLayer.h @@ -4,28 +4,26 @@ #include "EthLayer.h" #include "Layer.h" -namespace pcpp -{ +namespace pcpp { /** * @struct s7commhdr * Represents a S7COMM protocol header */ #pragma pack(push, 1) - typedef struct - { - /** protocol id */ - uint8_t protocolId; - /** message type */ - uint8_t msgType; - /** redundancy identification (reserved) */ - uint16_t reserved; - /** protocol data unit reference */ - uint16_t pduRef; - /** parameter length */ - uint16_t paramLength; - /** data length */ - uint16_t dataLength; - } s7commhdr; +typedef struct { + /** protocol id */ + uint8_t protocolId; + /** message type */ + uint8_t msgType; + /** redundancy identification (reserved) */ + uint16_t reserved; + /** protocol data unit reference */ + uint16_t pduRef; + /** parameter length */ + uint16_t paramLength; + /** data length */ + uint16_t dataLength; +} s7commhdr; #pragma pack(pop) /** @@ -33,187 +31,186 @@ namespace pcpp * Represents a S7COMM protocol header with Ack-Data header */ #pragma pack(push, 1) - struct s7comm_ack_data_hdr : s7commhdr - { - /** error class */ - uint8_t errorClass; - /** error code */ - uint8_t errorCode; - }; +struct s7comm_ack_data_hdr : s7commhdr { + /** error class */ + uint8_t errorClass; + /** error code */ + uint8_t errorCode; +}; #pragma pack(pop) - /** - * @class S7CommParameter - * Represents a S7COMM (S7 Communication) protocol Parameter - */ - class S7CommParameter - { - friend class S7CommLayer; - - public: - S7CommParameter() {} - - virtual ~S7CommParameter() {} - - /** - * @return The data of the Parameter - */ - uint8_t *getData() const { return m_Data; } - /** - * @return The length of the Parameter data - */ - size_t getDataLength() const { return m_DataLen; } - - private: - S7CommParameter(uint8_t *data, size_t dataLen) : m_Data(data), m_DataLen(dataLen) {} - uint8_t *m_Data; - size_t m_DataLen; - }; - /** - * @class S7CommLayer - * Represents a S7COMM (S7 Communication) protocol - */ - class S7CommLayer : public Layer - { - public: - /** - * A constructor that allocates a new S7comm header - * @param[in] msgType The general type of the message - * @param[in] pduRef Link responses to their requests - * @param[in] paramLength The length of the parameter field - * @param[in] dataLength The length of the data field - * @param[in] errorClass The value of the error class - * @param[in] errorCode The value of the error code - */ - S7CommLayer(uint8_t msgType, uint16_t pduRef, uint16_t paramLength, uint16_t dataLength, uint8_t errorClass = 0, - uint8_t errorCode = 0); - - /** - * A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data (will be casted to @ref s7commhdr) - * @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 - */ - S7CommLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) - : Layer(data, dataLen, prevLayer, packet) - { - m_Protocol = S7COMM; - m_Parameter = nullptr; - } - - virtual ~S7CommLayer() - { - if (m_Parameter) - delete m_Parameter; - } - - /** - * @return S7comm protocol id - */ - uint8_t getProtocolId() const; - - /** - * @return S7comm message type - */ - uint8_t getMsgType() const; - - /** - * @return S7comm PDU ref - */ - uint16_t getPduRef() const; - - /** - * @return S7comm parameter length - */ - uint16_t getParamLength() const; - - /** - * @return S7comm data length - */ - uint16_t getDataLength() const; - - /** - * @return S7comm error code - */ - uint8_t getErrorCode() const; - - /** - * @return S7comm error class - */ - uint8_t getErrorClass() const; - - /** - * @return S7comm parameter - */ - const S7CommParameter *getParameter(); - - /** - * Set the value of the message type - * @param[in] msgType The value of the message type - */ - void setMsgType(uint8_t msgType) const; - - /** - * Set the value of the PDU ref - * @param[in] pduRef The value of the PDU ref - */ - void setPduRef(uint16_t pduRef) const; - - /** - * Set the value of the error code - * @param[in] errorCode The value of the error code - */ - void setErrorCode(uint8_t errorCode) const; - /** - * Set the value of the error class - * @param[in] errorClass The value of the error class - */ - void setErrorClass(uint8_t errorClass) const; - - /** - * @return Size of S7CommLayer - */ - size_t getHeaderLen() const override { return m_DataLen; } - - /** - * Does nothing for this layer (S7CommLayer is always last) - */ - void computeCalculateFields() override {} - - /** - * Does nothing for this layer (S7CommLayer is always last) - */ - void parseNextLayer() override {} - - /** - * A static method that takes a byte array and detects whether it is a S7COMM - * @param[in] data A byte array - * @param[in] dataSize The byte array size (in bytes) - * @return True if the data looks like a valid S7COMM layer - */ - static bool isDataValid(const uint8_t *data, size_t dataSize); - - std::string toString() const override; - - OsiModelLayer getOsiModelLayer() const override { return OsiModelApplicationLayer; } - - private: - s7commhdr *getS7commHeader() const { return (s7commhdr *)m_Data; } - - s7comm_ack_data_hdr *getS7commAckDataHeader() const - { - if (getS7commHeader()->msgType == 0x03) - { - return (s7comm_ack_data_hdr *)m_Data; - } - return nullptr; - } - - size_t getS7commHeaderLength() const; - - S7CommParameter *m_Parameter; - }; +/** + * @class S7CommParameter + * Represents a S7COMM (S7 Communication) protocol Parameter + */ +class S7CommParameter { + friend class S7CommLayer; + + public: + S7CommParameter() {} + + virtual ~S7CommParameter() {} + + /** + * @return The data of the Parameter + */ + uint8_t* getData() const { return m_Data; } + /** + * @return The length of the Parameter data + */ + size_t getDataLength() const { return m_DataLen; } + + private: + S7CommParameter(uint8_t* data, size_t dataLen) + : m_Data(data), m_DataLen(dataLen) {} + uint8_t* m_Data; + size_t m_DataLen; +}; +/** + * @class S7CommLayer + * Represents a S7COMM (S7 Communication) protocol + */ +class S7CommLayer : public Layer { + public: + /** + * A constructor that allocates a new S7comm header + * @param[in] msgType The general type of the message + * @param[in] pduRef Link responses to their requests + * @param[in] paramLength The length of the parameter field + * @param[in] dataLength The length of the data field + * @param[in] errorClass The value of the error class + * @param[in] errorCode The value of the error code + */ + S7CommLayer(uint8_t msgType, uint16_t pduRef, uint16_t paramLength, + uint16_t dataLength, uint8_t errorClass = 0, + uint8_t errorCode = 0); + + /** + * A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data (will be casted to @ref + * s7commhdr) + * @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 + */ + S7CommLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : Layer(data, dataLen, prevLayer, packet) { + m_Protocol = S7COMM; + m_Parameter = nullptr; + } + + virtual ~S7CommLayer() { + if (m_Parameter) + delete m_Parameter; + } + + /** + * @return S7comm protocol id + */ + uint8_t getProtocolId() const; + + /** + * @return S7comm message type + */ + uint8_t getMsgType() const; + + /** + * @return S7comm PDU ref + */ + uint16_t getPduRef() const; + + /** + * @return S7comm parameter length + */ + uint16_t getParamLength() const; + + /** + * @return S7comm data length + */ + uint16_t getDataLength() const; + + /** + * @return S7comm error code + */ + uint8_t getErrorCode() const; + + /** + * @return S7comm error class + */ + uint8_t getErrorClass() const; + + /** + * @return S7comm parameter + */ + const S7CommParameter* getParameter(); + + /** + * Set the value of the message type + * @param[in] msgType The value of the message type + */ + void setMsgType(uint8_t msgType) const; + + /** + * Set the value of the PDU ref + * @param[in] pduRef The value of the PDU ref + */ + void setPduRef(uint16_t pduRef) const; + + /** + * Set the value of the error code + * @param[in] errorCode The value of the error code + */ + void setErrorCode(uint8_t errorCode) const; + /** + * Set the value of the error class + * @param[in] errorClass The value of the error class + */ + void setErrorClass(uint8_t errorClass) const; + + /** + * @return Size of S7CommLayer + */ + size_t getHeaderLen() const override { return m_DataLen; } + + /** + * Does nothing for this layer (S7CommLayer is always last) + */ + void computeCalculateFields() override {} + + /** + * Does nothing for this layer (S7CommLayer is always last) + */ + void parseNextLayer() override {} + + /** + * A static method that takes a byte array and detects whether it is a S7COMM + * @param[in] data A byte array + * @param[in] dataSize The byte array size (in bytes) + * @return True if the data looks like a valid S7COMM layer + */ + static bool isDataValid(const uint8_t* data, size_t dataSize); + + std::string toString() const override; + + OsiModelLayer getOsiModelLayer() const override { + return OsiModelApplicationLayer; + } + + private: + s7commhdr* getS7commHeader() const { return (s7commhdr*)m_Data; } + + s7comm_ack_data_hdr* getS7commAckDataHeader() const { + if (getS7commHeader()->msgType == 0x03) { + return (s7comm_ack_data_hdr*)m_Data; + } + return nullptr; + } + + size_t getS7commHeaderLength() const; + + S7CommParameter* m_Parameter; +}; } // namespace pcpp diff --git a/Packet++/header/SSHLayer.h b/Packet++/header/SSHLayer.h index 6ec86365d6..40af0a3cc0 100644 --- a/Packet++/header/SSHLayer.h +++ b/Packet++/header/SSHLayer.h @@ -5,426 +5,491 @@ /** * @file - * This file introduces classes and structures that represent the SSH (Secure Shell) protocol. + * This file introduces classes and structures that represent the SSH (Secure + Shell) protocol. * - * An overview of this protocol can be found here: https://en.wikipedia.org/wiki/Ssh_(Secure_Shell) + * An overview of this protocol can be found here: + https://en.wikipedia.org/wiki/Ssh_(Secure_Shell) * - * For more details please refer to RFC 4253: https://tools.ietf.org/html/rfc4253 + * For more details please refer to RFC 4253: + https://tools.ietf.org/html/rfc4253 * - * These current implementation supports parsing of SSH packets when possible (meaning when they are not encrypted). + * These current implementation supports parsing of SSH packets when possible + (meaning when they are not encrypted). * Creation and editing of SSH packets is currently __not supported__. * - * SSH typically uses TCP port 22 so PcapPlusPlus assumes all traffic on this port is SSH traffic. - * PcapPlusPlus uses some heuristics to determine the type of the SSH message (which will be covered later). - * If it doesn't find a match to one of the other SSH messages, it assumes it is an encrypted SSH message. + * SSH typically uses TCP port 22 so PcapPlusPlus assumes all traffic on this + port is SSH traffic. + * PcapPlusPlus uses some heuristics to determine the type of the SSH message + (which will be covered later). + * If it doesn't find a match to one of the other SSH messages, it assumes it is + an encrypted SSH message. * - * Following is an overview of the SSH protocol classes currently supported in PcapPlusPlus. They cover the different messages of the SSH protocol: + * Following is an overview of the SSH protocol classes currently supported in + PcapPlusPlus. They cover the different messages of the SSH protocol: * @verbatim - +----------------------------+ SSH version identification - +---| SSHIdentificationMessage | ===> as described here: - | +----------------------------+ https://tools.ietf.org/html/rfc4253#section-4.2 + +----------------------------+ SSH version + identification + +---| SSHIdentificationMessage | ===> as + described here: | +----------------------------+ + https://tools.ietf.org/html/rfc4253#section-4.2 | - +------------+ | +----------------------------+ SSH handshake message - | SSHLayer |-------------+---| SSHHandshakeMessage | ===> which is typically one of the messages described here: - | (abstract) | | +----------------------------+ https://tools.ietf.org/html/rfc4253#section-12 + +------------+ | +----------------------------+ SSH + handshake message | SSHLayer |-------------+---| SSHHandshakeMessage | + ===> which is typically one of the messages described here: | (abstract) | | + +----------------------------+ https://tools.ietf.org/html/rfc4253#section-12 +------------+ | | - | | +----------------------------+ SSH Key Exchange message - | +-----| SSHKeyExchangeInitMessage | ===> as described here: - | +----------------------------+ https://tools.ietf.org/html/rfc4253#section-7 + | | +----------------------------+ + SSH Key Exchange message | +-----| SSHKeyExchangeInitMessage | + ===> as described here: | +----------------------------+ + https://tools.ietf.org/html/rfc4253#section-7 | | +----------------------------+ - +---| SSHEncryptedMessage | ===> An encrypted SSH message + +---| SSHEncryptedMessage | ===> An + encrypted SSH message +----------------------------+ @endverbatim - * The following points describe the heuristics for deciding the message type for each packet: - * 1. If the data starts with the characters "SSH-" and ends with "\n" (or "\r\n") it's assumed the message is of type + * The following points describe the heuristics for deciding the message type + for each packet: + * 1. If the data starts with the characters "SSH-" and ends with "\n" (or + "\r\n") it's assumed the message is of type * pcpp#SSHIdentificationMessage * 2. Try to determine if this is a non-encrypted SSH handshake message: - * - Look at the first 4 bytes of the data which may contain the packet length and see if the value is smaller of equal + * - Look at the first 4 bytes of the data which may contain the packet + length and see if the value is smaller of equal * than the entire layer length - * - The next byte contains the padding length, check if it's smaller or equal than the packet length - * - The next byte contains the message type, check if the value is a valid message type as described in: + * - The next byte contains the padding length, check if it's smaller or + equal than the packet length + * - The next byte contains the message type, check if the value is a valid + message type as described in: * * - * If all of these condition are met, this message is either pcpp#SSHKeyExchangeInitMessage (if message type is - * pcpp#SSHHandshakeMessage#SSH_MSG_KEX_INIT) or pcpp#SSHHandshakeMessage (for all other message types) - * 3. If non of these conditions are met, it is assumed this is an encrypted message (pcpp#SSHEncryptedMessage) + * If all of these condition are met, this message is either + pcpp#SSHKeyExchangeInitMessage (if message type is + * pcpp#SSHHandshakeMessage#SSH_MSG_KEX_INIT) or pcpp#SSHHandshakeMessage + (for all other message types) + * 3. If non of these conditions are met, it is assumed this is an encrypted + message (pcpp#SSHEncryptedMessage) */ /** * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - - /** - * @class SSHLayer - * This is the base class for the SSH layer. It is an abstract class that cannot be instantiated. - * It holds some common functionality, but its most important method is createSSHMessage() - * which takes raw data and creates an SSH message according to the heuristics described - * in the SSHLayer.h file description - */ - class SSHLayer : public Layer - { - public: - /** - * A static method that takes raw packet data and uses the heuristics described in the - * SSHLayer.h file description to create an SSH layer instance. This method assumes the data is - * indeed SSH data and not some other arbitrary 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 - * @return An instance of one of the classes that inherit SSHLayer as described in the - * SSHLayer.h file description - */ - static SSHLayer* createSSHMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); - - /** - * A static method that takes src and dst ports and determines whether it's SSH traffic or not. - * @param[in] portSrc The source TCP port to examine - * @param[in] portDst The dest TCP port to examine - * @return Currently the implementation is very simple and returns "true" if either src or dst ports - * are equal to 22, "false" otherwise - */ - static bool isSSHPort(uint16_t portSrc, uint16_t portDst) { return portSrc == 22 || portDst == 22; } - - // implement abstract methods - - /** - * Several SSH records can reside in a single packets. This method examins the remaining data and creates additional - * SSH records if applicable - */ - void parseNextLayer(); - - /** - * Does nothing for this layer - */ - void computeCalculateFields() {} - - OsiModelLayer getOsiModelLayer() const { return OsiModelApplicationLayer; } - - protected: - // protected c'tor, this class cannot be instantiated - SSHLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) { m_Protocol = SSH; } - - private: - // this layer supports only parsing - SSHLayer(); - }; - - - - /** - * @class SSHIdentificationMessage - * A class that represents SSH identification message as described in RFC 4253: - * - * The message content is typically a string that contains the protocol version, software version and a few more details. - * This string can be retrieved using the getIdentificationMessage() method - */ - class SSHIdentificationMessage : public SSHLayer - { - public: - /** - * @return The SSH identification message which is typically the content of this message - */ - std::string getIdentificationMessage(); - - /** - * A static method that takes raw data and tries to parse it as an SSH identification message using the heuristics described - * in the SSHLayer.h file description. It returns a SSHIdentificationMessage instance if such a message can be identified or NULL - * otherwise. - * @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 - * @return An instance of SSHIdentificationMessage or NULL if this is not an identification message - */ - static SSHIdentificationMessage* tryParse(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); - - // implement abstract methods - - /** - * @return The size of the identification message - */ - size_t getHeaderLen() const { return m_DataLen; } - - std::string toString() const; - - private: - // this layer supports only parsing - SSHIdentificationMessage(); - - // private c'tor, this class cannot be instantiated - SSHIdentificationMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : SSHLayer(data, dataLen, prevLayer, packet) {} - - }; - - - /** - * @class SSHHandshakeMessage - * A class representing all of the non-encrypted SSH handshake messages. - * An handshake message typically has the following structure: - * - @verbatim - 0 1 2 3 4 5 6 - +---------+---------+---------+---------+---------+---------+----------- ---------+ - | Packet Length | Padding | Message | Message .... Padding | - | | Length | Type | Content .... | - +---------------------------------------+---------+---------+----------- ---------+ - @endverbatim - * - * The first 4 bytes hold the packet length, followed by 1 byte that holds the padding length (which comes at the end of the message), - * then 1 byte that holds the message type (which can be of type SSHHandshakeMessage#SSHHandshakeMessageType) and then the message content. - * At the end of the content there is typically padding. - * - * This class provides access to all of these values. The message content itself is not parse with the exception of SSHKeyExchangeInitMessage - * which inherits from this class and provides parsing of the Key Exchange Init message. - */ - class SSHHandshakeMessage : public SSHLayer - { - public: - /** - * An enum that represents SSH non-encrypted message types - */ - enum SSHHandshakeMessageType - { - /** Key Exchange Init message */ - SSH_MSG_KEX_INIT = 20, - /** New Keys message */ - SSH_MSG_NEW_KEYS = 21, - /** Diffie-Hellman Key Exchange Init message */ - SSH_MSG_KEX_DH_INIT = 30, - /** message */ - SSH_MSG_KEX_DH_REPLY = 31, - /** Diffie-Hellman Group Exchange Init message */ - SSH_MSG_KEX_DH_GEX_INIT = 32, - /** "Diffie-Hellman Group Exchange Reply message */ - SSH_MSG_KEX_DH_GEX_REPLY = 33, - /** Diffie-Hellman Group Exchange Request message */ - SSH_MSG_KEX_DH_GEX_REQUEST = 34, - /** Unknown message */ - SSH_MSG_UNKNOWN = 999 - }; - - /** - * @return The message type - */ - SSHHandshakeMessageType getMessageType() const; - - /** - * @return A string representation of the message type - */ - std::string getMessageTypeStr() const; - - /** - * @return A raw byte stream of the message content - */ - uint8_t* getSSHHandshakeMessage() const; - - /** - * @return The message content length in [bytes] which is calculated by the overall packet length - * minus the message header (which includes packet length, padding length and message type) and - * minus the padding bytes - */ - size_t getSSHHandshakeMessageLength() const; - - /** - * @return The padding length in [bytes] - */ - size_t getPaddingLength() const; - - /** - * A static method that takes raw packet data and uses some heuristics described in the - * SSHLayer.h file description to parse it as SSH handshake message instance - * @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 - * @return Upon successful parsing the return value would be an instance of SSHKeyExchangeInitMessage - * for Key Exchange Init message or SSHHandshakeMessage for any other message type. If parsing fails NULL - * will be returned - */ - static SSHHandshakeMessage* tryParse(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); - - // implement abstract methods - - /** - * @return The size of the SSH handshake message including the padding and message header - */ - size_t getHeaderLen() const; - - std::string toString() const; - - protected: - - /** - * An internal struct representing the SSH handshake message header - */ - #pragma pack(push, 1) - struct ssh_message_base - { - uint32_t packetLength; - uint8_t paddingLength; - uint8_t messageCode; - }; - #pragma pack(pop) - - // this layer supports only parsing - SSHHandshakeMessage(); - - // private c'tor, this class cannot be instantiated - SSHHandshakeMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : SSHLayer(data, dataLen, prevLayer, packet) {} - - ssh_message_base* getMsgBaseHeader() const { return (ssh_message_base*)m_Data; } - }; - - - /** - * @class SSHKeyExchangeInitMessage - * A class representing the SSH Key Exchange Init message. This is a non-encrypted message that contains information - * about the algorithms used for key exchange, encryption, MAC and compression. This class provides methods to access - * these details - */ - class SSHKeyExchangeInitMessage : public SSHHandshakeMessage - { - public: - /** - * A c'tor for this class that accepts raw message data. Please avoid using it as it's used internally - * when parsing SSH handshake messages in SSHHandshakeMessage#tryParse() - * @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 - */ - SSHKeyExchangeInitMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); - - /** - * Each SSH Key Exchange Init message contains a random 16-byte value generated by the sender. - * This method returns a pointer to this 16-byte cookie. To get the value as a hex string - * please refer to getCookieAsHexStream() - * @return A pointer to the 16-byte cookie value or NULL if the message is malformed - */ - uint8_t* getCookie(); - - /** - * Each SSH Key Exchange Init message contains a random 16-byte value generated by the sender. - * This method returns the 16-byte cookie as a hex stream. To get the raw data please refer to - * getCookie() - * @return A hex stream of the 16-byte cookie value or an empty string if the message is malformed - */ - std::string getCookieAsHexStream(); - - /** - * @return A comma-separated list of the key exchange algorithms used in this session. - * Can be empty if the value is missing or the message is malformed - */ - std::string getKeyExchangeAlgorithms() { return getFieldValue(0); } - - /** - * @return A comma-separated list of the algorithms supported for the server host key. - * Can be empty if the value is missing or the message is malformed - */ - std::string getServerHostKeyAlgorithms() { return getFieldValue(1); } - - /** - * @return A comma-separated list of acceptable symmetric encryption algorithms (also known as ciphers) - * from the client to the server. Can be empty if the value is missing or the message is malformed - */ - std::string getEncryptionAlgorithmsClientToServer() { return getFieldValue(2); } - - /** - * @return A comma-separated list of acceptable symmetric encryption algorithms (also known as ciphers) - * from the server to the client. Can be empty if the value is missing or the message is malformed - */ - std::string getEncryptionAlgorithmsServerToClient() { return getFieldValue(3); } - - /** - * @return A comma-separated list of acceptable MAC algorithms from the client to the server. - * Can be empty if the value is missing or the message is malformed - */ - std::string getMacAlgorithmsClientToServer() { return getFieldValue(4); } - - /** - * @return A comma-separated list of acceptable MAC algorithms from the server to the client. - * Can be empty if the value is missing or the message is malformed - */ - std::string getMacAlgorithmsServerToClient() { return getFieldValue(5); } - - /** - * @return A comma-separated list of acceptable compression algorithms from the client to the server. - * Can be empty if the value is missing or the message is malformed - */ - std::string getCompressionAlgorithmsClientToServer() { return getFieldValue(6); } - - /** - * @return A comma-separated list of acceptable compression algorithms from the server to the client. - * Can be empty if the value is missing or the message is malformed - */ - std::string getCompressionAlgorithmsServerToClient() { return getFieldValue(7); } - - /** - * @return A comma-separated list of language tags from the client to the server. - * Can be empty if the value is missing or the message is malformed - */ - std::string getLanguagesClientToServer() { return getFieldValue(8); } - - /** - * @return A comma-separated list of language tags from the server to the client. - * Can be empty if the value is missing or the message is malformed - */ - std::string getLanguagesServerToClient() { return getFieldValue(9); } - - /** - * @return Indicates whether a guessed key exchange packet follows. If a - * guessed packet will be sent, the return value is true. If no guessed - * packet will be sent or if this value is missing, the return value is false. - */ - bool isFirstKexPacketFollows(); - - private: - size_t m_FieldOffsets[11]; - bool m_OffsetsInitialized; - - void parseMessageAndInitOffsets(); - - std::string getFieldValue(int fieldOffsetIndex); - }; - - - /** - * @class SSHEncryptedMessage - * A class representing an SSH encrypted message. In such messages there is very little information to extract from the packet, - * hence this class doesn't expose any methods or getters, other than the ones inherited from parent classes. - * - * It is assumed that any SSH message which does not fit to any of the other SSH message types, according to the heuristics described in - * the SSHLayer.h file description, is considered as an encrypted message. - */ - class SSHEncryptedMessage : public SSHLayer - { - public: - - /** - * A c'tor for this class that accepts raw message data. Please avoid using it as it's used internally - * when parsing SSH messagess in SSHLayer#createSSHMessage() - */ - SSHEncryptedMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : SSHLayer(data, dataLen, prevLayer, packet) {} - - // implement abstract methods - - /** - * @return The size of the message which is equal to the size of the layer - */ - size_t getHeaderLen() const { return m_DataLen; } - - std::string toString() const; - }; - -} +namespace pcpp { + +/** + * @class SSHLayer + * This is the base class for the SSH layer. It is an abstract class that cannot + * be instantiated. It holds some common functionality, but its most important + * method is createSSHMessage() which takes raw data and creates an SSH message + * according to the heuristics described in the SSHLayer.h file description + */ +class SSHLayer : public Layer { + public: + /** + * A static method that takes raw packet data and uses the heuristics + * described in the SSHLayer.h file description to create an SSH layer + * instance. This method assumes the data is indeed SSH data and not some + * other arbitrary 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 + * @return An instance of one of the classes that inherit SSHLayer as + * described in the SSHLayer.h file description + */ + static SSHLayer* createSSHMessage(uint8_t* data, size_t dataLen, + Layer* prevLayer, Packet* packet); + + /** + * A static method that takes src and dst ports and determines whether it's + * SSH traffic or not. + * @param[in] portSrc The source TCP port to examine + * @param[in] portDst The dest TCP port to examine + * @return Currently the implementation is very simple and returns "true" if + * either src or dst ports are equal to 22, "false" otherwise + */ + static bool isSSHPort(uint16_t portSrc, uint16_t portDst) { + return portSrc == 22 || portDst == 22; + } + + // implement abstract methods + + /** + * Several SSH records can reside in a single packets. This method examins the + * remaining data and creates additional SSH records if applicable + */ + void parseNextLayer(); + + /** + * Does nothing for this layer + */ + void computeCalculateFields() {} + + OsiModelLayer getOsiModelLayer() const { return OsiModelApplicationLayer; } + + protected: + // protected c'tor, this class cannot be instantiated + SSHLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : Layer(data, dataLen, prevLayer, packet) { + m_Protocol = SSH; + } + + private: + // this layer supports only parsing + SSHLayer(); +}; + +/** + * @class SSHIdentificationMessage + * A class that represents SSH identification message as described in RFC 4253: + * + * + * The message content is typically a string that contains the protocol version, + * software version and a few more details. This string can be retrieved using + * the getIdentificationMessage() method + */ +class SSHIdentificationMessage : public SSHLayer { + public: + /** + * @return The SSH identification message which is typically the content of + * this message + */ + std::string getIdentificationMessage(); + + /** + * A static method that takes raw data and tries to parse it as an SSH + * identification message using the heuristics described in the SSHLayer.h + * file description. It returns a SSHIdentificationMessage instance if such a + * message can be identified or NULL otherwise. + * @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 + * @return An instance of SSHIdentificationMessage or NULL if this is not an + * identification message + */ + static SSHIdentificationMessage* tryParse(uint8_t* data, size_t dataLen, + Layer* prevLayer, Packet* packet); + + // implement abstract methods + + /** + * @return The size of the identification message + */ + size_t getHeaderLen() const { return m_DataLen; } + + std::string toString() const; + + private: + // this layer supports only parsing + SSHIdentificationMessage(); + + // private c'tor, this class cannot be instantiated + SSHIdentificationMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet) + : SSHLayer(data, dataLen, prevLayer, packet) {} +}; + +/** + * @class SSHHandshakeMessage + * A class representing all of the non-encrypted SSH handshake messages. + * An handshake message typically has the following structure: + * + @verbatim + 0 1 2 3 4 5 6 + +---------+---------+---------+---------+---------+---------+----------- + ---------+ | Packet Length | Padding | Message | + Message .... Padding | | | Length | + Type | Content .... | + +---------------------------------------+---------+---------+----------- + ---------+ + @endverbatim + * + * The first 4 bytes hold the packet length, followed by 1 byte that holds the + padding length (which comes at the end of the message), + * then 1 byte that holds the message type (which can be of type + SSHHandshakeMessage#SSHHandshakeMessageType) and then the message content. + * At the end of the content there is typically padding. + * + * This class provides access to all of these values. The message content itself + is not parse with the exception of SSHKeyExchangeInitMessage + * which inherits from this class and provides parsing of the Key Exchange Init + message. + */ +class SSHHandshakeMessage : public SSHLayer { + public: + /** + * An enum that represents SSH non-encrypted message types + */ + enum SSHHandshakeMessageType { + /** Key Exchange Init message */ + SSH_MSG_KEX_INIT = 20, + /** New Keys message */ + SSH_MSG_NEW_KEYS = 21, + /** Diffie-Hellman Key Exchange Init message */ + SSH_MSG_KEX_DH_INIT = 30, + /** message */ + SSH_MSG_KEX_DH_REPLY = 31, + /** Diffie-Hellman Group Exchange Init message */ + SSH_MSG_KEX_DH_GEX_INIT = 32, + /** "Diffie-Hellman Group Exchange Reply message */ + SSH_MSG_KEX_DH_GEX_REPLY = 33, + /** Diffie-Hellman Group Exchange Request message */ + SSH_MSG_KEX_DH_GEX_REQUEST = 34, + /** Unknown message */ + SSH_MSG_UNKNOWN = 999 + }; + + /** + * @return The message type + */ + SSHHandshakeMessageType getMessageType() const; + + /** + * @return A string representation of the message type + */ + std::string getMessageTypeStr() const; + + /** + * @return A raw byte stream of the message content + */ + uint8_t* getSSHHandshakeMessage() const; + + /** + * @return The message content length in [bytes] which is calculated by the + * overall packet length minus the message header (which includes packet + * length, padding length and message type) and minus the padding bytes + */ + size_t getSSHHandshakeMessageLength() const; + + /** + * @return The padding length in [bytes] + */ + size_t getPaddingLength() const; + + /** + * A static method that takes raw packet data and uses some heuristics + * described in the SSHLayer.h file description to parse it as SSH handshake + * message instance + * @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 + * @return Upon successful parsing the return value would be an instance of + * SSHKeyExchangeInitMessage for Key Exchange Init message or + * SSHHandshakeMessage for any other message type. If parsing fails NULL will + * be returned + */ + static SSHHandshakeMessage* tryParse(uint8_t* data, size_t dataLen, + Layer* prevLayer, Packet* packet); + + // implement abstract methods + + /** + * @return The size of the SSH handshake message including the padding and + * message header + */ + size_t getHeaderLen() const; + + std::string toString() const; + + protected: +/** + * An internal struct representing the SSH handshake message header + */ +#pragma pack(push, 1) + struct ssh_message_base { + uint32_t packetLength; + uint8_t paddingLength; + uint8_t messageCode; + }; +#pragma pack(pop) + + // this layer supports only parsing + SSHHandshakeMessage(); + + // private c'tor, this class cannot be instantiated + SSHHandshakeMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet) + : SSHLayer(data, dataLen, prevLayer, packet) {} + + ssh_message_base* getMsgBaseHeader() const { + return (ssh_message_base*)m_Data; + } +}; + +/** + * @class SSHKeyExchangeInitMessage + * A class representing the SSH Key Exchange Init message. This is a + * non-encrypted message that contains information about the algorithms used for + * key exchange, encryption, MAC and compression. This class provides methods to + * access these details + */ +class SSHKeyExchangeInitMessage : public SSHHandshakeMessage { + public: + /** + * A c'tor for this class that accepts raw message data. Please avoid using it + * as it's used internally when parsing SSH handshake messages in + * SSHHandshakeMessage#tryParse() + * @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 + */ + SSHKeyExchangeInitMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet); + + /** + * Each SSH Key Exchange Init message contains a random 16-byte value + * generated by the sender. This method returns a pointer to this 16-byte + * cookie. To get the value as a hex string please refer to + * getCookieAsHexStream() + * @return A pointer to the 16-byte cookie value or NULL if the message is + * malformed + */ + uint8_t* getCookie(); + + /** + * Each SSH Key Exchange Init message contains a random 16-byte value + * generated by the sender. This method returns the 16-byte cookie as a hex + * stream. To get the raw data please refer to getCookie() + * @return A hex stream of the 16-byte cookie value or an empty string if the + * message is malformed + */ + std::string getCookieAsHexStream(); + + /** + * @return A comma-separated list of the key exchange algorithms used in this + * session. Can be empty if the value is missing or the message is malformed + */ + std::string getKeyExchangeAlgorithms() { return getFieldValue(0); } + + /** + * @return A comma-separated list of the algorithms supported for the server + * host key. Can be empty if the value is missing or the message is malformed + */ + std::string getServerHostKeyAlgorithms() { return getFieldValue(1); } + + /** + * @return A comma-separated list of acceptable symmetric encryption + * algorithms (also known as ciphers) from the client to the server. Can be + * empty if the value is missing or the message is malformed + */ + std::string getEncryptionAlgorithmsClientToServer() { + return getFieldValue(2); + } + + /** + * @return A comma-separated list of acceptable symmetric encryption + * algorithms (also known as ciphers) from the server to the client. Can be + * empty if the value is missing or the message is malformed + */ + std::string getEncryptionAlgorithmsServerToClient() { + return getFieldValue(3); + } + + /** + * @return A comma-separated list of acceptable MAC algorithms from the client + * to the server. Can be empty if the value is missing or the message is + * malformed + */ + std::string getMacAlgorithmsClientToServer() { return getFieldValue(4); } + + /** + * @return A comma-separated list of acceptable MAC algorithms from the server + * to the client. Can be empty if the value is missing or the message is + * malformed + */ + std::string getMacAlgorithmsServerToClient() { return getFieldValue(5); } + + /** + * @return A comma-separated list of acceptable compression algorithms from + * the client to the server. Can be empty if the value is missing or the + * message is malformed + */ + std::string getCompressionAlgorithmsClientToServer() { + return getFieldValue(6); + } + + /** + * @return A comma-separated list of acceptable compression algorithms from + * the server to the client. Can be empty if the value is missing or the + * message is malformed + */ + std::string getCompressionAlgorithmsServerToClient() { + return getFieldValue(7); + } + + /** + * @return A comma-separated list of language tags from the client to the + * server. Can be empty if the value is missing or the message is malformed + */ + std::string getLanguagesClientToServer() { return getFieldValue(8); } + + /** + * @return A comma-separated list of language tags from the server to the + * client. Can be empty if the value is missing or the message is malformed + */ + std::string getLanguagesServerToClient() { return getFieldValue(9); } + + /** + * @return Indicates whether a guessed key exchange packet follows. If a + * guessed packet will be sent, the return value is true. If no guessed + * packet will be sent or if this value is missing, the return value is false. + */ + bool isFirstKexPacketFollows(); + + private: + size_t m_FieldOffsets[11]; + bool m_OffsetsInitialized; + + void parseMessageAndInitOffsets(); + + std::string getFieldValue(int fieldOffsetIndex); +}; + +/** + * @class SSHEncryptedMessage + * A class representing an SSH encrypted message. In such messages there is very + * little information to extract from the packet, hence this class doesn't + * expose any methods or getters, other than the ones inherited from parent + * classes. + * + * It is assumed that any SSH message which does not fit to any of the other SSH + * message types, according to the heuristics described in the SSHLayer.h file + * description, is considered as an encrypted message. + */ +class SSHEncryptedMessage : public SSHLayer { + public: + /** + * A c'tor for this class that accepts raw message data. Please avoid using it + * as it's used internally when parsing SSH messagess in + * SSHLayer#createSSHMessage() + */ + SSHEncryptedMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet) + : SSHLayer(data, dataLen, prevLayer, packet) {} + + // implement abstract methods + + /** + * @return The size of the message which is equal to the size of the layer + */ + size_t getHeaderLen() const { return m_DataLen; } + + std::string toString() const; +}; + +} // namespace pcpp #endif // PACKETPP_SSH_LAYER diff --git a/Packet++/header/SSLCommon.h b/Packet++/header/SSLCommon.h index 6a7c7895cb..43694ea1ac 100644 --- a/Packet++/header/SSLCommon.h +++ b/Packet++/header/SSLCommon.h @@ -1,612 +1,598 @@ #ifndef PACKETPP_SSL_LAYER_COMMON #define PACKETPP_SSL_LAYER_COMMON -#include #include +#include /** * @file - * See detailed explanation of the TLS/SSL protocol support in PcapPlusPlus in SSLLayer.h + * See detailed explanation of the TLS/SSL protocol support in PcapPlusPlus in + * SSLLayer.h */ /** * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ +namespace pcpp { - /** - * @struct ssl_tls_record_layer - * The common part of all SSL/TLS messages - */ +/** + * @struct ssl_tls_record_layer + * The common part of all SSL/TLS messages + */ #pragma pack(push, 1) - struct ssl_tls_record_layer - { - /** Message (record) type (one of ::SSLRecordType) */ - uint8_t recordType; - /** Message (record) version (one of SSLVersion::SSLVersionEnum) */ - uint16_t recordVersion; - /** Message (record) length in bytes */ - uint16_t length; - }; +struct ssl_tls_record_layer { + /** Message (record) type (one of ::SSLRecordType) */ + uint8_t recordType; + /** Message (record) version (one of SSLVersion::SSLVersionEnum) */ + uint16_t recordVersion; + /** Message (record) length in bytes */ + uint16_t length; +}; #pragma pack(pop) - - /** - * @struct ssl_tls_handshake_layer - * The common part of all SSL/TLS handshake message types - */ +/** + * @struct ssl_tls_handshake_layer + * The common part of all SSL/TLS handshake message types + */ #pragma pack(push, 1) - struct ssl_tls_handshake_layer - { - /** Type of the handshake message (one of ::SSLHandshakeType) */ - uint8_t handshakeType; - /** Length of the message. Length is 3-Byte long, This is the MSB byte */ - uint8_t length1; - /** Length of the message. Length is 3-Byte long, This is the 2 LSB bytes */ - uint16_t length2; - }; +struct ssl_tls_handshake_layer { + /** Type of the handshake message (one of ::SSLHandshakeType) */ + uint8_t handshakeType; + /** Length of the message. Length is 3-Byte long, This is the MSB byte */ + uint8_t length1; + /** Length of the message. Length is 3-Byte long, This is the 2 LSB bytes */ + uint16_t length2; +}; #pragma pack(pop) - - /** - * @struct ssl_tls_client_server_hello - * The common header part of client-hello and server-hello handshake messages - */ +/** + * @struct ssl_tls_client_server_hello + * The common header part of client-hello and server-hello handshake messages + */ #pragma pack(push, 1) - struct ssl_tls_client_server_hello : ssl_tls_handshake_layer - { - /** SSL/TLS handshake version (one of SSLVersion::SSLVersionEnum) */ - uint16_t handshakeVersion; - /** 32-bytes random number */ - uint8_t random[32]; - }; +struct ssl_tls_client_server_hello : ssl_tls_handshake_layer { + /** SSL/TLS handshake version (one of SSLVersion::SSLVersionEnum) */ + uint16_t handshakeVersion; + /** 32-bytes random number */ + uint8_t random[32]; +}; #pragma pack(pop) - - /** - * @struct ssl_tls_change_cipher_spec - * SSL/TLS change-cipher-spec message structure - */ +/** + * @struct ssl_tls_change_cipher_spec + * SSL/TLS change-cipher-spec message structure + */ #pragma pack(push, 1) - struct ssl_tls_change_cipher_spec - { - /** Unused byte */ - uint8_t changeCipherSpec; - }; +struct ssl_tls_change_cipher_spec { + /** Unused byte */ + uint8_t changeCipherSpec; +}; #pragma pack(pop) - - /** - * @struct ssl_tls_alert - * SSL/TLS alert message structure - */ +/** + * @struct ssl_tls_alert + * SSL/TLS alert message structure + */ #pragma pack(push, 1) - struct ssl_tls_alert - { - /** Alert level (one of ::SSLAlertLevel) */ - uint8_t alertLevel; - /** Alert description (one of ::SSLAlertDescription) */ - uint8_t alertDescription; - }; +struct ssl_tls_alert { + /** Alert level (one of ::SSLAlertLevel) */ + uint8_t alertLevel; + /** Alert description (one of ::SSLAlertDescription) */ + uint8_t alertDescription; +}; #pragma pack(pop) +/** + * SSL/TLS message types + */ +enum SSLRecordType { + /** Change-cipher-spec message */ + SSL_CHANGE_CIPHER_SPEC = 20, + /** SSL alert message */ + SSL_ALERT = 21, + /** SSL handshake message */ + SSL_HANDSHAKE = 22, + /** SSL data message */ + SSL_APPLICATION_DATA = 23 +}; - /** - * SSL/TLS message types - */ - enum SSLRecordType - { - /** Change-cipher-spec message */ - SSL_CHANGE_CIPHER_SPEC = 20, - /** SSL alert message */ - SSL_ALERT = 21, - /** SSL handshake message */ - SSL_HANDSHAKE = 22, - /** SSL data message */ - SSL_APPLICATION_DATA = 23 - }; - - - /** - * @class SSLVersion - * A wrapper class for SSL/TLS versions. The SSL/TLS version is typically represented by a 2-byte number, - * for example TLS 1.2 is represented by 0x0303. - * This class wraps the numeric value and provides methods to convert it into an enum, string, etc. - */ - class SSLVersion - { - public: - /** - * SSL/TLS versions enum - */ - enum SSLVersionEnum - { - /** SSL 2.0 */ - SSL2 = 0x0200, - /** SSL 3.0 */ - SSL3 = 0x0300, - /** TLS 1.0 */ - TLS1_0 = 0x0301, - /** TLS 1.1 */ - TLS1_1 = 0x0302, - /** TLS 1.2 */ - TLS1_2 = 0x0303, - /** TLS 1.3 */ - TLS1_3 = 0x0304, - /** TLS 1.3 (draft 14) */ - TLS1_3_D14 = 0x7f0e, - /** TLS 1.3 (draft 15) */ - TLS1_3_D15 = 0x7f0f, - /** TLS 1.3 (draft 16) */ - TLS1_3_D16 = 0x7f10, - /** TLS 1.3 (draft 17) */ - TLS1_3_D17 = 0x7f11, - /** TLS 1.3 (draft 18) */ - TLS1_3_D18 = 0x7f12, - /** TLS 1.3 (draft 19) */ - TLS1_3_D19 = 0x7f13, - /** TLS 1.3 (draft 20) */ - TLS1_3_D20 = 0x7f14, - /** TLS 1.3 (draft 21) */ - TLS1_3_D21 = 0x7f15, - /** TLS 1.3 (draft 22) */ - TLS1_3_D22 = 0x7f16, - /** TLS 1.3 (draft 23) */ - TLS1_3_D23 = 0x7f17, - /** TLS 1.3 (draft 24) */ - TLS1_3_D24 = 0x7f18, - /** TLS 1.3 (draft 25) */ - TLS1_3_D25 = 0x7f19, - /** TLS 1.3 (draft 26) */ - TLS1_3_D26 = 0x7f1a, - /** TLS 1.3 (draft 27) */ - TLS1_3_D27 = 0x7f1b, - /** TLS 1.3 (draft 28) */ - TLS1_3_D28 = 0x7f1c, - /** TLS 1.3 (Facebook draft 23) */ - TLS1_3_FBD23 = 0xfb17, - /** TLS 1.3 (Facebook draft 26) */ - TLS1_3_FBD26 = 0xfb1a, - /** Unknown value */ - Unknown = 0 - }; - - /** - * A c'tor for this class. - * @param[in] sslVersionValue The numeric value representing this SSL/TLS version. For example: - * for TLS 1.2 this would be 0x0303. - */ - explicit SSLVersion(uint16_t sslVersionValue) { m_SSLVersionValue = sslVersionValue; } - - /** - * @return An enum value of type SSLVersion::SSLVersionEnum representing the SSL/TLS version. - * If the numeric value is an invalid SSL/TLS version SSLVersion::Unknown will be returned. - * @param[in] countTlsDraftsAs1_3 A flag indicating whether to return the enum value SSLVersion::TLS1_3 for all TLS 1.3 drafts. If set to "true" - * all TLS 1.3 draft values (i.e 0x7f0e - 0x7f1c, 0xfb17, 0xfb1a) will return SSLVersion::TLS1_3, otherwise the corresponding enum values will be - * returned. The default value is "false". - */ - SSLVersionEnum asEnum(bool countTlsDraftsAs1_3 = false); - - /** - * @return The numeric value of the SSL/TLs version - */ - uint16_t asUInt() { return m_SSLVersionValue; } - - /** - * @return A string representation of the SSL/TLS version. For example: for TLS 1.2 the string "TLS 1.2" is returned. - * If the numeric value is an invalid SSL/TLS version the string "Unknown" will be returned. - * @param[in] countTlsDraftsAs1_3 A flag indicating whether to return the string value "TLS 1.3" for all TLS 1.3 drafts. If set to "true" - * all TLS 1.3 draft values (i.e 0x7f0e - 0x7f1c, 0xfb17, 0xfb1a) will return "TLS 1.3", otherwise the corresponding string values will be - * returned. The default value is "false". - */ - std::string toString(bool countTlsDraftsAs1_3 = false); - - private: - uint16_t m_SSLVersionValue; - - // unimplemented empty c'tor - SSLVersion(); - }; - - /** - * SSL/TLS handshake message types - */ - enum SSLHandshakeType - { - /** Hello-request message type */ - SSL_HELLO_REQUEST = 0, - /** Client-hello message type */ - SSL_CLIENT_HELLO = 1, - /** Server-hello message type */ - SSL_SERVER_HELLO = 2, - /** New-session-ticket message type */ - SSL_NEW_SESSION_TICKET = 4, - /** End-of-early-data message type (TLS 1.3) */ - SSL_END_OF_EARLY_DATE = 5, - /** Encrypted-extensions message type (TLS 1.3) */ - SSL_ENCRYPTED_EXTENSIONS = 8, - /** Certificate message type */ - SSL_CERTIFICATE = 11, - /** Server-key-exchange message type */ - SSL_SERVER_KEY_EXCHANGE = 12, - /** Certificate-request message type */ - SSL_CERTIFICATE_REQUEST = 13, - /** Server-hello-done message type */ - SSL_SERVER_DONE = 14, - /** Certificate-verify message type */ - SSL_CERTIFICATE_VERIFY = 15, - /** Client-key-exchange message type */ - SSL_CLIENT_KEY_EXCHANGE = 16, - /** Finish message type */ - SSL_FINISHED = 20, - /** Key-update message type (TLS 1.3) */ - SSL_KEY_UPDATE = 24, - /** Unknown SSL handshake message */ - SSL_HANDSHAKE_UNKNOWN = 255 - }; +/** + * @class SSLVersion + * A wrapper class for SSL/TLS versions. The SSL/TLS version is typically + * represented by a 2-byte number, for example TLS 1.2 is represented by 0x0303. + * This class wraps the numeric value and provides methods to convert it into an + * enum, string, etc. + */ +class SSLVersion { + public: + /** + * SSL/TLS versions enum + */ + enum SSLVersionEnum { + /** SSL 2.0 */ + SSL2 = 0x0200, + /** SSL 3.0 */ + SSL3 = 0x0300, + /** TLS 1.0 */ + TLS1_0 = 0x0301, + /** TLS 1.1 */ + TLS1_1 = 0x0302, + /** TLS 1.2 */ + TLS1_2 = 0x0303, + /** TLS 1.3 */ + TLS1_3 = 0x0304, + /** TLS 1.3 (draft 14) */ + TLS1_3_D14 = 0x7f0e, + /** TLS 1.3 (draft 15) */ + TLS1_3_D15 = 0x7f0f, + /** TLS 1.3 (draft 16) */ + TLS1_3_D16 = 0x7f10, + /** TLS 1.3 (draft 17) */ + TLS1_3_D17 = 0x7f11, + /** TLS 1.3 (draft 18) */ + TLS1_3_D18 = 0x7f12, + /** TLS 1.3 (draft 19) */ + TLS1_3_D19 = 0x7f13, + /** TLS 1.3 (draft 20) */ + TLS1_3_D20 = 0x7f14, + /** TLS 1.3 (draft 21) */ + TLS1_3_D21 = 0x7f15, + /** TLS 1.3 (draft 22) */ + TLS1_3_D22 = 0x7f16, + /** TLS 1.3 (draft 23) */ + TLS1_3_D23 = 0x7f17, + /** TLS 1.3 (draft 24) */ + TLS1_3_D24 = 0x7f18, + /** TLS 1.3 (draft 25) */ + TLS1_3_D25 = 0x7f19, + /** TLS 1.3 (draft 26) */ + TLS1_3_D26 = 0x7f1a, + /** TLS 1.3 (draft 27) */ + TLS1_3_D27 = 0x7f1b, + /** TLS 1.3 (draft 28) */ + TLS1_3_D28 = 0x7f1c, + /** TLS 1.3 (Facebook draft 23) */ + TLS1_3_FBD23 = 0xfb17, + /** TLS 1.3 (Facebook draft 26) */ + TLS1_3_FBD26 = 0xfb1a, + /** Unknown value */ + Unknown = 0 + }; + + /** + * A c'tor for this class. + * @param[in] sslVersionValue The numeric value representing this SSL/TLS + * version. For example: for TLS 1.2 this would be 0x0303. + */ + explicit SSLVersion(uint16_t sslVersionValue) { + m_SSLVersionValue = sslVersionValue; + } + + /** + * @return An enum value of type SSLVersion::SSLVersionEnum representing the + * SSL/TLS version. If the numeric value is an invalid SSL/TLS version + * SSLVersion::Unknown will be returned. + * @param[in] countTlsDraftsAs1_3 A flag indicating whether to return the enum + * value SSLVersion::TLS1_3 for all TLS 1.3 drafts. If set to "true" all + * TLS 1.3 draft values (i.e 0x7f0e - 0x7f1c, 0xfb17, 0xfb1a) will return + * SSLVersion::TLS1_3, otherwise the corresponding enum values will be + * returned. The default value is "false". + */ + SSLVersionEnum asEnum(bool countTlsDraftsAs1_3 = false); + + /** + * @return The numeric value of the SSL/TLs version + */ + uint16_t asUInt() { return m_SSLVersionValue; } + + /** + * @return A string representation of the SSL/TLS version. For example: for + * TLS 1.2 the string "TLS 1.2" is returned. If the numeric value is an + * invalid SSL/TLS version the string "Unknown" will be returned. + * @param[in] countTlsDraftsAs1_3 A flag indicating whether to return the + * string value "TLS 1.3" for all TLS 1.3 drafts. If set to "true" all TLS 1.3 + * draft values (i.e 0x7f0e - 0x7f1c, 0xfb17, 0xfb1a) will return "TLS 1.3", + * otherwise the corresponding string values will be returned. The default + * value is "false". + */ + std::string toString(bool countTlsDraftsAs1_3 = false); + + private: + uint16_t m_SSLVersionValue; + + // unimplemented empty c'tor + SSLVersion(); +}; - /** - * SSL/TLS alert levels - */ - enum SSLAlertLevel - { - /** Warning level alert */ - SSL_ALERT_LEVEL_WARNING = 1, - /** Fatal level alert */ - SSL_ALERT_LEVEL_FATAL = 2, - /** For encrypted alerts the level is unknown so this type will be returned */ - SSL_ALERT_LEVEL_ENCRYPTED = 255 - }; +/** + * SSL/TLS handshake message types + */ +enum SSLHandshakeType { + /** Hello-request message type */ + SSL_HELLO_REQUEST = 0, + /** Client-hello message type */ + SSL_CLIENT_HELLO = 1, + /** Server-hello message type */ + SSL_SERVER_HELLO = 2, + /** New-session-ticket message type */ + SSL_NEW_SESSION_TICKET = 4, + /** End-of-early-data message type (TLS 1.3) */ + SSL_END_OF_EARLY_DATE = 5, + /** Encrypted-extensions message type (TLS 1.3) */ + SSL_ENCRYPTED_EXTENSIONS = 8, + /** Certificate message type */ + SSL_CERTIFICATE = 11, + /** Server-key-exchange message type */ + SSL_SERVER_KEY_EXCHANGE = 12, + /** Certificate-request message type */ + SSL_CERTIFICATE_REQUEST = 13, + /** Server-hello-done message type */ + SSL_SERVER_DONE = 14, + /** Certificate-verify message type */ + SSL_CERTIFICATE_VERIFY = 15, + /** Client-key-exchange message type */ + SSL_CLIENT_KEY_EXCHANGE = 16, + /** Finish message type */ + SSL_FINISHED = 20, + /** Key-update message type (TLS 1.3) */ + SSL_KEY_UPDATE = 24, + /** Unknown SSL handshake message */ + SSL_HANDSHAKE_UNKNOWN = 255 +}; - /** - * SSL/TLS alert description types - */ - enum SSLAlertDescription - { - /** Close notify alert */ - SSL_ALERT_CLOSE_NOTIFY = 0, - /** Unexpected message alert */ - SSL_ALERT_UNEXPECTED_MESSAGE = 10, - /** Bad record MAC alert */ - SSL_ALERT_BAD_RECORD_MAC = 20, - /** Decryption failed alert */ - SSL_ALERT_DECRYPTION_FAILED = 21, - /** */ - SSL_ALERT_RECORD_OVERFLOW = 22, - /** Decompression failure alert */ - SSL_ALERT_DECOMPRESSION_FAILURE = 30, - /** Handshake failure alert */ - SSL_ALERT_HANDSHAKE_FAILURE = 40, - /** No certificate alert */ - SSL_ALERT_NO_CERTIFICATE = 41, - /** Bad certificate alert */ - SSL_ALERT_BAD_CERTIFICATE = 42, - /** Unsupported certificate */ - SSL_ALERT_UNSUPPORTED_CERTIFICATE = 43, - /** Certificate revoked alert */ - SSL_ALERT_CERTIFICATE_REVOKED = 44, - /** Certificate expired alert */ - SSL_ALERT_CERTIFICATE_EXPIRED = 45, - /** Certificate unknown alert */ - SSL_ALERT_CERTIFICATE_UNKNOWN = 46, - /** Illegal parameter alert */ - SSL_ALERT_ILLEGAL_PARAMETER = 47, - /** Unknown CA alert */ - SSL_ALERT_UNKNOWN_CA = 48, - /** Access denied alert */ - SSL_ALERT_ACCESS_DENIED = 49, - /** Decode error alert */ - SSL_ALERT_DECODE_ERROR = 50, - /** Decrypt error alert */ - SSL_ALERT_DECRYPT_ERROR = 51, - /** Export restriction alert */ - SSL_ALERT_EXPORT_RESTRICTION = 60, - /** Protocol version alert */ - SSL_ALERT_PROTOCOL_VERSION = 70, - /** Insufficient security alert */ - SSL_ALERT_INSUFFICIENT_SECURITY = 71, - /** Internal error alert */ - SSL_ALERT_INTERNAL_ERROR = 80, - /** User cancelled alert */ - SSL_ALERT_USER_CANCELLED = 90, - /** No negotiation alert */ - SSL_ALERT_NO_RENEGOTIATION = 100, - /** Unsupported extension alert */ - SSL_ALERT_UNSUPPORTED_EXTENSION = 110, - /** Encrtpyed alert (cannot determine its type) */ - SSL_ALERT_ENCRYPTED = 255 - }; +/** + * SSL/TLS alert levels + */ +enum SSLAlertLevel { + /** Warning level alert */ + SSL_ALERT_LEVEL_WARNING = 1, + /** Fatal level alert */ + SSL_ALERT_LEVEL_FATAL = 2, + /** For encrypted alerts the level is unknown so this type will be returned */ + SSL_ALERT_LEVEL_ENCRYPTED = 255 +}; - /** - * SSL/TLS key exchange algorithms - */ - enum SSLKeyExchangeAlgorithm - { - /** NULL value */ - SSL_KEYX_NULL, - /** RSA (Rivest-Shamir-Adleman) */ - SSL_KEYX_RSA, - /** Diffie-Hellman */ - SSL_KEYX_DH, - /** Diffie-Hellman ephemeral */ - SSL_KEYX_DHE, - /** Elliptic curve Diffie�Hellman */ - SSL_KEYX_ECDH, - /** Elliptic curve Diffie�Hellman ephemeral */ - SSL_KEYX_ECDHE, - /** Fortezza Crypto Card */ - SSL_KEYX_FORTEZZA, - /** Kerberos 5 */ - SSL_KEYX_KRB5, - /** Pre-Shared Key */ - SSL_KEYX_PSK, - /** GOST */ - SSL_KEYX_GOST, - /** Secure Remote Password */ - SSL_KEYX_SRP, - /** PCT */ - SSL_KEYX_PCT, - /** Unknown algorithm */ - SSL_KEYX_Unknown - }; +/** + * SSL/TLS alert description types + */ +enum SSLAlertDescription { + /** Close notify alert */ + SSL_ALERT_CLOSE_NOTIFY = 0, + /** Unexpected message alert */ + SSL_ALERT_UNEXPECTED_MESSAGE = 10, + /** Bad record MAC alert */ + SSL_ALERT_BAD_RECORD_MAC = 20, + /** Decryption failed alert */ + SSL_ALERT_DECRYPTION_FAILED = 21, + /** */ + SSL_ALERT_RECORD_OVERFLOW = 22, + /** Decompression failure alert */ + SSL_ALERT_DECOMPRESSION_FAILURE = 30, + /** Handshake failure alert */ + SSL_ALERT_HANDSHAKE_FAILURE = 40, + /** No certificate alert */ + SSL_ALERT_NO_CERTIFICATE = 41, + /** Bad certificate alert */ + SSL_ALERT_BAD_CERTIFICATE = 42, + /** Unsupported certificate */ + SSL_ALERT_UNSUPPORTED_CERTIFICATE = 43, + /** Certificate revoked alert */ + SSL_ALERT_CERTIFICATE_REVOKED = 44, + /** Certificate expired alert */ + SSL_ALERT_CERTIFICATE_EXPIRED = 45, + /** Certificate unknown alert */ + SSL_ALERT_CERTIFICATE_UNKNOWN = 46, + /** Illegal parameter alert */ + SSL_ALERT_ILLEGAL_PARAMETER = 47, + /** Unknown CA alert */ + SSL_ALERT_UNKNOWN_CA = 48, + /** Access denied alert */ + SSL_ALERT_ACCESS_DENIED = 49, + /** Decode error alert */ + SSL_ALERT_DECODE_ERROR = 50, + /** Decrypt error alert */ + SSL_ALERT_DECRYPT_ERROR = 51, + /** Export restriction alert */ + SSL_ALERT_EXPORT_RESTRICTION = 60, + /** Protocol version alert */ + SSL_ALERT_PROTOCOL_VERSION = 70, + /** Insufficient security alert */ + SSL_ALERT_INSUFFICIENT_SECURITY = 71, + /** Internal error alert */ + SSL_ALERT_INTERNAL_ERROR = 80, + /** User cancelled alert */ + SSL_ALERT_USER_CANCELLED = 90, + /** No negotiation alert */ + SSL_ALERT_NO_RENEGOTIATION = 100, + /** Unsupported extension alert */ + SSL_ALERT_UNSUPPORTED_EXTENSION = 110, + /** Encrtpyed alert (cannot determine its type) */ + SSL_ALERT_ENCRYPTED = 255 +}; - /** - * SSL/TLS authentication algorithms - */ - enum SSLAuthenticationAlgorithm - { - /** NULL value */ - SSL_AUTH_NULL, - /** RSA (Rivest-Shamir-Adleman) */ - SSL_AUTH_RSA, - /** Digital Signature Standard */ - SSL_AUTH_DSS, - /** Anonymous */ - SSL_AUTH_anon, - /** Diffie-Hellman based key-exchange protocol */ - SSL_AUTH_KEA, - /** Kerberos 5 */ - SSL_AUTH_KRB5, - /** Pre-Shared Key */ - SSL_AUTH_PSK, - /** Elliptic Curve Digital Signature Algorithm */ - SSL_AUTH_ECDSA, - /** GOST */ - SSL_AUTH_GOST, - /** SHA-1 (Secure Hash Algorithm) */ - SSL_AUTH_SHA, - /** PCT */ - SSL_AUTH_PCT, - /** Diffie-Hellman ephemeral */ - SSL_AUTH_DHE, - /** Unknown algorithm */ - SSL_AUTH_Unknown - }; +/** + * SSL/TLS key exchange algorithms + */ +enum SSLKeyExchangeAlgorithm { + /** NULL value */ + SSL_KEYX_NULL, + /** RSA (Rivest-Shamir-Adleman) */ + SSL_KEYX_RSA, + /** Diffie-Hellman */ + SSL_KEYX_DH, + /** Diffie-Hellman ephemeral */ + SSL_KEYX_DHE, + /** Elliptic curve Diffie�Hellman */ + SSL_KEYX_ECDH, + /** Elliptic curve Diffie�Hellman ephemeral */ + SSL_KEYX_ECDHE, + /** Fortezza Crypto Card */ + SSL_KEYX_FORTEZZA, + /** Kerberos 5 */ + SSL_KEYX_KRB5, + /** Pre-Shared Key */ + SSL_KEYX_PSK, + /** GOST */ + SSL_KEYX_GOST, + /** Secure Remote Password */ + SSL_KEYX_SRP, + /** PCT */ + SSL_KEYX_PCT, + /** Unknown algorithm */ + SSL_KEYX_Unknown +}; - /** - * SSL/TLS symmetric encryption algorithms - */ - enum SSLSymetricEncryptionAlgorithm - { - /** NULL value */ - SSL_SYM_NULL, - /** RC4_40 */ - SSL_SYM_RC4_40, - /** RC4_128 */ - SSL_SYM_RC4_128, - /** RC2_CBC_40 */ - SSL_SYM_RC2_CBC_40, - /** IDEA_CBC */ - SSL_SYM_IDEA_CBC, - /** DES40_CBC */ - SSL_SYM_DES40_CBC, - /** DES_CBC */ - SSL_SYM_DES_CBC, - /** 3DES_EDE_CBC */ - SSL_SYM_3DES_EDE_CBC, - /** FORTEZZA_CBC */ - SSL_SYM_FORTEZZA_CBC, - /** DES_CBC_40 */ - SSL_SYM_DES_CBC_40, - /** AES_128_CBC */ - SSL_SYM_AES_128_CBC, - /** AES_256_CBC */ - SSL_SYM_AES_256_CBC, - /** CAMELLIA_128_CBC */ - SSL_SYM_CAMELLIA_128_CBC, - /** CAMELLIA_128_GCM */ - SSL_SYM_CAMELLIA_128_GCM, - /** CAMELLIA_256_GCM */ - SSL_SYM_CAMELLIA_256_GCM, - /** RC4_56 */ - SSL_SYM_RC4_56, - /** RC2_CBC_56 */ - SSL_SYM_RC2_CBC_56, - /** GOST28147 */ - SSL_SYM_GOST28147, - /** CAMELLIA_256_CBC */ - SSL_SYM_CAMELLIA_256_CBC, - /** SEED_CBC */ - SSL_SYM_SEED_CBC, - /** AES_128 */ - SSL_SYM_AES_128, - /** AES_256 */ - SSL_SYM_AES_256, - /** SSL_SYM_AES_128_GCM */ - SSL_SYM_AES_128_GCM, - /** AES_256_GCM */ - SSL_SYM_AES_256_GCM, - /** RC4_128_EXPORT40 */ - SSL_SYM_RC4_128_EXPORT40, - /** RC2_CBC_128_CBC */ - SSL_SYM_RC2_CBC_128_CBC, - /** IDEA_128_CBC */ - SSL_SYM_IDEA_128_CBC, - /** DES_64_CBC */ - SSL_SYM_DES_64_CBC, - /** DES_192_EDE3_CBC */ - SSL_SYM_DES_192_EDE3_CBC, - /** RC4_64 */ - SSL_SYM_RC4_64, - /** ARIA_128_CBC*/ - SSL_SYM_ARIA_128_CBC, - /** ARIA_256_CBC */ - SSL_SYM_ARIA_256_CBC, - /** ARIA_128_GCM */ - SSL_SYM_ARIA_128_GCM, - /** ARIA_256_GCM */ - SSL_SYM_ARIA_256_GCM, - /** CHACHA20_POLY1305 */ - SSL_SYM_CHACHA20_POLY1305, - /** AES_128_CCM */ - SSL_SYM_AES_128_CCM, - /** AES_128_CCM_8 */ - SSL_SYM_AES_128_CCM_8, - /** Unknown algorithm */ - SSL_SYM_Unknown - }; +/** + * SSL/TLS authentication algorithms + */ +enum SSLAuthenticationAlgorithm { + /** NULL value */ + SSL_AUTH_NULL, + /** RSA (Rivest-Shamir-Adleman) */ + SSL_AUTH_RSA, + /** Digital Signature Standard */ + SSL_AUTH_DSS, + /** Anonymous */ + SSL_AUTH_anon, + /** Diffie-Hellman based key-exchange protocol */ + SSL_AUTH_KEA, + /** Kerberos 5 */ + SSL_AUTH_KRB5, + /** Pre-Shared Key */ + SSL_AUTH_PSK, + /** Elliptic Curve Digital Signature Algorithm */ + SSL_AUTH_ECDSA, + /** GOST */ + SSL_AUTH_GOST, + /** SHA-1 (Secure Hash Algorithm) */ + SSL_AUTH_SHA, + /** PCT */ + SSL_AUTH_PCT, + /** Diffie-Hellman ephemeral */ + SSL_AUTH_DHE, + /** Unknown algorithm */ + SSL_AUTH_Unknown +}; - /** - * SSL/TLS hashing algorithms - */ - enum SSLHashingAlgorithm - { - /** NULL value */ - SSL_HASH_NULL, - /** Message-Digest Algorithm */ - SSL_HASH_MD5, - /** SHA-1 (Secure Hash Algorithm) */ - SSL_HASH_SHA, - /** SHA-256 (Secure Hash Algorithm) */ - SSL_HASH_SHA256, - /** GOST 28147 */ - SSL_HASH_GOST28147, - /** GOST R 34.11 */ - SSL_HASH_GOSTR3411, - /** SHA-384 (Secure Hash Algorithm) */ - SSL_HASH_SHA384, - /** CCM mode (Counter with CBC-MAC) */ - SSL_HASH_CCM, - /** CCM mode (Counter with CBC-MAC) */ - SSL_HASH_CCM_8, - /** Unknown algorithm */ - SSL_HASH_Unknown - }; +/** + * SSL/TLS symmetric encryption algorithms + */ +enum SSLSymetricEncryptionAlgorithm { + /** NULL value */ + SSL_SYM_NULL, + /** RC4_40 */ + SSL_SYM_RC4_40, + /** RC4_128 */ + SSL_SYM_RC4_128, + /** RC2_CBC_40 */ + SSL_SYM_RC2_CBC_40, + /** IDEA_CBC */ + SSL_SYM_IDEA_CBC, + /** DES40_CBC */ + SSL_SYM_DES40_CBC, + /** DES_CBC */ + SSL_SYM_DES_CBC, + /** 3DES_EDE_CBC */ + SSL_SYM_3DES_EDE_CBC, + /** FORTEZZA_CBC */ + SSL_SYM_FORTEZZA_CBC, + /** DES_CBC_40 */ + SSL_SYM_DES_CBC_40, + /** AES_128_CBC */ + SSL_SYM_AES_128_CBC, + /** AES_256_CBC */ + SSL_SYM_AES_256_CBC, + /** CAMELLIA_128_CBC */ + SSL_SYM_CAMELLIA_128_CBC, + /** CAMELLIA_128_GCM */ + SSL_SYM_CAMELLIA_128_GCM, + /** CAMELLIA_256_GCM */ + SSL_SYM_CAMELLIA_256_GCM, + /** RC4_56 */ + SSL_SYM_RC4_56, + /** RC2_CBC_56 */ + SSL_SYM_RC2_CBC_56, + /** GOST28147 */ + SSL_SYM_GOST28147, + /** CAMELLIA_256_CBC */ + SSL_SYM_CAMELLIA_256_CBC, + /** SEED_CBC */ + SSL_SYM_SEED_CBC, + /** AES_128 */ + SSL_SYM_AES_128, + /** AES_256 */ + SSL_SYM_AES_256, + /** SSL_SYM_AES_128_GCM */ + SSL_SYM_AES_128_GCM, + /** AES_256_GCM */ + SSL_SYM_AES_256_GCM, + /** RC4_128_EXPORT40 */ + SSL_SYM_RC4_128_EXPORT40, + /** RC2_CBC_128_CBC */ + SSL_SYM_RC2_CBC_128_CBC, + /** IDEA_128_CBC */ + SSL_SYM_IDEA_128_CBC, + /** DES_64_CBC */ + SSL_SYM_DES_64_CBC, + /** DES_192_EDE3_CBC */ + SSL_SYM_DES_192_EDE3_CBC, + /** RC4_64 */ + SSL_SYM_RC4_64, + /** ARIA_128_CBC*/ + SSL_SYM_ARIA_128_CBC, + /** ARIA_256_CBC */ + SSL_SYM_ARIA_256_CBC, + /** ARIA_128_GCM */ + SSL_SYM_ARIA_128_GCM, + /** ARIA_256_GCM */ + SSL_SYM_ARIA_256_GCM, + /** CHACHA20_POLY1305 */ + SSL_SYM_CHACHA20_POLY1305, + /** AES_128_CCM */ + SSL_SYM_AES_128_CCM, + /** AES_128_CCM_8 */ + SSL_SYM_AES_128_CCM_8, + /** Unknown algorithm */ + SSL_SYM_Unknown +}; - /** - * SSL/TLS extension types - */ - enum SSLExtensionType - { - /** Server Name Indication extension */ - SSL_EXT_SERVER_NAME = 0, - /** Maximum Fragment Length Negotiation extension */ - SSL_EXT_MAX_FRAGMENT_LENGTH = 1, - /** Client Certificate URLs extension */ - SSL_EXT_CLIENT_CERTIFICATE_URL = 2, - /** Trusted CA Indication extension */ - SSL_EXT_TRUSTED_CA_KEYS = 3, - /** Truncated HMAC extension */ - SSL_EXT_TRUNCATED_HMAC = 4, - /** Certificate Status Request extension */ - SSL_EXT_STATUS_REQUEST = 5, - /** TLS User Mapping extension */ - SSL_EXT_USER_MAPPING = 6, - /** Client Authorization extension */ - SSL_EXT_CLIENT_AUTHZ = 7, - /** Server Authorization extension */ - SSL_EXT_SERVER_AUTHZ = 8, - /** Certificate Type extension */ - SSL_EXT_CERT_TYPE = 9, - /** Supported Groups extension (renamed from "elliptic curves") */ - SSL_EXT_SUPPORTED_GROUPS = 10, - /** Elliptic Curves Point Format extension */ - SSL_EXT_EC_POINT_FORMATS = 11, - /** Secure Remote Password extension */ - SSL_EXT_SRP = 12, - /** Signature Algorithms extension */ - SSL_EXT_SIGNATURE_ALGORITHMS = 13, - /** Use Secure Real-time Transport Protocol extension */ - SSL_EXT_USE_SRTP = 14, - /** TLS Heartbit extension */ - SSL_EXT_HEARTBEAT = 15, - /** Application Layer Protocol Negotiation (ALPN) extension */ - SSL_EXT_APPLICATION_LAYER_PROTOCOL_NEGOTIATION = 16, - /** Status Request extension */ - SSL_EXT_STATUS_REQUEST_V2 = 17, - /** Signed Certificate Timestamp extension */ - SSL_EXT_SIGNED_CERTIFICATE_TIMESTAMP = 18, - /** Client Certificate Type extension */ - SSL_EXT_CLIENT_CERTIFICATE_TYPE = 19, - /** Server Certificate Type extension */ - SSL_EXT_SERVER_CERTIFICATE_TYPE = 20, - /** ClientHello Padding extension */ - SSL_EXT_PADDING = 21, - /** Encrypt-then-MAC extension */ - SSL_EXT_ENCRYPT_THEN_MAC = 22, - /** Extended Master Secret extension */ - SSL_EXT_EXTENDED_MASTER_SECRET = 23, - /** Token Binding extension */ - SSL_EXT_TOKEN_BINDING = 24, - /** SessionTicket TLS extension */ - SSL_EXT_SESSIONTICKET_TLS = 35, - /** Pre-shared key (PSK) extension (TLS 1.3) */ - SSL_EXT_PRE_SHARED_KEY = 41, - /** Early data extension (TLS 1.3) */ - SSL_EXT_EARLY_DATA = 42, - /** Supported versions extension (TLS 1.3) */ - SSL_EXT_SUPPORTED_VERSIONS = 43, - /** Cookie extension (TLS 1.3) */ - SSL_EXT_COOKIE = 44, - /** Pre-Shared Key Exchange Modes extension (TLS 1.3) */ - SSL_EXT_PSK_KEY_EXCHANGE_MODES = 45, - /** Certificate authorities extension (TLS 1.3) */ - SSL_EXT_CERTIFICATE_AUTHORITIES = 47, - /** Old filters extension (TLS 1.3) */ - SSL_EXT_OLD_FILTERS = 48, - /** Post handshake auth extension (TLS 1.3) */ - SSL_EXT_POST_HANDSHAKE_AUTH = 49, - /** Signature algorithm cert extension (TLS 1.3) */ - SSL_EXT_SIGNATURE_ALGORITHM_CERT = 50, - /** Key share extension (TLS 1.3) */ - SSL_EXT_KEY_SHARE = 51, - /** Renegotiation Indication extension */ - SSL_EXT_RENEGOTIATION_INFO = 65281, - /** Unknown extension */ - SSL_EXT_Unknown - }; +/** + * SSL/TLS hashing algorithms + */ +enum SSLHashingAlgorithm { + /** NULL value */ + SSL_HASH_NULL, + /** Message-Digest Algorithm */ + SSL_HASH_MD5, + /** SHA-1 (Secure Hash Algorithm) */ + SSL_HASH_SHA, + /** SHA-256 (Secure Hash Algorithm) */ + SSL_HASH_SHA256, + /** GOST 28147 */ + SSL_HASH_GOST28147, + /** GOST R 34.11 */ + SSL_HASH_GOSTR3411, + /** SHA-384 (Secure Hash Algorithm) */ + SSL_HASH_SHA384, + /** CCM mode (Counter with CBC-MAC) */ + SSL_HASH_CCM, + /** CCM mode (Counter with CBC-MAC) */ + SSL_HASH_CCM_8, + /** Unknown algorithm */ + SSL_HASH_Unknown +}; - /** - * SSL/TLS client certificate types - */ - enum SSLClientCertificateType - { - /** RSA_SIGN */ - SSL_CCT_RSA_SIGN = 1, - /** DSS_SIGN */ - SSL_CCT_DSS_SIGN = 2, - /** RSA_FIXED_DH */ - SSL_CCT_RSA_FIXED_DH = 3, - /** DSS_FIXED_DH */ - SSL_CCT_DSS_FIXED_DH = 4, - /** RSA_EPHEMERAL_DH_RESERVED */ - SSL_CCT_RSA_EPHEMERAL_DH_RESERVED = 5, - /** DSS_EPHEMERAL_DH_RESERVED */ - SSL_CCT_DSS_EPHEMERAL_DH_RESERVED = 6, - /** FORTEZZA_DMS_RESERVED */ - SSL_CCT_FORTEZZA_DMS_RESERVED = 20, - /** ECDSA_SIGN */ - SSL_CCT_ECDSA_SIGN = 64, - /** FIXED_ECDH */ - SSL_CCT_RSA_FIXED_ECDH = 65, - /** ECDSA_FIXED_ECDH */ - SSL_CCT_ECDSA_FIXED_ECDH = 66, - /** Unknown client certificate type */ - SSL_CCT_UNKNOWN - }; +/** + * SSL/TLS extension types + */ +enum SSLExtensionType { + /** Server Name Indication extension */ + SSL_EXT_SERVER_NAME = 0, + /** Maximum Fragment Length Negotiation extension */ + SSL_EXT_MAX_FRAGMENT_LENGTH = 1, + /** Client Certificate URLs extension */ + SSL_EXT_CLIENT_CERTIFICATE_URL = 2, + /** Trusted CA Indication extension */ + SSL_EXT_TRUSTED_CA_KEYS = 3, + /** Truncated HMAC extension */ + SSL_EXT_TRUNCATED_HMAC = 4, + /** Certificate Status Request extension */ + SSL_EXT_STATUS_REQUEST = 5, + /** TLS User Mapping extension */ + SSL_EXT_USER_MAPPING = 6, + /** Client Authorization extension */ + SSL_EXT_CLIENT_AUTHZ = 7, + /** Server Authorization extension */ + SSL_EXT_SERVER_AUTHZ = 8, + /** Certificate Type extension */ + SSL_EXT_CERT_TYPE = 9, + /** Supported Groups extension (renamed from "elliptic curves") */ + SSL_EXT_SUPPORTED_GROUPS = 10, + /** Elliptic Curves Point Format extension */ + SSL_EXT_EC_POINT_FORMATS = 11, + /** Secure Remote Password extension */ + SSL_EXT_SRP = 12, + /** Signature Algorithms extension */ + SSL_EXT_SIGNATURE_ALGORITHMS = 13, + /** Use Secure Real-time Transport Protocol extension */ + SSL_EXT_USE_SRTP = 14, + /** TLS Heartbit extension */ + SSL_EXT_HEARTBEAT = 15, + /** Application Layer Protocol Negotiation (ALPN) extension */ + SSL_EXT_APPLICATION_LAYER_PROTOCOL_NEGOTIATION = 16, + /** Status Request extension */ + SSL_EXT_STATUS_REQUEST_V2 = 17, + /** Signed Certificate Timestamp extension */ + SSL_EXT_SIGNED_CERTIFICATE_TIMESTAMP = 18, + /** Client Certificate Type extension */ + SSL_EXT_CLIENT_CERTIFICATE_TYPE = 19, + /** Server Certificate Type extension */ + SSL_EXT_SERVER_CERTIFICATE_TYPE = 20, + /** ClientHello Padding extension */ + SSL_EXT_PADDING = 21, + /** Encrypt-then-MAC extension */ + SSL_EXT_ENCRYPT_THEN_MAC = 22, + /** Extended Master Secret extension */ + SSL_EXT_EXTENDED_MASTER_SECRET = 23, + /** Token Binding extension */ + SSL_EXT_TOKEN_BINDING = 24, + /** SessionTicket TLS extension */ + SSL_EXT_SESSIONTICKET_TLS = 35, + /** Pre-shared key (PSK) extension (TLS 1.3) */ + SSL_EXT_PRE_SHARED_KEY = 41, + /** Early data extension (TLS 1.3) */ + SSL_EXT_EARLY_DATA = 42, + /** Supported versions extension (TLS 1.3) */ + SSL_EXT_SUPPORTED_VERSIONS = 43, + /** Cookie extension (TLS 1.3) */ + SSL_EXT_COOKIE = 44, + /** Pre-Shared Key Exchange Modes extension (TLS 1.3) */ + SSL_EXT_PSK_KEY_EXCHANGE_MODES = 45, + /** Certificate authorities extension (TLS 1.3) */ + SSL_EXT_CERTIFICATE_AUTHORITIES = 47, + /** Old filters extension (TLS 1.3) */ + SSL_EXT_OLD_FILTERS = 48, + /** Post handshake auth extension (TLS 1.3) */ + SSL_EXT_POST_HANDSHAKE_AUTH = 49, + /** Signature algorithm cert extension (TLS 1.3) */ + SSL_EXT_SIGNATURE_ALGORITHM_CERT = 50, + /** Key share extension (TLS 1.3) */ + SSL_EXT_KEY_SHARE = 51, + /** Renegotiation Indication extension */ + SSL_EXT_RENEGOTIATION_INFO = 65281, + /** Unknown extension */ + SSL_EXT_Unknown +}; -} //namespace pcpp +/** + * SSL/TLS client certificate types + */ +enum SSLClientCertificateType { + /** RSA_SIGN */ + SSL_CCT_RSA_SIGN = 1, + /** DSS_SIGN */ + SSL_CCT_DSS_SIGN = 2, + /** RSA_FIXED_DH */ + SSL_CCT_RSA_FIXED_DH = 3, + /** DSS_FIXED_DH */ + SSL_CCT_DSS_FIXED_DH = 4, + /** RSA_EPHEMERAL_DH_RESERVED */ + SSL_CCT_RSA_EPHEMERAL_DH_RESERVED = 5, + /** DSS_EPHEMERAL_DH_RESERVED */ + SSL_CCT_DSS_EPHEMERAL_DH_RESERVED = 6, + /** FORTEZZA_DMS_RESERVED */ + SSL_CCT_FORTEZZA_DMS_RESERVED = 20, + /** ECDSA_SIGN */ + SSL_CCT_ECDSA_SIGN = 64, + /** FIXED_ECDH */ + SSL_CCT_RSA_FIXED_ECDH = 65, + /** ECDSA_FIXED_ECDH */ + SSL_CCT_ECDSA_FIXED_ECDH = 66, + /** Unknown client certificate type */ + SSL_CCT_UNKNOWN +}; + +} // namespace pcpp #endif // PACKETPP_SSL_LAYER_COMMON diff --git a/Packet++/header/SSLHandshake.h b/Packet++/header/SSLHandshake.h index 8283f86747..e0ff79a71c 100644 --- a/Packet++/header/SSLHandshake.h +++ b/Packet++/header/SSLHandshake.h @@ -1,1142 +1,1227 @@ #ifndef PACKETPP_SSL_HANDSHAKE_MESSAGE #define PACKETPP_SSL_HANDSHAKE_MESSAGE -#include -#include "SSLCommon.h" #include "PointerVector.h" +#include "SSLCommon.h" +#include /** * @file - * See detailed explanation of the TLS/SSL protocol support in PcapPlusPlus in SSLLayer.h + * See detailed explanation of the TLS/SSL protocol support in PcapPlusPlus in + * SSLLayer.h */ /** * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - +namespace pcpp { /** * @class SSLCipherSuite - * Represents a cipher-suite and enables access all information about it such as all algorithms it encapsulates, - * its ID (as appears in the client-hello or server-hello messages), - * its name (e.g "TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA") etc. PcapPlusPlus contains static instances of this type - * for all known cipher-suites and enables access to them through name or ID (see getCipherSuiteByID() and - * getCipherSuiteByName() ). List of cipher-suite was taken from here: + * Represents a cipher-suite and enables access all information about it such as + * all algorithms it encapsulates, its ID (as appears in the client-hello or + * server-hello messages), its name (e.g "TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA") + * etc. PcapPlusPlus contains static instances of this type for all known + * cipher-suites and enables access to them through name or ID (see + * getCipherSuiteByID() and getCipherSuiteByName() ). List of cipher-suite was + * taken from here: * http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml */ -class SSLCipherSuite -{ -public: - /** - * A c'tor for this class, should never be used by a user - * @param[in] id Cipher-suite ID - * @param[in] keyExAlg Key-exchange algorithm used in this cipher-suite - * @param[in] authAlg Authentication algorithm used in this cipher-suite - * @param[in] symKeyAlg Symmetric key algorithm used in this cipher-suite - * @param[in] MACAlg MAC algorithm used in this cipher-suite - * @param[in] name String representation of this cipher-suite - */ - SSLCipherSuite(uint16_t id, SSLKeyExchangeAlgorithm keyExAlg, - SSLAuthenticationAlgorithm authAlg, - SSLSymetricEncryptionAlgorithm symKeyAlg, - SSLHashingAlgorithm MACAlg, - const char* name) - : m_Id(id), m_KeyExAlg(keyExAlg), m_AuthAlg(authAlg), m_SymKeyAlg(symKeyAlg), m_MACAlg(MACAlg), m_Name(name) {} - - /** - * @return Cipher-suite ID - */ - uint16_t getID() const { return m_Id; } - - /** - * @return String representation of this cipher-suite - */ - std::string asString() const { return m_Name; } - - /** - * @return Key-exchange algorithm used in this cipher-suite - */ - SSLKeyExchangeAlgorithm getKeyExchangeAlg() const { return m_KeyExAlg; } - - /** - * @return Authentication algorithm used in this cipher-suite - */ - SSLAuthenticationAlgorithm getAuthAlg() const { return m_AuthAlg; } - - /** - * @return Symmetric key algorithm used in this cipher-suite - */ - SSLSymetricEncryptionAlgorithm getSymKeyAlg() const { return m_SymKeyAlg; } - - /** - * @return MAC algorithm used in this cipher-suite - */ - SSLHashingAlgorithm getMACAlg() const { return m_MACAlg; } - - /** - * A static method that returns a cipher-suite instance by ID - * @param[in] id Cipher-suite ID - * @return A cipher-suite instance matching this ID or NULL if ID not found - */ - static SSLCipherSuite* getCipherSuiteByID(uint16_t id); - - /** - * A static method that returns a cipher-suite instance by name - * @param[in] name Cipher-suite name (e.g "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA") - * @return A cipher-suite instance matching this name or NULL if name not found - */ - static SSLCipherSuite* getCipherSuiteByName(std::string name); - -private: - uint16_t m_Id; - SSLKeyExchangeAlgorithm m_KeyExAlg; - SSLAuthenticationAlgorithm m_AuthAlg; - SSLSymetricEncryptionAlgorithm m_SymKeyAlg; - SSLHashingAlgorithm m_MACAlg; - std::string m_Name; +class SSLCipherSuite { + public: + /** + * A c'tor for this class, should never be used by a user + * @param[in] id Cipher-suite ID + * @param[in] keyExAlg Key-exchange algorithm used in this cipher-suite + * @param[in] authAlg Authentication algorithm used in this cipher-suite + * @param[in] symKeyAlg Symmetric key algorithm used in this cipher-suite + * @param[in] MACAlg MAC algorithm used in this cipher-suite + * @param[in] name String representation of this cipher-suite + */ + SSLCipherSuite(uint16_t id, SSLKeyExchangeAlgorithm keyExAlg, + SSLAuthenticationAlgorithm authAlg, + SSLSymetricEncryptionAlgorithm symKeyAlg, + SSLHashingAlgorithm MACAlg, const char* name) + : m_Id(id), m_KeyExAlg(keyExAlg), m_AuthAlg(authAlg), + m_SymKeyAlg(symKeyAlg), m_MACAlg(MACAlg), m_Name(name) {} + + /** + * @return Cipher-suite ID + */ + uint16_t getID() const { return m_Id; } + + /** + * @return String representation of this cipher-suite + */ + std::string asString() const { return m_Name; } + + /** + * @return Key-exchange algorithm used in this cipher-suite + */ + SSLKeyExchangeAlgorithm getKeyExchangeAlg() const { return m_KeyExAlg; } + + /** + * @return Authentication algorithm used in this cipher-suite + */ + SSLAuthenticationAlgorithm getAuthAlg() const { return m_AuthAlg; } + + /** + * @return Symmetric key algorithm used in this cipher-suite + */ + SSLSymetricEncryptionAlgorithm getSymKeyAlg() const { return m_SymKeyAlg; } + + /** + * @return MAC algorithm used in this cipher-suite + */ + SSLHashingAlgorithm getMACAlg() const { return m_MACAlg; } + + /** + * A static method that returns a cipher-suite instance by ID + * @param[in] id Cipher-suite ID + * @return A cipher-suite instance matching this ID or NULL if ID not found + */ + static SSLCipherSuite* getCipherSuiteByID(uint16_t id); + + /** + * A static method that returns a cipher-suite instance by name + * @param[in] name Cipher-suite name (e.g + * "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA") + * @return A cipher-suite instance matching this name or NULL if name not + * found + */ + static SSLCipherSuite* getCipherSuiteByName(std::string name); + + private: + uint16_t m_Id; + SSLKeyExchangeAlgorithm m_KeyExAlg; + SSLAuthenticationAlgorithm m_AuthAlg; + SSLSymetricEncryptionAlgorithm m_SymKeyAlg; + SSLHashingAlgorithm m_MACAlg; + std::string m_Name; }; - /** * @class SSLExtension - * Represents a SSL/TLS extension. This is a base class that can represent any type of extension. Inherited classes may - * contain parsing logic for specific extensions. This class provides capabilities such as getting the extension type, - * length and viewing the extension data as raw (byte array) + * Represents a SSL/TLS extension. This is a base class that can represent any + * type of extension. Inherited classes may contain parsing logic for specific + * extensions. This class provides capabilities such as getting the extension + * type, length and viewing the extension data as raw (byte array) */ -class SSLExtension -{ -public: - /** - * C'tor for this class - * @param[in] data The raw data for the extension - */ - explicit SSLExtension(uint8_t* data); - - virtual ~SSLExtension() { } - - /** - * @return The type of the extension as enum - */ - SSLExtensionType getType() const; - - /** - * @return The type of the extension as a numeric value - */ - uint16_t getTypeAsInt() const; - - /** - * @return The length of the extension data in bytes (not including the type and length fields) - */ - uint16_t getLength() const; - - /** - * @return The total length of the extension, including type and length fields and the extension data field - */ - uint16_t getTotalLength() const; - - /** - * @return A pointer to the raw data of the extension - */ - uint8_t* getData() const; - -protected: - - /** - * @struct SSLExtensionStruct - * Represents the common fields of the extension - */ - struct SSLExtensionStruct - { - /** Extension type */ - uint16_t extensionType; - /** Extension length */ - uint16_t extensionDataLength; - /** Extension data as raw (byte array) */ - uint8_t extensionData[]; - }; - - uint8_t* m_RawData; - - SSLExtensionStruct* getExtensionStruct() const { return (SSLExtensionStruct*)m_RawData; } +class SSLExtension { + public: + /** + * C'tor for this class + * @param[in] data The raw data for the extension + */ + explicit SSLExtension(uint8_t* data); + + virtual ~SSLExtension() {} + + /** + * @return The type of the extension as enum + */ + SSLExtensionType getType() const; + + /** + * @return The type of the extension as a numeric value + */ + uint16_t getTypeAsInt() const; + + /** + * @return The length of the extension data in bytes (not including the type + * and length fields) + */ + uint16_t getLength() const; + + /** + * @return The total length of the extension, including type and length fields + * and the extension data field + */ + uint16_t getTotalLength() const; + + /** + * @return A pointer to the raw data of the extension + */ + uint8_t* getData() const; + + protected: + /** + * @struct SSLExtensionStruct + * Represents the common fields of the extension + */ + struct SSLExtensionStruct { + /** Extension type */ + uint16_t extensionType; + /** Extension length */ + uint16_t extensionDataLength; + /** Extension data as raw (byte array) */ + uint8_t extensionData[]; + }; + + uint8_t* m_RawData; + + SSLExtensionStruct* getExtensionStruct() const { + return (SSLExtensionStruct*)m_RawData; + } }; - /** * @class SSLServerNameIndicationExtension - * Represents SSL/TLS Server Name Indication extension. Inherits from SSLExtension and add parsing of the hostname - * written in the extension data + * Represents SSL/TLS Server Name Indication extension. Inherits from + * SSLExtension and add parsing of the hostname written in the extension data */ -class SSLServerNameIndicationExtension : public SSLExtension -{ -public: - /** - * C'tor for this class - * @param[in] data The raw data for the extension - */ - explicit SSLServerNameIndicationExtension(uint8_t* data) : SSLExtension(data) {} - - /** - * @return The hostname written in the extension data - */ - std::string getHostName() const; +class SSLServerNameIndicationExtension : public SSLExtension { + public: + /** + * C'tor for this class + * @param[in] data The raw data for the extension + */ + explicit SSLServerNameIndicationExtension(uint8_t* data) + : SSLExtension(data) {} + + /** + * @return The hostname written in the extension data + */ + std::string getHostName() const; }; - /** * @class SSLSupportedVersionsExtension - * Represents TLS Supported Versions extension. Inherits from SSLExtension and adds parsing of the list - * of supported versions mentioned in the extension data + * Represents TLS Supported Versions extension. Inherits from SSLExtension and + * adds parsing of the list of supported versions mentioned in the extension + * data */ -class SSLSupportedVersionsExtension : public SSLExtension -{ -public: - /** - * C'tor for this class - * @param[in] data The raw data for the extension - */ - explicit SSLSupportedVersionsExtension(uint8_t* data) : SSLExtension(data) {} - - /** - * @return The list of supported versions mentioned in the extension data - */ - std::vector getSupportedVersions() const; +class SSLSupportedVersionsExtension : public SSLExtension { + public: + /** + * C'tor for this class + * @param[in] data The raw data for the extension + */ + explicit SSLSupportedVersionsExtension(uint8_t* data) : SSLExtension(data) {} + + /** + * @return The list of supported versions mentioned in the extension data + */ + std::vector getSupportedVersions() const; }; - /** * @class TLSSupportedGroupsExtension - * Represents TLS Supported Groups extension. Inherits from SSLExtension and adds parsing of the - * supported groups (Elliptic Curves) mentioned in the extension data + * Represents TLS Supported Groups extension. Inherits from SSLExtension and + * adds parsing of the supported groups (Elliptic Curves) mentioned in the + * extension data */ -class TLSSupportedGroupsExtension : public SSLExtension -{ - public: - /** - * C'tor for this class - * @param[in] data The raw data for the extension - */ - explicit TLSSupportedGroupsExtension(uint8_t* data) : SSLExtension(data) {} - - /** - * @return A vector of the supported groups (also known as "Elliptic Curves") - */ - std::vector getSupportedGroups() const; +class TLSSupportedGroupsExtension : public SSLExtension { + public: + /** + * C'tor for this class + * @param[in] data The raw data for the extension + */ + explicit TLSSupportedGroupsExtension(uint8_t* data) : SSLExtension(data) {} + + /** + * @return A vector of the supported groups (also known as "Elliptic Curves") + */ + std::vector getSupportedGroups() const; }; - /** * @class TLSECPointFormatExtension - * Represents TLS EC (Elliptic Curves) Point Format extension. Inherits from SSLExtension and adds parsing of the - * EC point formats mentioned in the extension data + * Represents TLS EC (Elliptic Curves) Point Format extension. Inherits from + * SSLExtension and adds parsing of the EC point formats mentioned in the + * extension data */ -class TLSECPointFormatExtension : public SSLExtension -{ - public: - /** - * C'tor for this class - * @param[in] data The raw data for the extension - */ - explicit TLSECPointFormatExtension(uint8_t* data) : SSLExtension(data) {} - - /** - * @return A vector of the elliptic curves point formats - */ - std::vector getECPointFormatList() const; +class TLSECPointFormatExtension : public SSLExtension { + public: + /** + * C'tor for this class + * @param[in] data The raw data for the extension + */ + explicit TLSECPointFormatExtension(uint8_t* data) : SSLExtension(data) {} + + /** + * @return A vector of the elliptic curves point formats + */ + std::vector getECPointFormatList() const; }; - /** * @class SSLx509Certificate - * Represents a x509v3 certificate. the SSLCertificateMessage class returns an instance of this class as the certificate. - * Currently this class doesn't do much as it doesn't parse the certificate. It only acts as container to the raw data - * and returns general info as data as raw, length, etc. In the future I may add full parsing of the certificate + * Represents a x509v3 certificate. the SSLCertificateMessage class returns an + * instance of this class as the certificate. Currently this class doesn't do + * much as it doesn't parse the certificate. It only acts as container to the + * raw data and returns general info as data as raw, length, etc. In the future + * I may add full parsing of the certificate */ -class SSLx509Certificate -{ -public: - - /** - * C'tor for this class - * @param[in] data The raw data of the certificate - * @param[in] dataLen The length in bytes of the raw data - * @param[in] allDataExists Certificate messages usually spread on more than 1 packet. So a certificate is likely - * to split between 2 packets or more. This field indicates whether the raw data contains all ceritificate data - * of just a part of it - */ - SSLx509Certificate(uint8_t* data, size_t dataLen, bool allDataExists) - : m_Data(data), m_DataLen(dataLen), m_AllDataExists(allDataExists) {} - - /** - * @return A pointer to the raw data - */ - uint8_t* getData() const { return m_Data; } - - /** - * @return Raw data length - */ - size_t getDataLength() const { return m_DataLen; } - - /** - * Certificate messages usually spread on more than 1 packet. So a certificate is likely to split between 2 packets - * or more. This method provides an indication whether all certificate data exists or only part of it - * @return True if this data contains all certificate data, false otherwise - */ - bool allDataExists() const { return m_AllDataExists; } - -private: - uint8_t* m_Data; - size_t m_DataLen; - bool m_AllDataExists; +class SSLx509Certificate { + public: + /** + * C'tor for this class + * @param[in] data The raw data of the certificate + * @param[in] dataLen The length in bytes of the raw data + * @param[in] allDataExists Certificate messages usually spread on more than 1 + * packet. So a certificate is likely to split between 2 packets or more. This + * field indicates whether the raw data contains all ceritificate data of just + * a part of it + */ + SSLx509Certificate(uint8_t* data, size_t dataLen, bool allDataExists) + : m_Data(data), m_DataLen(dataLen), m_AllDataExists(allDataExists) {} + + /** + * @return A pointer to the raw data + */ + uint8_t* getData() const { return m_Data; } + + /** + * @return Raw data length + */ + size_t getDataLength() const { return m_DataLen; } + + /** + * Certificate messages usually spread on more than 1 packet. So a certificate + * is likely to split between 2 packets or more. This method provides an + * indication whether all certificate data exists or only part of it + * @return True if this data contains all certificate data, false otherwise + */ + bool allDataExists() const { return m_AllDataExists; } + + private: + uint8_t* m_Data; + size_t m_DataLen; + bool m_AllDataExists; }; - class SSLHandshakeLayer; - /** * @class SSLHandshakeMessage - * A base class for SSL/TLS handshake messages. This is an abstract class and cannot be instantiated. SSL/TLS handshake - * messages are contained in SSLHandshakeLayer, meaning a SSLHandshakeLayer instance can contain one or more SSLHandshakeMessage - * instances. For example: one SSLHandshakeLayer may contain a server-hello, certificate, - * server-key-exchange, and server-hello-done messages (although it's not such a common case, most handshake layers - * contain 1 handshake message only) + * A base class for SSL/TLS handshake messages. This is an abstract class and + * cannot be instantiated. SSL/TLS handshake messages are contained in + * SSLHandshakeLayer, meaning a SSLHandshakeLayer instance can contain one or + * more SSLHandshakeMessage instances. For example: one SSLHandshakeLayer may + * contain a server-hello, certificate, server-key-exchange, and + * server-hello-done messages (although it's not such a common case, most + * handshake layers contain 1 handshake message only) */ -class SSLHandshakeMessage -{ -public: - - virtual ~SSLHandshakeMessage() {} - - /** - * A factory method for creating instances of handshake messages from raw data - * @param[in] data The raw data containing 1 handshake message - * @param[in] dataLen Raw data length in bytes - * @param[in] container A pointer to the SSLHandshakeLayer instance which will contain the created message. - * This parameter is required because the handshake message includes a pointer to its container - */ - static SSLHandshakeMessage* createHandshakeMessage(uint8_t* data, size_t dataLen, SSLHandshakeLayer* container); - - /** - * @return The handshake message type - */ - virtual SSLHandshakeType getHandshakeType() const; - - /** - * @return The handshake message length in bytes. Notice that sometimes the handshake message is divided between - * several packets, in this case this method will return the length of part of the message in the current packet - */ - virtual size_t getMessageLength() const; - - /** - * @return True if current packet contains the entire message or false otherwise. This method is important - * because sometimes handshake messages are divided in consequent packets (happens a lot in certificate messages - * which usually contain several KB of data which is larger than standard packet size, so the message is divided between - * several packets) - */ - virtual bool isMessageComplete() const; - - /** - * @return A pointer to the SSLHandshakeLayer instance containing this message - */ - SSLHandshakeLayer* getContainingLayer() const { return m_Container; } - - /** - * @return A string representation of the message type (e.g "Client Hello message") - */ - virtual std::string toString() const = 0; - -protected: - - SSLHandshakeMessage(uint8_t* data, size_t dataLen, SSLHandshakeLayer* container); - - uint8_t* m_Data; - size_t m_DataLen; - SSLHandshakeLayer* m_Container; - +class SSLHandshakeMessage { + public: + virtual ~SSLHandshakeMessage() {} + + /** + * A factory method for creating instances of handshake messages from raw data + * @param[in] data The raw data containing 1 handshake message + * @param[in] dataLen Raw data length in bytes + * @param[in] container A pointer to the SSLHandshakeLayer instance which will + * contain the created message. This parameter is required because the + * handshake message includes a pointer to its container + */ + static SSLHandshakeMessage* + createHandshakeMessage(uint8_t* data, size_t dataLen, + SSLHandshakeLayer* container); + + /** + * @return The handshake message type + */ + virtual SSLHandshakeType getHandshakeType() const; + + /** + * @return The handshake message length in bytes. Notice that sometimes the + * handshake message is divided between several packets, in this case this + * method will return the length of part of the message in the current packet + */ + virtual size_t getMessageLength() const; + + /** + * @return True if current packet contains the entire message or false + * otherwise. This method is important because sometimes handshake messages + * are divided in consequent packets (happens a lot in certificate messages + * which usually contain several KB of data which is larger than standard + * packet size, so the message is divided between several packets) + */ + virtual bool isMessageComplete() const; + + /** + * @return A pointer to the SSLHandshakeLayer instance containing this message + */ + SSLHandshakeLayer* getContainingLayer() const { return m_Container; } + + /** + * @return A string representation of the message type (e.g "Client Hello + * message") + */ + virtual std::string toString() const = 0; + + protected: + SSLHandshakeMessage(uint8_t* data, size_t dataLen, + SSLHandshakeLayer* container); + + uint8_t* m_Data; + size_t m_DataLen; + SSLHandshakeLayer* m_Container; }; - /** * @class SSLClientHelloMessage - * Represents a client-hello message (type 1). Inherits from SSLHandshakeMessage and adds parsing of all fields - * of this message including the message extensions, cipher-suite list, etc. + * Represents a client-hello message (type 1). Inherits from SSLHandshakeMessage + * and adds parsing of all fields of this message including the message + * extensions, cipher-suite list, etc. */ -class SSLClientHelloMessage : public SSLHandshakeMessage -{ -public: - - /** - * @struct ClientHelloTLSFingerprint - * A struct that contains all the elements needed for creating a Client Hello TLS fingerprinting: - * TLS version, a list of Cipher Suite IDs, a list of Extensions IDs, a list of support groups and a list of - * EC point formats. - * You can read more about this in SSLClientHelloMessage#generateTLSFingerprint(). - * This struct contains methods to extract the TLS fingerprint itself: toString() and toMD5() - */ - struct ClientHelloTLSFingerprint - { - /** TLS version */ - uint16_t tlsVersion; - /** A list of Cipher Suite IDs */ - std::vector cipherSuites; - /** A list of extension IDs */ - std::vector extensions; - /** A list of Suppotred Groups taken from the "supported groups" TLS extension (if exist in the message) */ - std::vector supportedGroups; - /** A list of EC point formats taken from the "EC point formats" TLS extension (if exist in the message) */ - std::vector ecPointFormats; - - /** - * @return A string representing the TLS fingerprint, for example: - * 771,4866-4867-4865-255,0-11-10-35-22-23-13-43-45-51,29-23-30-25-24,0-1-2 - * - * This string has the following format: TLSVersion,CipherSuiteIDs,ExtensionIDs,SupportedGroups,ECPointFormats - * - * The extension IDs, supported groups and EC point formats are each separated by a "-". - * If the message doesn't include the "supported groups" or "EC point formats" extensions, they will be replaced - * by an empty string for example: 771,4866-4867-4865-255,0-11-10-35-22-23-13-43-45-51,, - */ - std::string toString(); - - /** - * @return An MD5 hash of the string generated by toString() - */ - std::string toMD5(); - - /** - * @return A pair of the string and MD5 hash (string is first, MD5 is second). - * If you want both this method is more efficient than calling toString() and toMD5() separately - */ - std::pair toStringAndMD5(); - }; - - /** - * C'tor for this class. Currently only in use in SSLHandshakeMessage::createHandshakeMessage() and shouldn't be used - * by a user - * @param[in] data The message as raw data - * @param[in] dataLen Message raw data length in bytes - * @param[in] container The SSL handshake layer which shall contain this message - */ - SSLClientHelloMessage(uint8_t* data, size_t dataLen, SSLHandshakeLayer* container); - - virtual ~SSLClientHelloMessage() {} - - /** - * @return A struct containing common fields for client-hello and server-hello messages. Notice this points directly - * to the data, so every change will change the actual packet data - */ - ssl_tls_client_server_hello* getClientHelloHeader() const { return (ssl_tls_client_server_hello*)m_Data; } - - /** - * @return Handshake SSL/TLS version (notice it may be different than SSLLayer#getRecordVersion(). Each client-hello - * or server-hello message has both record version and handshake version and they may differ from one another) - */ - SSLVersion getHandshakeVersion() const; - - /** - * @return Session ID length in bytes. If server-hello message doesn't include session ID 0 will be returned - */ - uint8_t getSessionIDLength() const; - - /** - * @return Session ID as byte array. If server-hello message doesn't include session ID NULL will be returned - */ - uint8_t* getSessionID() const; - - /** - * @return The number of cipher-suites included in this message - */ - int getCipherSuiteCount() const; - - /** - * Get a pointer to a cipher-suite by index. The cipher-suites are numbered according to their order of appearance - * in the message. If index is out of bounds (less than 0 or larger than total amount of cipher suites) NULL will be - * returned. NULL will also be returned if the cipher-suite ID is unknown. If you still want to get the cipher-suite - * ID you can use getCipherSuiteID() - * @param[in] index The index of the cipher-suite to return - * @return The pointer to the cipher-suite object or NULL if index is out of bounds - */ - SSLCipherSuite* getCipherSuite(int index) const; - - /** - * Get the cipher-suite ID by index. This method just parses the ID from the client-hello message and returns it. - * To get more information on the cipher-suite you can use the getCipherSuite() method - * @param[in] index The index of the cipher-suite to return - * @param[out] isValid Set to "true" if parsing succeeded and the return value is valid or "false" if: - * (1) the index is out-of-bounds (less than 0 or larger than total amount of cipher suites) or (2) the parsing failed. - * If the value is "false" the return value can be ignored - * @return The cipher-suite ID if "isValid" is set to "true". If "isValid" is set to "false" the return value can be ignored - */ - uint16_t getCipherSuiteID(int index, bool& isValid) const; - - /** - * @return The value of the compression method byte - */ - uint8_t getCompressionMethodsValue() const; - - /** - * @return The number of extensions in this message - */ - int getExtensionCount() const; - - /** - * @return The size (in bytes) of all extensions data in this message. Extracted from the "extensions length" field - */ - uint16_t getExtensionsLength() const; - - /** - * Get a pointer to an extension by index. The extensions are numbered according to their order of appearance - * in the message. If index is out of bounds (less than 0 or larger than total amount of extensions) NULL will be - * returned - * @param[in] index The index of the extension to return - * @return The pointer to the extension or NULL if index is out of bounds - */ - SSLExtension* getExtension(int index) const; - - /** - * Get a pointer to an extension by numeric type field. Every extension has a 2-byte numeric value representing - * its type (for example: renegotiation info extension type is 0x1ff). This method gets the type and returns a - * pointer to the extension object - * @param[in] type The 2-byte numeric type of the extension - * @return A pointer to the extension object of NULL if this type doesn't exist in this message - */ - SSLExtension* getExtensionOfType(uint16_t type) const; - - /** - * Get a pointer to an extension by its enum type - * @param[in] type The type of extension to return - * @return A pointer to the extension object or NULL if this type doesn't exist in this message - */ - SSLExtension* getExtensionOfType(SSLExtensionType type) const; - - /** - * Get a pointer to an extension by its class type. This is a templated method that is used with the type of the - * requested extension and returns the first extension instance of this type - * @return A pointer to the extension object or NULL if this extension type doesn't exist in this message - * - */ - template - TExtension* getExtensionOfType() const; - - /** - * TLS fingerprinting is a way to identify client applications using the details in the TLS Client Hello packet. - * It was initially introduced by Lee Brotherston in his 2015 research: - * This implementation of TLS fingerprint is a C++ version of Salesforce's JA3 open source project (originally written in - * Python and Zeek): - * - * @return A SSLClientHelloMessage#ClientHelloTLSFingerprint struct that contains all the elements needed for - * creating a TLS fingerprint out of this Client Hello message. This struct has also methods to extract the TLS fingerprint - * itself in a string or MD5 formats - */ - ClientHelloTLSFingerprint generateTLSFingerprint() const; - - // implement abstract methods - - std::string toString() const; - -private: - PointerVector m_ExtensionList; - +class SSLClientHelloMessage : public SSLHandshakeMessage { + public: + /** + * @struct ClientHelloTLSFingerprint + * A struct that contains all the elements needed for creating a Client Hello + * TLS fingerprinting: TLS version, a list of Cipher Suite IDs, a list of + * Extensions IDs, a list of support groups and a list of EC point formats. + * You can read more about this in + * SSLClientHelloMessage#generateTLSFingerprint(). This struct contains + * methods to extract the TLS fingerprint itself: toString() and toMD5() + */ + struct ClientHelloTLSFingerprint { + /** TLS version */ + uint16_t tlsVersion; + /** A list of Cipher Suite IDs */ + std::vector cipherSuites; + /** A list of extension IDs */ + std::vector extensions; + /** A list of Suppotred Groups taken from the "supported groups" TLS + * extension (if exist in the message) */ + std::vector supportedGroups; + /** A list of EC point formats taken from the "EC point formats" TLS + * extension (if exist in the message) */ + std::vector ecPointFormats; + + /** + * @return A string representing the TLS fingerprint, for example: + * 771,4866-4867-4865-255,0-11-10-35-22-23-13-43-45-51,29-23-30-25-24,0-1-2 + * + * This string has the following format: + * TLSVersion,CipherSuiteIDs,ExtensionIDs,SupportedGroups,ECPointFormats + * + * The extension IDs, supported groups and EC point formats are each + * separated by a "-". If the message doesn't include the "supported groups" + * or "EC point formats" extensions, they will be replaced by an empty + * string for example: + * 771,4866-4867-4865-255,0-11-10-35-22-23-13-43-45-51,, + */ + std::string toString(); + + /** + * @return An MD5 hash of the string generated by toString() + */ + std::string toMD5(); + + /** + * @return A pair of the string and MD5 hash (string is first, MD5 is + * second). If you want both this method is more efficient than calling + * toString() and toMD5() separately + */ + std::pair toStringAndMD5(); + }; + + /** + * C'tor for this class. Currently only in use in + * SSLHandshakeMessage::createHandshakeMessage() and shouldn't be used by a + * user + * @param[in] data The message as raw data + * @param[in] dataLen Message raw data length in bytes + * @param[in] container The SSL handshake layer which shall contain this + * message + */ + SSLClientHelloMessage(uint8_t* data, size_t dataLen, + SSLHandshakeLayer* container); + + virtual ~SSLClientHelloMessage() {} + + /** + * @return A struct containing common fields for client-hello and server-hello + * messages. Notice this points directly to the data, so every change will + * change the actual packet data + */ + ssl_tls_client_server_hello* getClientHelloHeader() const { + return (ssl_tls_client_server_hello*)m_Data; + } + + /** + * @return Handshake SSL/TLS version (notice it may be different than + * SSLLayer#getRecordVersion(). Each client-hello or server-hello message has + * both record version and handshake version and they may differ from one + * another) + */ + SSLVersion getHandshakeVersion() const; + + /** + * @return Session ID length in bytes. If server-hello message doesn't include + * session ID 0 will be returned + */ + uint8_t getSessionIDLength() const; + + /** + * @return Session ID as byte array. If server-hello message doesn't include + * session ID NULL will be returned + */ + uint8_t* getSessionID() const; + + /** + * @return The number of cipher-suites included in this message + */ + int getCipherSuiteCount() const; + + /** + * Get a pointer to a cipher-suite by index. The cipher-suites are numbered + * according to their order of appearance in the message. If index is out of + * bounds (less than 0 or larger than total amount of cipher suites) NULL will + * be returned. NULL will also be returned if the cipher-suite ID is unknown. + * If you still want to get the cipher-suite ID you can use getCipherSuiteID() + * @param[in] index The index of the cipher-suite to return + * @return The pointer to the cipher-suite object or NULL if index is out of + * bounds + */ + SSLCipherSuite* getCipherSuite(int index) const; + + /** + * Get the cipher-suite ID by index. This method just parses the ID from the + * client-hello message and returns it. To get more information on the + * cipher-suite you can use the getCipherSuite() method + * @param[in] index The index of the cipher-suite to return + * @param[out] isValid Set to "true" if parsing succeeded and the return value + * is valid or "false" if: (1) the index is out-of-bounds (less than 0 or + * larger than total amount of cipher suites) or (2) the parsing failed. If + * the value is "false" the return value can be ignored + * @return The cipher-suite ID if "isValid" is set to "true". If "isValid" is + * set to "false" the return value can be ignored + */ + uint16_t getCipherSuiteID(int index, bool& isValid) const; + + /** + * @return The value of the compression method byte + */ + uint8_t getCompressionMethodsValue() const; + + /** + * @return The number of extensions in this message + */ + int getExtensionCount() const; + + /** + * @return The size (in bytes) of all extensions data in this message. + * Extracted from the "extensions length" field + */ + uint16_t getExtensionsLength() const; + + /** + * Get a pointer to an extension by index. The extensions are numbered + * according to their order of appearance in the message. If index is out of + * bounds (less than 0 or larger than total amount of extensions) NULL will be + * returned + * @param[in] index The index of the extension to return + * @return The pointer to the extension or NULL if index is out of bounds + */ + SSLExtension* getExtension(int index) const; + + /** + * Get a pointer to an extension by numeric type field. Every extension has a + * 2-byte numeric value representing its type (for example: renegotiation info + * extension type is 0x1ff). This method gets the type and returns a pointer + * to the extension object + * @param[in] type The 2-byte numeric type of the extension + * @return A pointer to the extension object of NULL if this type doesn't + * exist in this message + */ + SSLExtension* getExtensionOfType(uint16_t type) const; + + /** + * Get a pointer to an extension by its enum type + * @param[in] type The type of extension to return + * @return A pointer to the extension object or NULL if this type doesn't + * exist in this message + */ + SSLExtension* getExtensionOfType(SSLExtensionType type) const; + + /** + * Get a pointer to an extension by its class type. This is a templated method + * that is used with the type of the requested extension and returns the first + * extension instance of this type + * @return A pointer to the extension object or NULL if this extension type + * doesn't exist in this message + * + */ + template + TExtension* getExtensionOfType() const; + + /** + * TLS fingerprinting is a way to identify client applications using the + * details in the TLS Client Hello packet. It was initially introduced by Lee + * Brotherston in his 2015 research: + * This implementation of + * TLS fingerprint is a C++ version of Salesforce's JA3 open source project + * (originally written in Python and Zeek): + * + * @return A SSLClientHelloMessage#ClientHelloTLSFingerprint struct that + * contains all the elements needed for creating a TLS fingerprint out of this + * Client Hello message. This struct has also methods to extract the TLS + * fingerprint itself in a string or MD5 formats + */ + ClientHelloTLSFingerprint generateTLSFingerprint() const; + + // implement abstract methods + + std::string toString() const; + + private: + PointerVector m_ExtensionList; }; - /** * @class SSLServerHelloMessage - * Represents SSL/TLS server-hello message (type 2). Inherits from SSLHandshakeMessage and adds parsing of all fields - * of this message including the message extensions, cipher-suite, etc. + * Represents SSL/TLS server-hello message (type 2). Inherits from + * SSLHandshakeMessage and adds parsing of all fields of this message including + * the message extensions, cipher-suite, etc. */ -class SSLServerHelloMessage : public SSLHandshakeMessage -{ -public: - - /** - * @struct ServerHelloTLSFingerprint - * A struct that contains all the elements needed for creating a Server Hello TLS fingerprinting: - * TLS version, Cipher Suite ID, and a list of Extensions IDs. - * You can read more about this in SSLServerHelloMessage#generateTLSFingerprint(). - * This struct contains methods to extract the TLS fingerprint itself: toString() and toMD5() - */ - struct ServerHelloTLSFingerprint - { - /** TLS version */ - uint16_t tlsVersion; - /** Cipher Suite ID */ - uint16_t cipherSuite; - /** A list of extension IDs */ - std::vector extensions; - - /** - * @return A string representing the TLS fingerprint, for example: 771,49195,65281-16-11 - * - * This string has the following format: TLSVersion,Cipher,Extensions - * - * The extension ID are separated with a "-" - */ - std::string toString(); - - /** - * @return An MD5 hash of the string generated by toString() - */ - std::string toMD5(); - - /** - * @return A pair of the string and MD5 hash (string is first, MD5 is second). - * If you want both this method is more efficient than calling toString() and toMD5() separately - */ - std::pair toStringAndMD5(); - }; - - /** - * C'tor for this class. Currently only in use in SSLHandshakeMessage::createHandshakeMessage() and shouldn't be used - * by a user - * @param[in] data The message as raw data - * @param[in] dataLen Message raw data length in bytes - * @param[in] container The SSL handshake layer which shall contain this message - */ - SSLServerHelloMessage(uint8_t* data, size_t dataLen, SSLHandshakeLayer* container); - - virtual ~SSLServerHelloMessage() {} - - /** - * @return A struct containing common fields for client-hello and server-hello messages. Notice this points directly - * to the data, so every change will change the actual packet data - */ - ssl_tls_client_server_hello* getServerHelloHeader() const { return (ssl_tls_client_server_hello*)m_Data; } - - /** - * @return Handshake SSL/TLS version (notice it may be different than SSLLayer#getRecordVersion(). Each client-hello - * or server-hello message has both record version and handshake version and they may differ from one another). - * - * NOTE: for TLS 1.3 the handshake version written in ssl_tls_client_server_hello::handshakeVersion is still TLS 1.2, - * so a special check is made here see if a SupportedVersions extension exists and if so extract the version from it. - * This is the most straight-forward way to detect TLS 1.3. - */ - SSLVersion getHandshakeVersion() const; - - /** - * @return Session ID length in bytes. If server-hello message doesn't include session ID 0 will be returned - */ - uint8_t getSessionIDLength() const; - - /** - * @return Session ID as byte array. If server-hello message doesn't include session ID NULL will be returned - */ - uint8_t* getSessionID() const; - - /** - * @return A pointer to the cipher suite encapsulated in this message (server-hello message contains one - * cipher-suite, the one that will be used to for encryption between client and server). May return NULL - * if the parsing of the message failed or the cipher-suite ID is unknown. If you still want to get the - * cipher-suite ID you can use the getCipherSuiteID() method - */ - SSLCipherSuite* getCipherSuite() const; - - /** - * Get the cipher-suite ID. This method just parses the ID from the server-hello message and returns it. - * To get more information on the cipher-suite you can use the getCipherSuite() method - * @param[out] isValid Set to "true" if parsing succeeded and the return value is valid or "false" otherwise. - * If the value is "false" the return value can be ignored - * @return The cipher-suite ID if "isValid" is set to "true". If "isValid" is set to "false" the return value can be ignored - */ - uint16_t getCipherSuiteID(bool& isValid) const; - - /** - * @return The value of the compression method byte - */ - uint8_t getCompressionMethodsValue() const; - - /** - * @return The number of extensions in this message - */ - int getExtensionCount() const; - - /** - * @return The size (in bytes) of all extensions data in this message. Extracted from the "extensions length" field - */ - uint16_t getExtensionsLength() const; - - /** - * Get a pointer to an extension by index. The extensions are numbered according to their order of appearance - * in the message. If index is out of bounds (less than 0 or larger than total amount of extensions) NULL will be - * returned - * @param[in] index The index of the extension to return - * @return The pointer to the extension or NULL if index is out of bounds - */ - SSLExtension* getExtension(int index) const; - - /** - * Get a pointer to an extension by numeric type field. Every extension has a 2-byte numeric value representing - * its type (for example: renegotiation info extension type is 0x1ff). This method gets the type and returns a - * pointer to the extension object - * @param[in] type The 2-byte numeric type of the extension - * @return A pointer to the extension object of NULL if this type doesn't exist in this message - */ - SSLExtension* getExtensionOfType(uint16_t type) const; - - /** - * Get a pointer to an extension by its enum type - * @param[in] type The type of extension to return - * @return A pointer to the extension object or NULL if this type doesn't exist in this message - */ - SSLExtension* getExtensionOfType(SSLExtensionType type) const; - - /** - * Get a pointer to an extension by its class type. This is a templated method that is used with the type of the - * requested extension and returns the first extension instance of this type - * @return A pointer to the extension object or NULL if this extension type doesn't exist in this message - * - */ - template - TExtension* getExtensionOfType() const; - - /** - * ServerHello TLS fingerprinting is a way to fingerprint TLS Server Hello messages. In conjunction with - * ClientHello TLS fingerprinting it can assist in identifying specific client-server communication (for - * example: a malware connecting to its backend server). - * ServerHello TLS fingerprinting was introduced in Salesforce's JA3S open source project: - * - * This implementation is a C++ version of Salesforce's JAS3 (originally written in Python and Zeek) - * @return A SSLServerHelloMessage#ServerHelloTLSFingerprint struct that contains all the elements needed for - * creating a TLS fingerprint out of this Server Hello message. This struct has also methods to extract the TLS fingerprint - * itself in a string or MD5 formats - */ - ServerHelloTLSFingerprint generateTLSFingerprint() const; - - // implement abstract methods - - std::string toString() const; - -private: - PointerVector m_ExtensionList; +class SSLServerHelloMessage : public SSLHandshakeMessage { + public: + /** + * @struct ServerHelloTLSFingerprint + * A struct that contains all the elements needed for creating a Server Hello + * TLS fingerprinting: TLS version, Cipher Suite ID, and a list of Extensions + * IDs. You can read more about this in + * SSLServerHelloMessage#generateTLSFingerprint(). This struct contains + * methods to extract the TLS fingerprint itself: toString() and toMD5() + */ + struct ServerHelloTLSFingerprint { + /** TLS version */ + uint16_t tlsVersion; + /** Cipher Suite ID */ + uint16_t cipherSuite; + /** A list of extension IDs */ + std::vector extensions; + + /** + * @return A string representing the TLS fingerprint, for example: + * 771,49195,65281-16-11 + * + * This string has the following format: TLSVersion,Cipher,Extensions + * + * The extension ID are separated with a "-" + */ + std::string toString(); + + /** + * @return An MD5 hash of the string generated by toString() + */ + std::string toMD5(); + + /** + * @return A pair of the string and MD5 hash (string is first, MD5 is + * second). If you want both this method is more efficient than calling + * toString() and toMD5() separately + */ + std::pair toStringAndMD5(); + }; + + /** + * C'tor for this class. Currently only in use in + * SSLHandshakeMessage::createHandshakeMessage() and shouldn't be used by a + * user + * @param[in] data The message as raw data + * @param[in] dataLen Message raw data length in bytes + * @param[in] container The SSL handshake layer which shall contain this + * message + */ + SSLServerHelloMessage(uint8_t* data, size_t dataLen, + SSLHandshakeLayer* container); + + virtual ~SSLServerHelloMessage() {} + + /** + * @return A struct containing common fields for client-hello and server-hello + * messages. Notice this points directly to the data, so every change will + * change the actual packet data + */ + ssl_tls_client_server_hello* getServerHelloHeader() const { + return (ssl_tls_client_server_hello*)m_Data; + } + + /** + * @return Handshake SSL/TLS version (notice it may be different than + * SSLLayer#getRecordVersion(). Each client-hello or server-hello message has + * both record version and handshake version and they may differ from one + * another). + * + * NOTE: for TLS 1.3 the handshake version written in + * ssl_tls_client_server_hello::handshakeVersion is still TLS 1.2, so a + * special check is made here see if a SupportedVersions extension exists and + * if so extract the version from it. This is the most straight-forward way to + * detect TLS 1.3. + */ + SSLVersion getHandshakeVersion() const; + + /** + * @return Session ID length in bytes. If server-hello message doesn't include + * session ID 0 will be returned + */ + uint8_t getSessionIDLength() const; + + /** + * @return Session ID as byte array. If server-hello message doesn't include + * session ID NULL will be returned + */ + uint8_t* getSessionID() const; + + /** + * @return A pointer to the cipher suite encapsulated in this message + * (server-hello message contains one cipher-suite, the one that will be used + * to for encryption between client and server). May return NULL if the + * parsing of the message failed or the cipher-suite ID is unknown. If you + * still want to get the cipher-suite ID you can use the getCipherSuiteID() + * method + */ + SSLCipherSuite* getCipherSuite() const; + + /** + * Get the cipher-suite ID. This method just parses the ID from the + * server-hello message and returns it. To get more information on the + * cipher-suite you can use the getCipherSuite() method + * @param[out] isValid Set to "true" if parsing succeeded and the return value + * is valid or "false" otherwise. If the value is "false" the return value can + * be ignored + * @return The cipher-suite ID if "isValid" is set to "true". If "isValid" is + * set to "false" the return value can be ignored + */ + uint16_t getCipherSuiteID(bool& isValid) const; + + /** + * @return The value of the compression method byte + */ + uint8_t getCompressionMethodsValue() const; + + /** + * @return The number of extensions in this message + */ + int getExtensionCount() const; + + /** + * @return The size (in bytes) of all extensions data in this message. + * Extracted from the "extensions length" field + */ + uint16_t getExtensionsLength() const; + + /** + * Get a pointer to an extension by index. The extensions are numbered + * according to their order of appearance in the message. If index is out of + * bounds (less than 0 or larger than total amount of extensions) NULL will be + * returned + * @param[in] index The index of the extension to return + * @return The pointer to the extension or NULL if index is out of bounds + */ + SSLExtension* getExtension(int index) const; + + /** + * Get a pointer to an extension by numeric type field. Every extension has a + * 2-byte numeric value representing its type (for example: renegotiation info + * extension type is 0x1ff). This method gets the type and returns a pointer + * to the extension object + * @param[in] type The 2-byte numeric type of the extension + * @return A pointer to the extension object of NULL if this type doesn't + * exist in this message + */ + SSLExtension* getExtensionOfType(uint16_t type) const; + + /** + * Get a pointer to an extension by its enum type + * @param[in] type The type of extension to return + * @return A pointer to the extension object or NULL if this type doesn't + * exist in this message + */ + SSLExtension* getExtensionOfType(SSLExtensionType type) const; + + /** + * Get a pointer to an extension by its class type. This is a templated method + * that is used with the type of the requested extension and returns the first + * extension instance of this type + * @return A pointer to the extension object or NULL if this extension type + * doesn't exist in this message + * + */ + template + TExtension* getExtensionOfType() const; + + /** + * ServerHello TLS fingerprinting is a way to fingerprint TLS Server Hello + * messages. In conjunction with ClientHello TLS fingerprinting it can assist + * in identifying specific client-server communication (for example: a malware + * connecting to its backend server). ServerHello TLS fingerprinting was + * introduced in Salesforce's JA3S open source project: + * + * This implementation is a C++ version of Salesforce's JAS3 (originally + * written in Python and Zeek) + * @return A SSLServerHelloMessage#ServerHelloTLSFingerprint struct that + * contains all the elements needed for creating a TLS fingerprint out of this + * Server Hello message. This struct has also methods to extract the TLS + * fingerprint itself in a string or MD5 formats + */ + ServerHelloTLSFingerprint generateTLSFingerprint() const; + + // implement abstract methods + + std::string toString() const; + + private: + PointerVector m_ExtensionList; }; - /** * @class SSLCertificateMessage - * Represents SSL/TLS certificate message (type 11). Inherits from SSLHandshakeMessage and adds parsing functionality - * such as extracting the certificates data. Notice that in most cases this message is spread over more than 1 packet - * as its size is too big for a single packet. So SSLCertificateMessage instance will be created just for the first - * part of the message - the one encapsulated in the first packet. Other parts (encapsulated in the following packets) - * won't be recognized as SSLCertificateMessage messages + * Represents SSL/TLS certificate message (type 11). Inherits from + * SSLHandshakeMessage and adds parsing functionality such as extracting the + * certificates data. Notice that in most cases this message is spread over more + * than 1 packet as its size is too big for a single packet. So + * SSLCertificateMessage instance will be created just for the first part of the + * message - the one encapsulated in the first packet. Other parts (encapsulated + * in the following packets) won't be recognized as SSLCertificateMessage + * messages */ -class SSLCertificateMessage : public SSLHandshakeMessage -{ -public: - - /** - * C'tor for this class. Currently only in use in SSLHandshakeMessage::createHandshakeMessage() and should be used - * by a user - * @param[in] data The message as raw data - * @param[in] dataLen Message raw data length in bytes - * @param[in] container The SSL handshake layer which shall contain this message - */ - SSLCertificateMessage(uint8_t* data, size_t dataLen, SSLHandshakeLayer* container); - - virtual ~SSLCertificateMessage() {} - - /** - * @return The number of certificates encapsulated in this message (as written in the 'length' field of the - * message). Notice that because the message may spread over several packets, not all certificates will necessarily - * be in this packet. So, for example, there may be a case where this method return 3 (message contains 3 - * certificates) but this message actually contains only 1 certificate as the other 2 are spread over the other - * packets - */ - int getNumOfCertificates() const; - - /** - * Get a certificate by index - * @param[in] index The index of the certificate to retrieve - * @return A pointer to the certificate object. Notice that if index < 0 or index > num of certificates encapsulated - * in current packet a NULL value will be returned - */ - SSLx509Certificate* getCertificate(int index) const; - - // implement abstract methods - - std::string toString() const; - -private: - PointerVector m_CertificateList; +class SSLCertificateMessage : public SSLHandshakeMessage { + public: + /** + * C'tor for this class. Currently only in use in + * SSLHandshakeMessage::createHandshakeMessage() and should be used by a user + * @param[in] data The message as raw data + * @param[in] dataLen Message raw data length in bytes + * @param[in] container The SSL handshake layer which shall contain this + * message + */ + SSLCertificateMessage(uint8_t* data, size_t dataLen, + SSLHandshakeLayer* container); + + virtual ~SSLCertificateMessage() {} + + /** + * @return The number of certificates encapsulated in this message (as written + * in the 'length' field of the message). Notice that because the message may + * spread over several packets, not all certificates will necessarily be in + * this packet. So, for example, there may be a case where this method return + * 3 (message contains 3 certificates) but this message actually contains only + * 1 certificate as the other 2 are spread over the other packets + */ + int getNumOfCertificates() const; + + /** + * Get a certificate by index + * @param[in] index The index of the certificate to retrieve + * @return A pointer to the certificate object. Notice that if index < 0 or + * index > num of certificates encapsulated in current packet a NULL value + * will be returned + */ + SSLx509Certificate* getCertificate(int index) const; + + // implement abstract methods + + std::string toString() const; + + private: + PointerVector m_CertificateList; }; - /** * @class SSLHelloRequestMessage - * Represents SSL/TLS hello-request message (type 0). This message has no additional payload except for the common payload - * described in SSLHandshakeMessage + * Represents SSL/TLS hello-request message (type 0). This message has no + * additional payload except for the common payload described in + * SSLHandshakeMessage */ -class SSLHelloRequestMessage : public SSLHandshakeMessage -{ -public: - - /** - * C'tor for this class. Currently only in use in SSLHandshakeMessage::createHandshakeMessage() and should be used - * by a user - * @param[in] data The message as raw data - * @param[in] dataLen Message raw data length in bytes - * @param[in] container The SSL handshake layer which shall contain this message - */ - SSLHelloRequestMessage(uint8_t* data, size_t dataLen, SSLHandshakeLayer* container) : SSLHandshakeMessage(data, dataLen, container) {} - - virtual ~SSLHelloRequestMessage() {} - - // implement abstract methods - - std::string toString() const; +class SSLHelloRequestMessage : public SSLHandshakeMessage { + public: + /** + * C'tor for this class. Currently only in use in + * SSLHandshakeMessage::createHandshakeMessage() and should be used by a user + * @param[in] data The message as raw data + * @param[in] dataLen Message raw data length in bytes + * @param[in] container The SSL handshake layer which shall contain this + * message + */ + SSLHelloRequestMessage(uint8_t* data, size_t dataLen, + SSLHandshakeLayer* container) + : SSLHandshakeMessage(data, dataLen, container) {} + + virtual ~SSLHelloRequestMessage() {} + + // implement abstract methods + + std::string toString() const; }; - /** * @class SSLServerKeyExchangeMessage - * Represents SSL/TLS server-key-exchange message (type 12). Inherits from SSLHandshakeMessage and adds parsing - * functionality such as getting the server key exchange params as raw data (parsing of this may be added in the - * future) + * Represents SSL/TLS server-key-exchange message (type 12). Inherits from + * SSLHandshakeMessage and adds parsing functionality such as getting the server + * key exchange params as raw data (parsing of this may be added in the future) */ -class SSLServerKeyExchangeMessage : public SSLHandshakeMessage -{ -public: - - /** - * C'tor for this class. Currently only in use in SSLHandshakeMessage::createHandshakeMessage() and should be used - * by a user - * @param[in] data The message as raw data - * @param[in] dataLen Message raw data length in bytes - * @param[in] container The SSL handshake layer which shall contain this message - */ - SSLServerKeyExchangeMessage(uint8_t* data, size_t dataLen, SSLHandshakeLayer* container) : SSLHandshakeMessage(data, dataLen, container) {} - - ~SSLServerKeyExchangeMessage() {} - - /** - * @return A pointer to the raw data of the server key exchange params. Currently this data can only returned as - * raw, parsing may be added in the future. Notice that if the message is spread over more than 1 packet in a way - * params doesn't exist in the first packet, NULL will be returned - */ - uint8_t* getServerKeyExchangeParams() const; - - /** - * @return The size of the params field. Notice that if the message is spread over more than 1 packet in a way the - * ssl_tls_handshake_layer cannot be parsed from the packet, 0 will be returned. Also, if only part of the params - * exist in current packet (and the rest are on consequent packets), the size that will be returned is the size - * of the part that exists in the current packet (and not total size of params) - */ - size_t getServerKeyExchangeParamsLength() const; - - // implement abstract methods - - std::string toString() const; +class SSLServerKeyExchangeMessage : public SSLHandshakeMessage { + public: + /** + * C'tor for this class. Currently only in use in + * SSLHandshakeMessage::createHandshakeMessage() and should be used by a user + * @param[in] data The message as raw data + * @param[in] dataLen Message raw data length in bytes + * @param[in] container The SSL handshake layer which shall contain this + * message + */ + SSLServerKeyExchangeMessage(uint8_t* data, size_t dataLen, + SSLHandshakeLayer* container) + : SSLHandshakeMessage(data, dataLen, container) {} + + ~SSLServerKeyExchangeMessage() {} + + /** + * @return A pointer to the raw data of the server key exchange params. + * Currently this data can only returned as raw, parsing may be added in the + * future. Notice that if the message is spread over more than 1 packet in a + * way params doesn't exist in the first packet, NULL will be returned + */ + uint8_t* getServerKeyExchangeParams() const; + + /** + * @return The size of the params field. Notice that if the message is spread + * over more than 1 packet in a way the ssl_tls_handshake_layer cannot be + * parsed from the packet, 0 will be returned. Also, if only part of the + * params exist in current packet (and the rest are on consequent packets), + * the size that will be returned is the size of the part that exists in the + * current packet (and not total size of params) + */ + size_t getServerKeyExchangeParamsLength() const; + + // implement abstract methods + + std::string toString() const; }; - /** * @class SSLClientKeyExchangeMessage - * Represents SSL/TLS client-key-exchange message (type 16). Inherits from SSLHandshakeMessage and adds parsing - * functionality such as getting the server key exchange params as raw data (parsing of this may be added in the - * future) + * Represents SSL/TLS client-key-exchange message (type 16). Inherits from + * SSLHandshakeMessage and adds parsing functionality such as getting the server + * key exchange params as raw data (parsing of this may be added in the future) */ -class SSLClientKeyExchangeMessage : public SSLHandshakeMessage -{ -public: - - /** - * C'tor for this class. Currently only in use in SSLHandshakeMessage::createHandshakeMessage() and should be used - * by a user - * @param[in] data The message as raw data - * @param[in] dataLen Message raw data length in bytes - * @param[in] container The SSL handshake layer which shall contain this message - */ - SSLClientKeyExchangeMessage(uint8_t* data, size_t dataLen, SSLHandshakeLayer* container) : SSLHandshakeMessage(data, dataLen, container) {} - - ~SSLClientKeyExchangeMessage() {} - - /** - * @return A pointer to the raw data of the server key exchange params. Currently this data can only be returned - * as raw, parsing may be added in the future. Notice that if the message is spread over more than 1 packet in - * a way params doesn't exist in the first packet, NULL will be returned - */ - uint8_t* getClientKeyExchangeParams() const; - - /** - * @return The size of the params field. Notice that if the message is spread over more than 1 packet in a way the - * ssl_tls_handshake_layer cannot be parsed from the packet, 0 will be returned. Also, if only part of the params - * exist in current packet (and the rest are on consequent packets), the size that will be returned is the size - * of the part that exists in the current packet (and not the total size of params) - */ - size_t getClientKeyExchangeParamsLength() const; - - // implement abstract methods - - std::string toString() const; +class SSLClientKeyExchangeMessage : public SSLHandshakeMessage { + public: + /** + * C'tor for this class. Currently only in use in + * SSLHandshakeMessage::createHandshakeMessage() and should be used by a user + * @param[in] data The message as raw data + * @param[in] dataLen Message raw data length in bytes + * @param[in] container The SSL handshake layer which shall contain this + * message + */ + SSLClientKeyExchangeMessage(uint8_t* data, size_t dataLen, + SSLHandshakeLayer* container) + : SSLHandshakeMessage(data, dataLen, container) {} + + ~SSLClientKeyExchangeMessage() {} + + /** + * @return A pointer to the raw data of the server key exchange params. + * Currently this data can only be returned as raw, parsing may be added in + * the future. Notice that if the message is spread over more than 1 packet in + * a way params doesn't exist in the first packet, NULL will be returned + */ + uint8_t* getClientKeyExchangeParams() const; + + /** + * @return The size of the params field. Notice that if the message is spread + * over more than 1 packet in a way the ssl_tls_handshake_layer cannot be + * parsed from the packet, 0 will be returned. Also, if only part of the + * params exist in current packet (and the rest are on consequent packets), + * the size that will be returned is the size of the part that exists in the + * current packet (and not the total size of params) + */ + size_t getClientKeyExchangeParamsLength() const; + + // implement abstract methods + + std::string toString() const; }; - /** * @class SSLCertificateRequestMessage - * Represents SSL/TLS certificate-request message (type 13). Inherits from SSLHandshakeMessage and adds parsing - * functionality such as retrieving client certificate types and authority data + * Represents SSL/TLS certificate-request message (type 13). Inherits from + * SSLHandshakeMessage and adds parsing functionality such as retrieving client + * certificate types and authority data */ -class SSLCertificateRequestMessage : public SSLHandshakeMessage -{ -public: - - /** - * C'tor for this class. Currently only in use in SSLHandshakeMessage::createHandshakeMessage() and should be used - * by a user - * @param[in] data The message as raw data - * @param[in] dataLen Message raw data length in bytes - * @param[in] container The SSL handshake layer which shall contain this message - */ - SSLCertificateRequestMessage(uint8_t* data, size_t dataLen, SSLHandshakeLayer* container); - - ~SSLCertificateRequestMessage() {} - - /** - * @return A reference to a vector containing all client certificate types exist in this message - */ - std::vector& getCertificateTypes(); - - /** - * @return A pointer to the certificate authority data as raw data (byte array). Parsing of this data may be added - * in the future. Notice that if this message is spread over several packets in a way none of the certificate - * authority data exists in this packet, NULL will be returned - */ - uint8_t* getCertificateAuthorityData() const; - - /** - * @return The length of certificate authority data returned by getCertificateAuthorityData(). Notice that if - * this message is spread over several packets in a way none of certificate authority data exists in the current - * packet, 0 will be returned. Also, if some of the data exists in the consequent packets, the length that will be - * returned is the length of data exists in the current packet only (and not the total length) - */ - size_t getCertificateAuthorityLength() const; - - // implement abstract methods - - std::string toString() const; - -private: - std::vector m_ClientCertificateTypes; +class SSLCertificateRequestMessage : public SSLHandshakeMessage { + public: + /** + * C'tor for this class. Currently only in use in + * SSLHandshakeMessage::createHandshakeMessage() and should be used by a user + * @param[in] data The message as raw data + * @param[in] dataLen Message raw data length in bytes + * @param[in] container The SSL handshake layer which shall contain this + * message + */ + SSLCertificateRequestMessage(uint8_t* data, size_t dataLen, + SSLHandshakeLayer* container); + + ~SSLCertificateRequestMessage() {} + + /** + * @return A reference to a vector containing all client certificate types + * exist in this message + */ + std::vector& getCertificateTypes(); + + /** + * @return A pointer to the certificate authority data as raw data (byte + * array). Parsing of this data may be added in the future. Notice that if + * this message is spread over several packets in a way none of the + * certificate authority data exists in this packet, NULL will be returned + */ + uint8_t* getCertificateAuthorityData() const; + + /** + * @return The length of certificate authority data returned by + * getCertificateAuthorityData(). Notice that if this message is spread over + * several packets in a way none of certificate authority data exists in the + * current packet, 0 will be returned. Also, if some of the data exists in the + * consequent packets, the length that will be returned is the length of data + * exists in the current packet only (and not the total length) + */ + size_t getCertificateAuthorityLength() const; + + // implement abstract methods + + std::string toString() const; + + private: + std::vector m_ClientCertificateTypes; }; - /** * @class SSLServerHelloDoneMessage - * Represents SSL/TLS server-hello-done message (type 14). This message has no additional payload except for the common - * payload described in SSLHandshakeMessage + * Represents SSL/TLS server-hello-done message (type 14). This message has no + * additional payload except for the common payload described in + * SSLHandshakeMessage */ -class SSLServerHelloDoneMessage : public SSLHandshakeMessage -{ -public: - - /** - * C'tor for this class. Currently only in use in SSLHandshakeMessage::createHandshakeMessage() and should be used - * by a user - * @param[in] data The message as raw data - * @param[in] dataLen Message raw data length in bytes - * @param[in] container The SSL handshake layer which shall contain this message - */ - SSLServerHelloDoneMessage(uint8_t* data, size_t dataLen, SSLHandshakeLayer* container) : SSLHandshakeMessage(data, dataLen, container) {} - - virtual ~SSLServerHelloDoneMessage() {} - - // implement abstract methods - - std::string toString() const; +class SSLServerHelloDoneMessage : public SSLHandshakeMessage { + public: + /** + * C'tor for this class. Currently only in use in + * SSLHandshakeMessage::createHandshakeMessage() and should be used by a user + * @param[in] data The message as raw data + * @param[in] dataLen Message raw data length in bytes + * @param[in] container The SSL handshake layer which shall contain this + * message + */ + SSLServerHelloDoneMessage(uint8_t* data, size_t dataLen, + SSLHandshakeLayer* container) + : SSLHandshakeMessage(data, dataLen, container) {} + + virtual ~SSLServerHelloDoneMessage() {} + + // implement abstract methods + + std::string toString() const; }; - /** * @class SSLCertificateVerifyMessage - * Represents SSL/TLS certificate-verify message (type 15). Inherits from SSLHandshakeMessage and adds parsing - * functionality such as retrieving signed hash data as raw data (parsing may be added in the future) + * Represents SSL/TLS certificate-verify message (type 15). Inherits from + * SSLHandshakeMessage and adds parsing functionality such as retrieving signed + * hash data as raw data (parsing may be added in the future) * @todo This message type wasn't tested in unit-tests */ -class SSLCertificateVerifyMessage : public SSLHandshakeMessage -{ -public: - - /** - * C'tor for this class. Currently only in use in SSLHandshakeMessage::createHandshakeMessage() and should be used - * by a user - * @param[in] data The message as raw data - * @param[in] dataLen Message raw data length in bytes - * @param[in] container The SSL handshake layer which shall contain this message - */ - SSLCertificateVerifyMessage(uint8_t* data, size_t dataLen, SSLHandshakeLayer* container) : SSLHandshakeMessage(data, dataLen, container) {} - - virtual ~SSLCertificateVerifyMessage() {} - - /** - * @return A pointer to the signed hash data as raw data (byte array). Parsing of this data may be added - * in the future. Notice that if this message is spread over several packets in a way none of the signed hash data - * exists in this packet, NULL will be returned - */ - uint8_t* getSignedHash() const; - - /** - * @return The length of signed hash data returned by getSignedHash(). Notice that if this message is spread over - * several packets in a way none of this data exists in the current packet, 0 will be returned. Also, if some of - * the data exists in the consequent packets, the length that will be returned will be the length of data exists in - * the current packet only (and not the total length) - */ - size_t getSignedHashLength() const; - - // implement abstract methods - - std::string toString() const; +class SSLCertificateVerifyMessage : public SSLHandshakeMessage { + public: + /** + * C'tor for this class. Currently only in use in + * SSLHandshakeMessage::createHandshakeMessage() and should be used by a user + * @param[in] data The message as raw data + * @param[in] dataLen Message raw data length in bytes + * @param[in] container The SSL handshake layer which shall contain this + * message + */ + SSLCertificateVerifyMessage(uint8_t* data, size_t dataLen, + SSLHandshakeLayer* container) + : SSLHandshakeMessage(data, dataLen, container) {} + + virtual ~SSLCertificateVerifyMessage() {} + + /** + * @return A pointer to the signed hash data as raw data (byte array). Parsing + * of this data may be added in the future. Notice that if this message is + * spread over several packets in a way none of the signed hash data exists in + * this packet, NULL will be returned + */ + uint8_t* getSignedHash() const; + + /** + * @return The length of signed hash data returned by getSignedHash(). Notice + * that if this message is spread over several packets in a way none of this + * data exists in the current packet, 0 will be returned. Also, if some of the + * data exists in the consequent packets, the length that will be returned + * will be the length of data exists in the current packet only (and not the + * total length) + */ + size_t getSignedHashLength() const; + + // implement abstract methods + + std::string toString() const; }; - /** * @class SSLFinishedMessage - * Represents SSL/TLS finished message (type 20). Inherits from SSLHandshakeMessage and adds parsing - * functionality such as retrieving signed hash data as raw data (parsing may be added in the future) + * Represents SSL/TLS finished message (type 20). Inherits from + * SSLHandshakeMessage and adds parsing functionality such as retrieving signed + * hash data as raw data (parsing may be added in the future) * @todo This message type wasn't tested in unit-tests */ -class SSLFinishedMessage : public SSLHandshakeMessage -{ -public: - - /** - * C'tor for this class. Currently only in use in SSLHandshakeMessage::createHandshakeMessage() and should be used - * by a user - * @param[in] data The message as raw data - * @param[in] dataLen Message raw data length in bytes - * @param[in] container The SSL handshake layer which shall contain this message - */ - SSLFinishedMessage(uint8_t* data, size_t dataLen, SSLHandshakeLayer* container) : SSLHandshakeMessage(data, dataLen, container) {} - - virtual ~SSLFinishedMessage() {} - - /** - * @return A pointer to the signed hash data as raw data (byte array). Parsing of this data may be added - * in the future. Notice that if this message is spread over several packets in a way none of the signed hash data - * exists in this packet, NULL will be returned - */ - uint8_t* getSignedHash() const; - - /** - * @return The length of signed hash data returned by getSignedHash(). Notice that if the message is spread over - * several packets in a way none of this data exists in the current packet, 0 will be returned. Also, if some of - * the data exists in the consequent packets, the length that will be returned will be the length of data exists - * in the current packet only (and not the total length) - */ - size_t getSignedHashLength() const; - - // implement abstract methods - - std::string toString() const; +class SSLFinishedMessage : public SSLHandshakeMessage { + public: + /** + * C'tor for this class. Currently only in use in + * SSLHandshakeMessage::createHandshakeMessage() and should be used by a user + * @param[in] data The message as raw data + * @param[in] dataLen Message raw data length in bytes + * @param[in] container The SSL handshake layer which shall contain this + * message + */ + SSLFinishedMessage(uint8_t* data, size_t dataLen, + SSLHandshakeLayer* container) + : SSLHandshakeMessage(data, dataLen, container) {} + + virtual ~SSLFinishedMessage() {} + + /** + * @return A pointer to the signed hash data as raw data (byte array). Parsing + * of this data may be added in the future. Notice that if this message is + * spread over several packets in a way none of the signed hash data exists in + * this packet, NULL will be returned + */ + uint8_t* getSignedHash() const; + + /** + * @return The length of signed hash data returned by getSignedHash(). Notice + * that if the message is spread over several packets in a way none of this + * data exists in the current packet, 0 will be returned. Also, if some of the + * data exists in the consequent packets, the length that will be returned + * will be the length of data exists in the current packet only (and not the + * total length) + */ + size_t getSignedHashLength() const; + + // implement abstract methods + + std::string toString() const; }; - /** * @class SSLNewSessionTicketMessage - * Represents SSL/TLS new-session-ticket message (type 4). Inherits from SSLHandshakeMessage and adds parsing - * functionality such as retrieving session ticket data as raw data (parsing may be added in the future) + * Represents SSL/TLS new-session-ticket message (type 4). Inherits from + * SSLHandshakeMessage and adds parsing functionality such as retrieving session + * ticket data as raw data (parsing may be added in the future) */ -class SSLNewSessionTicketMessage : public SSLHandshakeMessage -{ -public: - - /** - * C'tor for this class. Currently only in use in SSLHandshakeMessage::createHandshakeMessage() and should be used - * by a user - * @param[in] data The message as raw data - * @param[in] dataLen Message raw data length in bytes - * @param[in] container The SSL handshake layer which shall contain this message - */ - SSLNewSessionTicketMessage(uint8_t* data, size_t dataLen, SSLHandshakeLayer* container) : SSLHandshakeMessage(data, dataLen, container) {} - - virtual ~SSLNewSessionTicketMessage() {} - - /** - * @return A pointer to the session ticket data as raw data (byte array). Parsing of this data may be added - * in the future. Notice that if this message is spread over several packets in a way none of the signed hash data - * exists in current packet, NULL will be returned - */ - uint8_t* getSessionTicketData() const; - - /** - * @return The length of session ticket data returned by getSessionTicketData(). Notice that if this message is - * spread over several packets in a way none of this data exists in the current packet, 0 will be returned. Also, - * if some of the data exist in the consequent packets, the length that will be returned will be the length of the - * data existing in the current packet only (and not the total length) - */ - size_t getSessionTicketDataLength() const; - - // implement abstract methods - - std::string toString() const; +class SSLNewSessionTicketMessage : public SSLHandshakeMessage { + public: + /** + * C'tor for this class. Currently only in use in + * SSLHandshakeMessage::createHandshakeMessage() and should be used by a user + * @param[in] data The message as raw data + * @param[in] dataLen Message raw data length in bytes + * @param[in] container The SSL handshake layer which shall contain this + * message + */ + SSLNewSessionTicketMessage(uint8_t* data, size_t dataLen, + SSLHandshakeLayer* container) + : SSLHandshakeMessage(data, dataLen, container) {} + + virtual ~SSLNewSessionTicketMessage() {} + + /** + * @return A pointer to the session ticket data as raw data (byte array). + * Parsing of this data may be added in the future. Notice that if this + * message is spread over several packets in a way none of the signed hash + * data exists in current packet, NULL will be returned + */ + uint8_t* getSessionTicketData() const; + + /** + * @return The length of session ticket data returned by + * getSessionTicketData(). Notice that if this message is spread over several + * packets in a way none of this data exists in the current packet, 0 will be + * returned. Also, if some of the data exist in the consequent packets, the + * length that will be returned will be the length of the data existing in the + * current packet only (and not the total length) + */ + size_t getSessionTicketDataLength() const; + + // implement abstract methods + + std::string toString() const; }; - /** * @class SSLUnknownMessage - * Represents an unknown type of message or an encrypted message that PcapPlusPlus can't determine its type. In these - * cases length can't always be determined from the message itself (especially if the message is encrypted), so - * the length of this message will always be the size counted from message start until the end of the layer + * Represents an unknown type of message or an encrypted message that + * PcapPlusPlus can't determine its type. In these cases length can't always be + * determined from the message itself (especially if the message is encrypted), + * so the length of this message will always be the size counted from message + * start until the end of the layer */ -class SSLUnknownMessage : public SSLHandshakeMessage -{ -public: - - /** - * C'tor for this class. Currently only in use in SSLHandshakeMessage::createHandshakeMessage() and should be used - * by a user - * @param[in] data The message as raw data - * @param[in] dataLen Message raw data length in bytes - * @param[in] container The SSL handshake layer which shall contain this message - */ - SSLUnknownMessage(uint8_t* data, size_t dataLen, SSLHandshakeLayer* container) : SSLHandshakeMessage(data, dataLen, container) {} - - virtual ~SSLUnknownMessage() {} - - // implement virtual and abstract methods - - /** - * @return Always ::SSL_HANDSHAKE_UNKNOWN (overridden from SSLHandshakeMessage) - */ - SSLHandshakeType getHandshakeType() const; - - /** - * @return The length of the data from message start until the end of the layer. Since it's an unknown type - * or an encrypted message the length parsed from the message can't be guaranteed to be the correct length. That's - * why the length returned is the size until the end of the layer - */ - size_t getMessageLength() const; - - std::string toString() const; +class SSLUnknownMessage : public SSLHandshakeMessage { + public: + /** + * C'tor for this class. Currently only in use in + * SSLHandshakeMessage::createHandshakeMessage() and should be used by a user + * @param[in] data The message as raw data + * @param[in] dataLen Message raw data length in bytes + * @param[in] container The SSL handshake layer which shall contain this + * message + */ + SSLUnknownMessage(uint8_t* data, size_t dataLen, SSLHandshakeLayer* container) + : SSLHandshakeMessage(data, dataLen, container) {} + + virtual ~SSLUnknownMessage() {} + + // implement virtual and abstract methods + + /** + * @return Always ::SSL_HANDSHAKE_UNKNOWN (overridden from + * SSLHandshakeMessage) + */ + SSLHandshakeType getHandshakeType() const; + + /** + * @return The length of the data from message start until the end of the + * layer. Since it's an unknown type or an encrypted message the length parsed + * from the message can't be guaranteed to be the correct length. That's why + * the length returned is the size until the end of the layer + */ + size_t getMessageLength() const; + + std::string toString() const; }; -template -TExtension* SSLClientHelloMessage::getExtensionOfType() const -{ - size_t vecSize = m_ExtensionList.size(); - for (size_t i = 0; i < vecSize; i++) - { - SSLExtension* curElem = const_cast(m_ExtensionList.at(i)); - if (dynamic_cast(curElem) != NULL) - return (TExtension*)curElem; - } - - return NULL; +template +TExtension* SSLClientHelloMessage::getExtensionOfType() const { + size_t vecSize = m_ExtensionList.size(); + for (size_t i = 0; i < vecSize; i++) { + SSLExtension* curElem = const_cast(m_ExtensionList.at(i)); + if (dynamic_cast(curElem) != NULL) + return (TExtension*)curElem; + } + + return NULL; } -template -TExtension* SSLServerHelloMessage::getExtensionOfType() const -{ - size_t vecSize = m_ExtensionList.size(); - for (size_t i = 0; i < vecSize; i++) - { - SSLExtension* curElem = const_cast(m_ExtensionList.at(i)); - if (dynamic_cast(curElem) != NULL) - return (TExtension*)curElem; - } - - return NULL; +template +TExtension* SSLServerHelloMessage::getExtensionOfType() const { + size_t vecSize = m_ExtensionList.size(); + for (size_t i = 0; i < vecSize; i++) { + SSLExtension* curElem = const_cast(m_ExtensionList.at(i)); + if (dynamic_cast(curElem) != NULL) + return (TExtension*)curElem; + } + + return NULL; } } // namespace pcpp diff --git a/Packet++/header/SSLLayer.h b/Packet++/header/SSLLayer.h index 5d2341a28d..34e514c9fd 100644 --- a/Packet++/header/SSLLayer.h +++ b/Packet++/header/SSLLayer.h @@ -1,19 +1,22 @@ #ifndef PACKETPP_SSL_LAYER #define PACKETPP_SSL_LAYER -#include "PointerVector.h" #include "Layer.h" +#include "PointerVector.h" #include "SSLCommon.h" #include "SSLHandshake.h" /** * @file - * This file as well as SSLCommon.h and SSLHandshake.h provide structures that represent SSL/TLS protocol. + * This file as well as SSLCommon.h and SSLHandshake.h provide structures that + represent SSL/TLS protocol. * Main features: * - All common SSL/TLS version are supported from SSL 3.0 to TLS 1.3 - * - All SSL/TLS message types are supported (at least the message types that are not encrypted) + * - All SSL/TLS message types are supported (at least the message types that + are not encrypted) * - More than 300 cipher-suites are supported - * - Only parsing capabilities exist, editing and creation of messages are not supported + * - Only parsing capabilities exist, editing and creation of messages are not + supported * - X509 certificate parsing is not supported * *

@@ -26,30 +29,35 @@ * - Alert record type * - Application data record type * - * Each record type corresponds to a layer class, and these classes inherit from one base class which is pcpp::SSLLayer. - * The pcpp::SSLLayer is an abstract class which cannot be instantiated. Only its 4 derived classes can be instantiated. - * This means you'll never see a layer of type pcpp::SSLLayer, you'll only see the type of the derived classes. + * Each record type corresponds to a layer class, and these classes inherit from + one base class which is pcpp::SSLLayer. + * The pcpp::SSLLayer is an abstract class which cannot be instantiated. Only + its 4 derived classes can be instantiated. + * This means you'll never see a layer of type pcpp::SSLLayer, you'll only see + the type of the derived classes. * A basic class diagram looks like this: @verbatim +----------------------------+ - +---| SSLHandshakeLayer | ===> Handshake record type - | +----------------------------+ + +---| SSLHandshakeLayer | ===> Handshake + record type | +----------------------------+ | | +----------------------------+ - +---| SSLChangeCipherSpecLayer | ===> Change cipher spec record type - | +----------------------------+ + +---| SSLChangeCipherSpecLayer | ===> Change + cipher spec record type | +----------------------------+ | +------------+ | +----------------------------+ - | SSLLayer |-------------+---| SSLAlertLayer | ===> Alert record type - | (abstract) | | +----------------------------+ + | SSLLayer |-------------+---| SSLAlertLayer | ===> Alert + record type | (abstract) | | +----------------------------+ +------------+ | | +----------------------------+ - +---| SSLApplicationDataLayer | ===> Application data record type + +---| SSLApplicationDataLayer | ===> Application + data record type +----------------------------+ @endverbatim * - * A single packet may include several SSL/TLS records, meaning several layer instances of these types, for example: + * A single packet may include several SSL/TLS records, meaning several layer + instances of these types, for example: * @verbatim @@ -62,7 +70,8 @@ +--------------------------+ | SSLHandshakeLayer | \ +--------------------------+ \ - | SSLChangeCipherSpecLayer | -------- 3 SSL/TLS records in the same packet! + | SSLChangeCipherSpecLayer | -------- 3 SSL/TLS records in the same + packet! +--------------------------+ / | SSLHandshakeLayer | / +--------------------------+ @@ -73,12 +82,18 @@ * * __SSL/TLS Handshake records:__
* - * The SSL/TLS handshake records are the most complex ones. These type of records encapsulate all messages between - * client and server during SSL/TLS connection establishment. To accomplish that a SSL/TLS handshake record holds - * zero or more handshake messages (usually it holds 1 message). These messages form the handshake negotiation between - * the client and the server. There are several types of handshake messages. Some of the are sent from client to server - * and some from server to client. PcapPlusPlus supports 11 of these types (definitely the most common ones). For each - * message there is a designated class which parses the message and exposes its attributes in an easy-to-use manner. + * The SSL/TLS handshake records are the most complex ones. These type of + records encapsulate all messages between + * client and server during SSL/TLS connection establishment. To accomplish that + a SSL/TLS handshake record holds + * zero or more handshake messages (usually it holds 1 message). These messages + form the handshake negotiation between + * the client and the server. There are several types of handshake messages. + Some of the are sent from client to server + * and some from server to client. PcapPlusPlus supports 11 of these types + (definitely the most common ones). For each + * message there is a designated class which parses the message and exposes its + attributes in an easy-to-use manner. * Here are the list of supported messages: * - Client-hello * - Server-hello @@ -92,463 +107,511 @@ * - Finished * - New-session-ticket * - * All handshake messages classes inherit from a base abstract class: pcpp::SSLHandshakeMessage which cannot be instantiated. - * Also, all of them reside in SSLHandshake.h. Following is a simple diagram of these classes: + * All handshake messages classes inherit from a base abstract class: + pcpp::SSLHandshakeMessage which cannot be instantiated. + * Also, all of them reside in SSLHandshake.h. Following is a simple diagram of + these classes: * @verbatim SSLHandshakeMessage | - +-------------------------------+ |--- SSLClientHelloMessage ==> Client-hello message - | SSLHandshakeLayer | | - +-------------------------------+ |--- SSLServerHelloMessage ==> Server-hello message - | -List of SSLHandshakeMessage | | - | Message1 | |---SSLCertificateMessage ==> Certificate message - | Message2 | | - | ... | |---SSLHelloRequestMessage ==> Hello-request message - | | | - +-------------------------------+ |---SSLServerKeyExchangeMessage ==> Server-key-exchange message + +-------------------------------+ |--- SSLClientHelloMessage ==> + Client-hello message | SSLHandshakeLayer | | + +-------------------------------+ |--- SSLServerHelloMessage ==> + Server-hello message | -List of SSLHandshakeMessage | | | Message1 + | |---SSLCertificateMessage ==> Certificate message | + Message2 | | | ... | + |---SSLHelloRequestMessage ==> Hello-request message | | | + +-------------------------------+ |---SSLServerKeyExchangeMessage ==> + Server-key-exchange message | - |---SSLClientKeyExchangeMessage ==> Client-key-exchange message + |---SSLClientKeyExchangeMessage ==> + Client-key-exchange message | - |---SSLCertificateRequestMessage ==> Certificate-request message + |---SSLCertificateRequestMessage + ==> Certificate-request message | - |---SSLServerHelloDoneMessage ==> Server-hello-done message + |---SSLServerHelloDoneMessage ==> + Server-hello-done message | - |---SSLCertificateVerifyMessage ==> Certificate-verify message + |---SSLCertificateVerifyMessage ==> + Certificate-verify message | - |---SSLFinishedMessage ==> Finished message + |---SSLFinishedMessage ==> Finished + message | - |---SSLNewSessionTicketMessage ==> New-session-ticket message + |---SSLNewSessionTicketMessage ==> + New-session-ticket message @endverbatim * - * In addition, for all handshake messages which aren't supported in PcapPlusPlus or for encrypted handshake messages + * In addition, for all handshake messages which aren't supported in + PcapPlusPlus or for encrypted handshake messages * There is another class: pcpp::SSLUnknownMessage * *

* * __Cipher suites:__
* - * Cipher suites are named combinations of authentication, encryption, message authentication code (MAC) and key exchange - * algorithms used to negotiate the security settings for a network connection using SSL/TLS. - * There are many known cipher-suites. PcapPlusPlus support above 300 of them, according to this list: + * Cipher suites are named combinations of authentication, encryption, message + authentication code (MAC) and key exchange + * algorithms used to negotiate the security settings for a network connection + using SSL/TLS. + * There are many known cipher-suites. PcapPlusPlus support above 300 of them, + according to this list: * http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml - * There is a designated class in PcapPlusPlus called pcpp::SSLCipherSuite which represents the cipher-suites and provides - * access to their attributes. Then there is a static instance of this class for each one of the supported cipher-suites. - * This means there are 300+ static instances of pcpp::SSLCipherSuite representing the different cipher suites. The user can - * access them through static methods in pcpp::SSLCipherSuite or from client-hello and server-hello messages where they appear + * There is a designated class in PcapPlusPlus called pcpp::SSLCipherSuite which + represents the cipher-suites and provides + * access to their attributes. Then there is a static instance of this class for + each one of the supported cipher-suites. + * This means there are 300+ static instances of pcpp::SSLCipherSuite + representing the different cipher suites. The user can + * access them through static methods in pcpp::SSLCipherSuite or from + client-hello and server-hello messages where they appear * *

* * __SSL/TLS extensions:__
* - * SSL/TLS handshake messages, specifically client-hello and server-hello usually include extensions. There are various - * types of extensions - some are more broadly used, some are less. In PcapPlusPlus there is a base class for all - * extensions: pcpp::SSLExtension. This class is instantiable and represents a generic extension, which means extension data - * isn't parsed and given to the user as raw data. Currently there are only two extension that are fully parsed which are - * server-name-indication (pcpp::SSLServerNameIndicationExtension) and SupportedVersions (pcpp::SSLSupportedVersionsExtension). - * Both inherit from pcpp::SSLExtension and add additional parsing relevant for the specific extension. - * All other extensions aren't parsed and are represented by instance of pcpp::SSLExtension. - * Access to extensions is done through the handshake messages classes, specifically pcpp::SSLClientHelloMessage and pcpp::SSLServerHelloMessage + * SSL/TLS handshake messages, specifically client-hello and server-hello + usually include extensions. There are various + * types of extensions - some are more broadly used, some are less. In + PcapPlusPlus there is a base class for all + * extensions: pcpp::SSLExtension. This class is instantiable and represents a + generic extension, which means extension data + * isn't parsed and given to the user as raw data. Currently there are only two + extension that are fully parsed which are + * server-name-indication (pcpp::SSLServerNameIndicationExtension) and + SupportedVersions (pcpp::SSLSupportedVersionsExtension). + * Both inherit from pcpp::SSLExtension and add additional parsing relevant for + the specific extension. + * All other extensions aren't parsed and are represented by instance of + pcpp::SSLExtension. + * Access to extensions is done through the handshake messages classes, + specifically pcpp::SSLClientHelloMessage and pcpp::SSLServerHelloMessage */ - /** * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - - /** - * @class SSLLayer - * The base class for the 4 record type classes. Each record type is represented as a layer. See SSLLayer.h for - * detailed explanation of the TLS/SSL protocol support in PcapPlusPlus. - * This class provides the common functionality used by all record types and also contains static methods for identifying - * an creating SSL/TLS record type layers - */ - class SSLLayer : public Layer - { - public: - - /** - * A static method that checks whether the port is considered as SSL/TLS - * @param[in] port The port number to be checked - */ - static inline bool isSSLPort(uint16_t port); - - /** - * A static methods that gets raw data of a layer and checks whether this data is a SSL/TLS record or not. This check is - * done using the source/dest port and matching of a legal record type in the raw data. The list of ports identified - * as SSL/TLS is hard-coded and includes the following ports: - * - Port 443 [HTTPS] - * - Port 261 [NSIIOPS] - * - Port 448 [DDM-SSL] - * - Port 563 [NNTPS] - * - Port 614 [SSHELL] - * - Port 465 [SMTPS] - * - Port 636 [LDAPS] - * - Port 989 [FTPS - data] - * - Port 990 [FTPS - control] - * - Port 992 [Telnet over TLS/SSL] - * - Port 993 [IMAPS] - * - Port 994 [IRCS] - * - Port 995 [POP3S] - * @param[in] srcPort The source port of the packet that contains the raw data. Source port (or dest port) are a - * criteria to identify SSL/TLS packets - * @param[in] dstPort The dest port of the packet that contains the raw data. Dest port (or source port) are a - * criteria to identify SSL/TLS packets - * @param[in] data The data to check - * @param[in] dataLen Length (in bytes) of the data - * @param[in] ignorePorts SSL/TLS ports are only relevant for parsing the first SSL/TLS message, but are not relevant - * for parsing subsequent messages. This parameter can be set to "true" to skip SSL/TLS ports check. This is an - * optional parameter and its default is "false" - */ - static bool IsSSLMessage(uint16_t srcPort, uint16_t dstPort, uint8_t* data, size_t dataLen, bool ignorePorts = false); - - /** - * A static method that creates SSL/TLS layers by raw data. This method parses the raw data, finds if and which - * SSL/TLS record it is and creates the corresponding record layer. It's the responsibility of the user to free - * the created object when done using it - * @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 - * @return A pointer to the newly created record layer. If no SSL/TLS record could be identified from the raw data - * NULL is returned - */ - static SSLLayer* createSSLMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); - - /** - * Get a pointer to the record header. Notice this points directly to the data, so every change will change the actual packet data - * @return A pointer to the @ref ssl_tls_record_layer - */ - ssl_tls_record_layer* getRecordLayer() const { return (ssl_tls_record_layer*)m_Data; } - - /** - * @return The SSL/TLS version used in this record (parsed from the record) - */ - SSLVersion getRecordVersion() const; - - /** - * @return The SSL/TLS record type as parsed from the record - */ - SSLRecordType getRecordType() const; - - // implement abstract methods - - /** - * @return The record size as extracted from the record data (in ssl_tls_record_layer#length) - */ - size_t getHeaderLen() const; - - /** - * Several SSL/TLS records can reside in a single packets. So this method checks the remaining data and if it's - * identified as SSL/TLS it creates another SSL/TLS record layer as the next layer - */ - void parseNextLayer(); - - OsiModelLayer getOsiModelLayer() const { return OsiModelPresentationLayer; } - - protected: - SSLLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) { m_Protocol = SSL; } - - }; // class SSLLayer - - - /** - * @class SSLHandshakeLayer - * Represents SSL/TLS handshake layer. This layer may contain one or more handshake messages (all of them inherit from - * the base class SSLHandshakeMessage) which are the SSL/TLS handshake message sent between a client and a server until - * they establish a secure connection (e.g client-hello, server-hello, certificate, client-key-exchange, - * server-key-exchange, etc.). Usually this layer will contain just one message (as the first example below - * demonstrates). But there are cases a layer may contain more than 1 message. To better explain this layer structure - * we'll use 2 examples. The first will be client-hello message. The layer structure will look like this: - @verbatim - - |------------------- SSLHandshakeLayer ----------------------| - +----------------------+-------------------------------------+ - | ssl_tls_record_layer | SSLClientHelloMessage | - | struct | | - +----------------------+-------------------------------------+ - / | \ | \ \ \ - / version \ | handshake \ \ \ - / TLS1_0 \ type \ \ rest of - type \ | SSL_CLIENT_HELLO \ \ message fields... - SSL_HANDSHAKE length handshake \ - (22) xxx | version message - TLS1_2 length - | yyy - @endverbatim - - * Second example is a multiple-message handshake layer comprises of server-hello, certificate and server-key-exchange - * messages: - - @verbatim - - |---------------------------------------------- SSLHandshakeLayer -----------------------------------------------------| - +----------------------+-------------------------------------+---------------------------+-----------------------------+ - | ssl_tls_record_layer | SSLServerHelloMessage | SSLCertificateMessage | SSLServerKeyExchangeMessage | - | struct | | | | - +----------------------+-------------------------------------+---------------------------+-----------------------------+ - / | \ | \ \ | \ | \ - / version \ | handshake \ rest of | | rest | | rest - / TLS1_0 \ type \ message handshake of fields... handshake of fields... - type \ | SSL_SERVER_HELLO \ fields...| type | type - SSL_HANDSHAKE length handshake SSL_CERTIFICATE SSL_SERVER_KEY_EXCHANGE - (22) xxx | version,length | | - @endverbatim - */ - class SSLHandshakeLayer: public SSLLayer - { - public: - - /** - * C'tor for this class 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 - */ - SSLHandshakeLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); - - /** - * @return The number of messages in this layer instance - */ - size_t getHandshakeMessagesCount() const { return m_MessageList.size(); } - - /** - * Get a pointer to an handshake message by index. The message are numbered according to their order of appearance - * in the layer. If index is out of bounds (less than 0 or larger than total amount of message) NULL will be - * returned - * @param[in] index The index of the message to return - * @return The pointer to the message object or NULL if index is out of bounds - */ - SSLHandshakeMessage* getHandshakeMessageAt(int index) const; - - /** - * A templated method to get a message of a certain type. If no message of such type is found, NULL is returned - * @return A pointer to the message of the requested type, NULL if not found - */ - template - THandshakeMessage* getHandshakeMessageOfType() const; - - /** - * A templated method to get the first message of a certain type, starting to search from a certain message. - * For example: if the layer looks like: HelloRequest(1) -> HelloRequest(2) - * and the user put HelloRequest(1) as a parameter and wishes to search for an HelloRequest message, the - * HelloRequest(2) will be returned.
- * If no layer of such type is found, NULL is returned - * @param[in] after A pointer to the message to start search from - * @return A pointer to the message of the requested type, NULL if not found - */ - template - THandshakeMessage* getNextHandshakeMessageOfType(const SSLHandshakeMessage* after) const; - - // implement abstract methods - - std::string toString() const; - - /** - * There are no calculated fields for this layer - */ - void computeCalculateFields() {} - - private: - PointerVector m_MessageList; - }; // class SSLHandshakeLayer - - - /** - * @class SSLChangeCipherSpecLayer - * Represents SSL/TLS change-cipher-spec layer. This layer has no additional fields besides common fields described in - * SSLLayer - */ - class SSLChangeCipherSpecLayer : public SSLLayer - { - public: - - /** - * C'tor for this class 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 - */ - SSLChangeCipherSpecLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) - : SSLLayer(data, dataLen, prevLayer, packet) {} - - ~SSLChangeCipherSpecLayer() {} - - // implement abstract methods - - std::string toString() const; - - /** - * There are no calculated fields for this layer - */ - void computeCalculateFields() {} - }; // class SSLChangeCipherSpecLayer - - - /** - * @class SSLAlertLayer - * Represents SSL/TLS alert layer. Inherits from SSLLayer and adds parsing functionality such as retrieving the alert - * level and description - */ - class SSLAlertLayer : public SSLLayer - { - public: - - /** - * C'tor for this class 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 - */ - SSLAlertLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) - : SSLLayer(data, dataLen, prevLayer, packet) {} - - ~SSLAlertLayer() {} - - /** - * @return SSL/TLS alert level. Will return ::SSL_ALERT_LEVEL_ENCRYPTED if alert is encrypted - */ - SSLAlertLevel getAlertLevel() const; - - /** - * @return SSL/TLS alert description. Will return ::SSL_ALERT_ENCRYPTED if alert is encrypted - */ - SSLAlertDescription getAlertDescription(); - - // implement abstract methods - - std::string toString() const; - - /** - * There are no calculated fields for this layer - */ - void computeCalculateFields() {} - }; // class SSLAlertLayer - - - /** - * @class SSLApplicationDataLayer - * Represents SSL/TLS application data layer. This message contains the encrypted data transferred from client to - * server and vice-versa after the SSL/TLS handshake was completed successfully - */ - class SSLApplicationDataLayer : public SSLLayer - { - public: - - /** - * C'tor for this class 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 - */ - SSLApplicationDataLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) - : SSLLayer(data, dataLen, prevLayer, packet) {} - - ~SSLApplicationDataLayer() {} - - /** - * @return A pointer to the encrypted data. This data can be decrypted only if you have the symmetric key - * that was agreed between the client and the server during SSL/TLS handshake process - */ - uint8_t* getEncryptedData() const; - - /** - * @return The length in bytes of the encrypted data returned in getEncryptedData() - */ - size_t getEncryptedDataLen() const; - - // implement abstract methods - - std::string toString() const; - - /** - * There are no calculated fields for this layer - */ - void computeCalculateFields() {} - }; // class SSLApplicationDataLayer - - - template - THandshakeMessage* SSLHandshakeLayer::getHandshakeMessageOfType() const - { - size_t vecSize = m_MessageList.size(); - for (size_t i = 0; i < vecSize; i++) - { - SSLHandshakeMessage* curElem = const_cast(m_MessageList.at(i)); - if (dynamic_cast(curElem) != NULL) - return (THandshakeMessage*)curElem; - } - - // element not found - return NULL; - } // getHandshakeMessageOfType - - - template - THandshakeMessage* SSLHandshakeLayer::getNextHandshakeMessageOfType(const SSLHandshakeMessage* after) const - { - size_t vecSize = m_MessageList.size(); - size_t afterIndex; - - // find the index of "after" - for (afterIndex = 0; afterIndex < vecSize; afterIndex++) - { - SSLHandshakeMessage* curElem = const_cast(m_MessageList.at(afterIndex)); - if (curElem == after) - break; - } - - // "after" not found - if (afterIndex == vecSize) - return NULL; - - for (size_t i = afterIndex+1; i < vecSize; i++) - { - SSLHandshakeMessage* curElem = const_cast(m_MessageList.at(i)); - if (dynamic_cast(curElem) != NULL) - return (THandshakeMessage*)curElem; - } - - // element not found - return NULL; - } // getNextHandshakeMessageOfType - - - // implementation of inline methods - - bool SSLLayer::isSSLPort(uint16_t port) - { - if (port == 443) // HTTPS, this is likely case - return true; - - switch (port) - { - case 261: // NSIIOPS - case 448: // DDM-SSL - case 465: // SMTPS - case 563: // NNTPS - case 614: // SSHELL - case 636: // LDAPS - case 989: // FTPS - data - case 990: // FTPS - control - case 992: // Telnet over TLS/SSL - case 993: // IMAPS - case 994: // IRCS - case 995: // POP3S - return true; - default: - return false; - } - } // isSSLPort +namespace pcpp { + +/** + * @class SSLLayer + * The base class for the 4 record type classes. Each record type is represented + * as a layer. See SSLLayer.h for detailed explanation of the TLS/SSL protocol + * support in PcapPlusPlus. This class provides the common functionality used by + * all record types and also contains static methods for identifying an creating + * SSL/TLS record type layers + */ +class SSLLayer : public Layer { + public: + /** + * A static method that checks whether the port is considered as SSL/TLS + * @param[in] port The port number to be checked + */ + static inline bool isSSLPort(uint16_t port); + + /** + * A static methods that gets raw data of a layer and checks whether this data + * is a SSL/TLS record or not. This check is done using the source/dest port + * and matching of a legal record type in the raw data. The list of ports + * identified as SSL/TLS is hard-coded and includes the following ports: + * - Port 443 [HTTPS] + * - Port 261 [NSIIOPS] + * - Port 448 [DDM-SSL] + * - Port 563 [NNTPS] + * - Port 614 [SSHELL] + * - Port 465 [SMTPS] + * - Port 636 [LDAPS] + * - Port 989 [FTPS - data] + * - Port 990 [FTPS - control] + * - Port 992 [Telnet over TLS/SSL] + * - Port 993 [IMAPS] + * - Port 994 [IRCS] + * - Port 995 [POP3S] + * @param[in] srcPort The source port of the packet that contains the raw + * data. Source port (or dest port) are a criteria to identify SSL/TLS packets + * @param[in] dstPort The dest port of the packet that contains the raw data. + * Dest port (or source port) are a criteria to identify SSL/TLS packets + * @param[in] data The data to check + * @param[in] dataLen Length (in bytes) of the data + * @param[in] ignorePorts SSL/TLS ports are only relevant for parsing the + * first SSL/TLS message, but are not relevant for parsing subsequent + * messages. This parameter can be set to "true" to skip SSL/TLS ports check. + * This is an optional parameter and its default is "false" + */ + static bool IsSSLMessage(uint16_t srcPort, uint16_t dstPort, uint8_t* data, + size_t dataLen, bool ignorePorts = false); + + /** + * A static method that creates SSL/TLS layers by raw data. This method parses + * the raw data, finds if and which SSL/TLS record it is and creates the + * corresponding record layer. It's the responsibility of the user to free the + * created object when done using it + * @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 + * @return A pointer to the newly created record layer. If no SSL/TLS record + * could be identified from the raw data NULL is returned + */ + static SSLLayer* createSSLMessage(uint8_t* data, size_t dataLen, + Layer* prevLayer, Packet* packet); + + /** + * Get a pointer to the record header. Notice this points directly to the + * data, so every change will change the actual packet data + * @return A pointer to the @ref ssl_tls_record_layer + */ + ssl_tls_record_layer* getRecordLayer() const { + return (ssl_tls_record_layer*)m_Data; + } + + /** + * @return The SSL/TLS version used in this record (parsed from the record) + */ + SSLVersion getRecordVersion() const; + + /** + * @return The SSL/TLS record type as parsed from the record + */ + SSLRecordType getRecordType() const; + + // implement abstract methods + + /** + * @return The record size as extracted from the record data (in + * ssl_tls_record_layer#length) + */ + size_t getHeaderLen() const; + + /** + * Several SSL/TLS records can reside in a single packets. So this method + * checks the remaining data and if it's identified as SSL/TLS it creates + * another SSL/TLS record layer as the next layer + */ + void parseNextLayer(); + + OsiModelLayer getOsiModelLayer() const { return OsiModelPresentationLayer; } + + protected: + SSLLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : Layer(data, dataLen, prevLayer, packet) { + m_Protocol = SSL; + } + +}; // class SSLLayer + +/** + * @class SSLHandshakeLayer + * Represents SSL/TLS handshake layer. This layer may contain one or more + handshake messages (all of them inherit from + * the base class SSLHandshakeMessage) which are the SSL/TLS handshake message + sent between a client and a server until + * they establish a secure connection (e.g client-hello, server-hello, + certificate, client-key-exchange, + * server-key-exchange, etc.). Usually this layer will contain just one message + (as the first example below + * demonstrates). But there are cases a layer may contain more than 1 message. + To better explain this layer structure + * we'll use 2 examples. The first will be client-hello message. The layer + structure will look like this: + @verbatim + + |------------------- SSLHandshakeLayer ----------------------| + +----------------------+-------------------------------------+ + | ssl_tls_record_layer | SSLClientHelloMessage | + | struct | | + +----------------------+-------------------------------------+ + / | \ | \ \ \ + / version \ | handshake \ \ \ + / TLS1_0 \ type \ \ rest of + type \ | SSL_CLIENT_HELLO \ \ message + fields... + SSL_HANDSHAKE length handshake \ + (22) xxx | version message + TLS1_2 length + | yyy + @endverbatim + + * Second example is a multiple-message handshake layer comprises of + server-hello, certificate and server-key-exchange + * messages: + + @verbatim + + |---------------------------------------------- SSLHandshakeLayer + -----------------------------------------------------| + +----------------------+-------------------------------------+---------------------------+-----------------------------+ + | ssl_tls_record_layer | SSLServerHelloMessage | + SSLCertificateMessage | SSLServerKeyExchangeMessage | | struct | | | | + +----------------------+-------------------------------------+---------------------------+-----------------------------+ + / | \ | \ \ | + \ | \ / version \ | handshake \ + rest of | | rest | | rest / TLS1_0 \ + type \ message handshake of fields... handshake of + fields... type \ | SSL_SERVER_HELLO \ fields...| + type | type SSL_HANDSHAKE length handshake + SSL_CERTIFICATE SSL_SERVER_KEY_EXCHANGE (22) xxx | + version,length | | + @endverbatim + */ +class SSLHandshakeLayer : public SSLLayer { + public: + /** + * C'tor for this class 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 + */ + SSLHandshakeLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet); + + /** + * @return The number of messages in this layer instance + */ + size_t getHandshakeMessagesCount() const { return m_MessageList.size(); } + + /** + * Get a pointer to an handshake message by index. The message are numbered + * according to their order of appearance in the layer. If index is out of + * bounds (less than 0 or larger than total amount of message) NULL will be + * returned + * @param[in] index The index of the message to return + * @return The pointer to the message object or NULL if index is out of bounds + */ + SSLHandshakeMessage* getHandshakeMessageAt(int index) const; + + /** + * A templated method to get a message of a certain type. If no message of + * such type is found, NULL is returned + * @return A pointer to the message of the requested type, NULL if not found + */ + template + THandshakeMessage* getHandshakeMessageOfType() const; + + /** + * A templated method to get the first message of a certain type, starting to + * search from a certain message. For example: if the layer looks like: + * HelloRequest(1) -> HelloRequest(2) and the user put HelloRequest(1) as a + * parameter and wishes to search for an HelloRequest message, the + * HelloRequest(2) will be returned.
+ * If no layer of such type is found, NULL is returned + * @param[in] after A pointer to the message to start search from + * @return A pointer to the message of the requested type, NULL if not found + */ + template + THandshakeMessage* + getNextHandshakeMessageOfType(const SSLHandshakeMessage* after) const; + + // implement abstract methods + + std::string toString() const; + + /** + * There are no calculated fields for this layer + */ + void computeCalculateFields() {} + + private: + PointerVector m_MessageList; +}; // class SSLHandshakeLayer + +/** + * @class SSLChangeCipherSpecLayer + * Represents SSL/TLS change-cipher-spec layer. This layer has no additional + * fields besides common fields described in SSLLayer + */ +class SSLChangeCipherSpecLayer : public SSLLayer { + public: + /** + * C'tor for this class 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 + */ + SSLChangeCipherSpecLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet) + : SSLLayer(data, dataLen, prevLayer, packet) {} + + ~SSLChangeCipherSpecLayer() {} + + // implement abstract methods + + std::string toString() const; + + /** + * There are no calculated fields for this layer + */ + void computeCalculateFields() {} +}; // class SSLChangeCipherSpecLayer + +/** + * @class SSLAlertLayer + * Represents SSL/TLS alert layer. Inherits from SSLLayer and adds parsing + * functionality such as retrieving the alert level and description + */ +class SSLAlertLayer : public SSLLayer { + public: + /** + * C'tor for this class 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 + */ + SSLAlertLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : SSLLayer(data, dataLen, prevLayer, packet) {} + + ~SSLAlertLayer() {} + + /** + * @return SSL/TLS alert level. Will return ::SSL_ALERT_LEVEL_ENCRYPTED if + * alert is encrypted + */ + SSLAlertLevel getAlertLevel() const; + + /** + * @return SSL/TLS alert description. Will return ::SSL_ALERT_ENCRYPTED if + * alert is encrypted + */ + SSLAlertDescription getAlertDescription(); + + // implement abstract methods + + std::string toString() const; + + /** + * There are no calculated fields for this layer + */ + void computeCalculateFields() {} +}; // class SSLAlertLayer + +/** + * @class SSLApplicationDataLayer + * Represents SSL/TLS application data layer. This message contains the + * encrypted data transferred from client to server and vice-versa after the + * SSL/TLS handshake was completed successfully + */ +class SSLApplicationDataLayer : public SSLLayer { + public: + /** + * C'tor for this class 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 + */ + SSLApplicationDataLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet) + : SSLLayer(data, dataLen, prevLayer, packet) {} + + ~SSLApplicationDataLayer() {} + + /** + * @return A pointer to the encrypted data. This data can be decrypted only if + * you have the symmetric key that was agreed between the client and the + * server during SSL/TLS handshake process + */ + uint8_t* getEncryptedData() const; + + /** + * @return The length in bytes of the encrypted data returned in + * getEncryptedData() + */ + size_t getEncryptedDataLen() const; + + // implement abstract methods + + std::string toString() const; + + /** + * There are no calculated fields for this layer + */ + void computeCalculateFields() {} +}; // class SSLApplicationDataLayer + +template +THandshakeMessage* SSLHandshakeLayer::getHandshakeMessageOfType() const { + size_t vecSize = m_MessageList.size(); + for (size_t i = 0; i < vecSize; i++) { + SSLHandshakeMessage* curElem = + const_cast(m_MessageList.at(i)); + if (dynamic_cast(curElem) != NULL) + return (THandshakeMessage*)curElem; + } + + // element not found + return NULL; +} // getHandshakeMessageOfType + +template +THandshakeMessage* SSLHandshakeLayer::getNextHandshakeMessageOfType( + const SSLHandshakeMessage* after) const { + size_t vecSize = m_MessageList.size(); + size_t afterIndex; + + // find the index of "after" + for (afterIndex = 0; afterIndex < vecSize; afterIndex++) { + SSLHandshakeMessage* curElem = + const_cast(m_MessageList.at(afterIndex)); + if (curElem == after) + break; + } + + // "after" not found + if (afterIndex == vecSize) + return NULL; + + for (size_t i = afterIndex + 1; i < vecSize; i++) { + SSLHandshakeMessage* curElem = + const_cast(m_MessageList.at(i)); + if (dynamic_cast(curElem) != NULL) + return (THandshakeMessage*)curElem; + } + + // element not found + return NULL; +} // getNextHandshakeMessageOfType + +// implementation of inline methods + +bool SSLLayer::isSSLPort(uint16_t port) { + if (port == 443) // HTTPS, this is likely case + return true; + + switch (port) { + case 261: // NSIIOPS + case 448: // DDM-SSL + case 465: // SMTPS + case 563: // NNTPS + case 614: // SSHELL + case 636: // LDAPS + case 989: // FTPS - data + case 990: // FTPS - control + case 992: // Telnet over TLS/SSL + case 993: // IMAPS + case 994: // IRCS + case 995: // POP3S + return true; + default: + return false; + } +} // isSSLPort } // namespace pcpp diff --git a/Packet++/header/SdpLayer.h b/Packet++/header/SdpLayer.h index 9ae16e6559..994115d9f3 100644 --- a/Packet++/header/SdpLayer.h +++ b/Packet++/header/SdpLayer.h @@ -11,159 +11,180 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ +namespace pcpp { /** Protocol version (v) */ #define PCPP_SDP_PROTOCOL_VERSION_FIELD "v" /** Originator and session identifier (o) */ -#define PCPP_SDP_ORIGINATOR_FIELD "o" +#define PCPP_SDP_ORIGINATOR_FIELD "o" /** Session name (s) */ -#define PCPP_SDP_SESSION_NAME_FIELD "s" +#define PCPP_SDP_SESSION_NAME_FIELD "s" /** Session title, media title or short information (i) */ -#define PCPP_SDP_INFO_FIELD "i" +#define PCPP_SDP_INFO_FIELD "i" /** URI of description (u) */ -#define PCPP_SDP_URI_FIELD "u" +#define PCPP_SDP_URI_FIELD "u" /** Email address with optional name of contacts (e) */ -#define PCPP_SDP_EMAIL_FIELD "e" +#define PCPP_SDP_EMAIL_FIELD "e" /** Phone number with optional name of contacts (p) */ -#define PCPP_SDP_PHONE_FIELD "p" +#define PCPP_SDP_PHONE_FIELD "p" /** Connection information (c) */ -#define PCPP_SDP_CONNECTION_INFO_FIELD "c" +#define PCPP_SDP_CONNECTION_INFO_FIELD "c" /** Bandwidth information (b) */ -#define PCPP_SDP_BANDWIDTH_FIELD "b" +#define PCPP_SDP_BANDWIDTH_FIELD "b" /** Time the session is active (t) */ -#define PCPP_SDP_TIME_FIELD "t" +#define PCPP_SDP_TIME_FIELD "t" /** Repeat times (r) */ -#define PCPP_SDP_REPEAT_TIMES_FIELD "r" +#define PCPP_SDP_REPEAT_TIMES_FIELD "r" /** Time zone adjustments (z) */ -#define PCPP_SDP_TIME_ZONE_FIELD "z" +#define PCPP_SDP_TIME_ZONE_FIELD "z" /** Encryption key (k) */ -#define PCPP_SDP_ENCRYPTION_KEY_FIELD "k" +#define PCPP_SDP_ENCRYPTION_KEY_FIELD "k" /** Media attribute (a) */ -#define PCPP_SDP_MEDIA_ATTRIBUTE_FIELD "a" +#define PCPP_SDP_MEDIA_ATTRIBUTE_FIELD "a" /** Media name and transport address (m) */ -#define PCPP_SDP_MEDIA_NAME_FIELD "m" - - /** - * @class SdpLayer - * Represents a SDP (Session Description Protocol) message. SDP is a text-based protocol described by a series of fields, one per line (lines are separated by CRLF). - * The form of each field is as follows:
- * @code - * [character]=[value] - * @endcode - * Each character represents a certain type of field. All field type are represented as macros in SdpLayer.h file - * (for example: PCPP_SDP_ORIGINATOR_FIELD is a macro for the originator field (o=) ).
- * For more details about SDP structure please refer to its Wikipedia page: https://en.wikipedia.org/wiki/Session_Description_Protocol - */ - class SdpLayer : public TextBasedProtocolMessage - { - public: - - /** 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 - */ - SdpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); - - /** - * An empty c'tor which initialize an empty message with no fields - */ - SdpLayer(); - - /** - * A c'tor which initializes a message with the minimum required fields.
- * After this c'tor the message will look like this: - * - * @code - * v=0 - * o=[username] [sessionID] [sessionVersion] IN IP4 [ipAddress] - * s=[sessionName] - * c=IN IP4 [ipAddress] - * t=[startTime] [endTime] - * @endcode - * - * @param[in] username User's login on the originating host - * @param[in] sessionID A globally unique identifier for the session - * @param[in] sessionVersion A version number for this session description - * @param[in] ipAddress The address of the machine from which the session is created - * @param[in] sessionName A textual session name - * @param[in] startTime The start time of the session - * @param[in] stopTime The stop time of the session - */ - SdpLayer(const std::string& username, long sessionID, long sessionVersion, IPv4Address ipAddress, const std::string& sessionName, long startTime, long stopTime); - - ~SdpLayer() {} - - /** - * A copy constructor for this layer. Inherits the base copy constructor and doesn't add - * anything else - * @param[in] other The instance to copy from - */ - SdpLayer(const SdpLayer& other) : TextBasedProtocolMessage(other) {} - - /** - * An assignment operator overload for this layer. Inherits the base assignment operator - * and doesn't add anything else - * @param[in] other The instance to copy from - */ - SdpLayer& operator=(const SdpLayer& other) { TextBasedProtocolMessage::operator=(other); return *this; } - - /** - * The 'originator' field (o=) contains the IP address of the the machine from which the session is created. - * This IP address can be used to track the RTP data relevant for the call. This method extracts this IP address from the 'originator' field and returns it. - * A value of IPv4Address#Zero will be returned in the following cases: (1) if 'originator' field doesn't exist; (2) if it doesn't contain the IP address; - * (3) if it contains a non-IPv4 address - * @return The IP address of the the machine from which the session is created - */ - IPv4Address getOwnerIPv4Address() const; - - /** - * The 'media-description' field (m=) contains the transport port to which the media stream is sent. This port can be used to track the RTP data relevant for the call. - * This method extracts this port from the 'media-description' field and returns it. Since a SDP message can contain several 'media-description' fields, one for each media type - * (e.g audio, image, etc.), the user is required to provide the media type. A value of 0 will be returned in the following cases: (1) if 'media-description' field doesn't - * exist; (2) if provided media type was not found; (3) if 'media-description' field didn't contain a port - * @param[in] mediaType The media type to search in - * @return The transport port to which the media stream is sent - */ - uint16_t getMediaPort(const std::string& mediaType) const; - - /** - * Adds a 'media-description' field (m=) with all necessary data and attribute fields (a=) with data relevant for this media.
- * After this method is run the following block of fields will be added at the end of the message: - * - * @code - * m=[mediaType] [mediaPort] [mediaProtocol] [mediaFormat] - * a=[1st media attribute] - * a=[2nd media attribute] - * ... - * @endcode - * - * @param[in] mediaType The media type, usually "audio", "video", "text" or "image" - * @param[in] mediaPort The transport port to which the media stream is sent - * @param[in] mediaProtocol The transport protocol, usually "udp", "RTP/AVP" or "RTP/SAVP" - * @param[in] mediaFormat A space-separated list of media format description. For example: "8 96" - * @param[in] mediaAttributes A vector of media attributes. Each string in this vector will be - * translated into a 'media-attribute' field (a=) - * @return True if all fields were added properly or false if at least one field was failed to be added - */ - bool addMediaDescription(const std::string& mediaType, uint16_t mediaPort, const std::string& mediaProtocol, const std::string& mediaFormat, std::vector mediaAttributes); - - // overridden methods - - OsiModelLayer getOsiModelLayer() const { return OsiModelSesionLayer; } - - std::string toString() const; - - protected: - - // implementation of abstract methods - char getHeaderFieldNameValueSeparator() const { return '='; } - bool spacesAllowedBetweenHeaderFieldNameAndValue() const { return false; } - - }; -} +#define PCPP_SDP_MEDIA_NAME_FIELD "m" + +/** + * @class SdpLayer + * Represents a SDP (Session Description Protocol) message. SDP is a text-based + * protocol described by a series of fields, one per line (lines are separated + * by CRLF). The form of each field is as follows:
+ * @code + * [character]=[value] + * @endcode + * Each character represents a certain type of field. All field type are + * represented as macros in SdpLayer.h file (for example: + * PCPP_SDP_ORIGINATOR_FIELD is a macro for the originator field (o=) ).
For + * more details about SDP structure please refer to its Wikipedia page: + * https://en.wikipedia.org/wiki/Session_Description_Protocol + */ +class SdpLayer : public TextBasedProtocolMessage { + public: + /** 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 + */ + SdpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); + + /** + * An empty c'tor which initialize an empty message with no fields + */ + SdpLayer(); + + /** + * A c'tor which initializes a message with the minimum required fields.
+ * After this c'tor the message will look like this: + * + * @code + * v=0 + * o=[username] [sessionID] [sessionVersion] IN IP4 [ipAddress] + * s=[sessionName] + * c=IN IP4 [ipAddress] + * t=[startTime] [endTime] + * @endcode + * + * @param[in] username User's login on the originating host + * @param[in] sessionID A globally unique identifier for the session + * @param[in] sessionVersion A version number for this session description + * @param[in] ipAddress The address of the machine from which the session is + * created + * @param[in] sessionName A textual session name + * @param[in] startTime The start time of the session + * @param[in] stopTime The stop time of the session + */ + SdpLayer(const std::string& username, long sessionID, long sessionVersion, + IPv4Address ipAddress, const std::string& sessionName, + long startTime, long stopTime); + + ~SdpLayer() {} + + /** + * A copy constructor for this layer. Inherits the base copy constructor and + * doesn't add anything else + * @param[in] other The instance to copy from + */ + SdpLayer(const SdpLayer& other) : TextBasedProtocolMessage(other) {} + + /** + * An assignment operator overload for this layer. Inherits the base + * assignment operator and doesn't add anything else + * @param[in] other The instance to copy from + */ + SdpLayer& operator=(const SdpLayer& other) { + TextBasedProtocolMessage::operator=(other); + return *this; + } + + /** + * The 'originator' field (o=) contains the IP address of the the machine from + * which the session is created. This IP address can be used to track the RTP + * data relevant for the call. This method extracts this IP address from the + * 'originator' field and returns it. A value of IPv4Address#Zero will be + * returned in the following cases: (1) if 'originator' field doesn't exist; + * (2) if it doesn't contain the IP address; (3) if it contains a non-IPv4 + * address + * @return The IP address of the the machine from which the session is created + */ + IPv4Address getOwnerIPv4Address() const; + + /** + * The 'media-description' field (m=) contains the transport port to which the + * media stream is sent. This port can be used to track the RTP data relevant + * for the call. This method extracts this port from the 'media-description' + * field and returns it. Since a SDP message can contain several + * 'media-description' fields, one for each media type (e.g audio, image, + * etc.), the user is required to provide the media type. A value of 0 will be + * returned in the following cases: (1) if 'media-description' field doesn't + * exist; (2) if provided media type was not found; (3) if 'media-description' + * field didn't contain a port + * @param[in] mediaType The media type to search in + * @return The transport port to which the media stream is sent + */ + uint16_t getMediaPort(const std::string& mediaType) const; + + /** + * Adds a 'media-description' field (m=) with all necessary data and attribute + * fields (a=) with data relevant for this media.
After this method is run + * the following block of fields will be added at the end of the message: + * + * @code + * m=[mediaType] [mediaPort] [mediaProtocol] [mediaFormat] + * a=[1st media attribute] + * a=[2nd media attribute] + * ... + * @endcode + * + * @param[in] mediaType The media type, usually "audio", "video", "text" or + * "image" + * @param[in] mediaPort The transport port to which the media stream is sent + * @param[in] mediaProtocol The transport protocol, usually "udp", "RTP/AVP" + * or "RTP/SAVP" + * @param[in] mediaFormat A space-separated list of media format description. + * For example: "8 96" + * @param[in] mediaAttributes A vector of media attributes. Each string in + * this vector will be translated into a 'media-attribute' field (a=) + * @return True if all fields were added properly or false if at least one + * field was failed to be added + */ + bool addMediaDescription(const std::string& mediaType, uint16_t mediaPort, + const std::string& mediaProtocol, + const std::string& mediaFormat, + std::vector mediaAttributes); + + // overridden methods + + OsiModelLayer getOsiModelLayer() const { return OsiModelSesionLayer; } + + std::string toString() const; + + protected: + // implementation of abstract methods + char getHeaderFieldNameValueSeparator() const { return '='; } + bool spacesAllowedBetweenHeaderFieldNameAndValue() const { return false; } +}; +} // namespace pcpp #endif // PACKETPP_SDP_LAYER diff --git a/Packet++/header/SingleCommandTextProtocol.h b/Packet++/header/SingleCommandTextProtocol.h index 0d2cc05e6b..2ec00b0e58 100644 --- a/Packet++/header/SingleCommandTextProtocol.h +++ b/Packet++/header/SingleCommandTextProtocol.h @@ -1,8 +1,8 @@ #ifndef PACKETPP_SINGLE_COMMAND_TEXT_PROTOCOL_LAYER #define PACKETPP_SINGLE_COMMAND_TEXT_PROTOCOL_LAYER -#include #include "Layer.h" +#include /// @file @@ -10,47 +10,49 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - - /** - * Class for single command text based protocol (FTP, SMTP) messages - */ - class SingleCommandTextProtocol : public Layer - { - private: - size_t getArgumentFieldOffset() const; - void setDelimiter(bool hyphen); - bool hyphenRequired(const std::string& value); - - protected: - SingleCommandTextProtocol(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) : Layer(data, dataLen, prevLayer, packet) {}; - SingleCommandTextProtocol(const std::string &command, const std::string &option); - - bool setCommandInternal(std::string value); - bool setCommandOptionInternal(std::string value); - - std::string getCommandInternal() const; - std::string getCommandOptionInternal() const; - - public: - - /** - * Checks if the current message is a multi-line reply. Multi-line messages are indicated with a Hyphen (-) immediately after reply code. - * @return true If this is a multi-line reply - * @return false Otherwise - */ - bool isMultiLine() const; - - /** - * A static method that takes a byte array and detects whether it is a single command text based message. - * All single command text based message terminated with single "\r\n". - * @param[in] data A byte array - * @param[in] dataSize The byte array size (in bytes) - * @return True if the data is identified as single command text based message - */ - static bool isDataValid(const uint8_t *data, size_t dataSize); - }; +namespace pcpp { + +/** + * Class for single command text based protocol (FTP, SMTP) messages + */ +class SingleCommandTextProtocol : public Layer { + private: + size_t getArgumentFieldOffset() const; + void setDelimiter(bool hyphen); + bool hyphenRequired(const std::string& value); + + protected: + SingleCommandTextProtocol(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet) + : Layer(data, dataLen, prevLayer, packet){}; + SingleCommandTextProtocol(const std::string& command, + const std::string& option); + + bool setCommandInternal(std::string value); + bool setCommandOptionInternal(std::string value); + + std::string getCommandInternal() const; + std::string getCommandOptionInternal() const; + + public: + /** + * Checks if the current message is a multi-line reply. Multi-line messages + * are indicated with a Hyphen (-) immediately after reply code. + * @return true If this is a multi-line reply + * @return false Otherwise + */ + bool isMultiLine() const; + + /** + * A static method that takes a byte array and detects whether it is a single + * command text based message. All single command text based message + * terminated with single "\r\n". + * @param[in] data A byte array + * @param[in] dataSize The byte array size (in bytes) + * @return True if the data is identified as single command text based message + */ + static bool isDataValid(const uint8_t* data, size_t dataSize); +}; } // namespace pcpp #endif /* PACKETPP_SINGLE_COMMAND_TEXT_PROTOCOL_LAYER */ diff --git a/Packet++/header/SipLayer.h b/Packet++/header/SipLayer.h index 14ce1d8f72..f854980da0 100644 --- a/Packet++/header/SipLayer.h +++ b/Packet++/header/SipLayer.h @@ -9,671 +9,793 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ +namespace pcpp { // some popular SIP header fields /** From field */ -#define PCPP_SIP_FROM_FIELD "From" +#define PCPP_SIP_FROM_FIELD "From" /** To field */ -#define PCPP_SIP_TO_FIELD "To" +#define PCPP_SIP_TO_FIELD "To" /** Via field */ -#define PCPP_SIP_VIA_FIELD "Via" +#define PCPP_SIP_VIA_FIELD "Via" /** Call-ID field */ -#define PCPP_SIP_CALL_ID_FIELD "Call-ID" +#define PCPP_SIP_CALL_ID_FIELD "Call-ID" /** Content-Type field */ -#define PCPP_SIP_CONTENT_TYPE_FIELD "Content-Type" +#define PCPP_SIP_CONTENT_TYPE_FIELD "Content-Type" /** Content-Length field */ -#define PCPP_SIP_CONTENT_LENGTH_FIELD "Content-Length" +#define PCPP_SIP_CONTENT_LENGTH_FIELD "Content-Length" /** Content-Disposition field */ #define PCPP_SIP_CONTENT_DISPOSITION_FIELD "Content-Disposition" /** Content-Encoding field */ -#define PCPP_SIP_CONTENT_ENCODING_FIELD "Content-Encoding" +#define PCPP_SIP_CONTENT_ENCODING_FIELD "Content-Encoding" /** Content-Language field */ -#define PCPP_SIP_CONTENT_LANGUAGE_FIELD "Content-Language" +#define PCPP_SIP_CONTENT_LANGUAGE_FIELD "Content-Language" /** CSeq field */ -#define PCPP_SIP_CSEQ_FIELD "CSeq" +#define PCPP_SIP_CSEQ_FIELD "CSeq" /** Contact field */ -#define PCPP_SIP_CONTACT_FIELD "Contact" +#define PCPP_SIP_CONTACT_FIELD "Contact" /** Max-Forwards field */ -#define PCPP_SIP_MAX_FORWARDS_FIELD "Max-Forwards" +#define PCPP_SIP_MAX_FORWARDS_FIELD "Max-Forwards" /** User-Agent field */ -#define PCPP_SIP_USER_AGENT_FIELD "User-Agent" +#define PCPP_SIP_USER_AGENT_FIELD "User-Agent" /** Accept field */ -#define PCPP_SIP_ACCEPT_FIELD "Accept" +#define PCPP_SIP_ACCEPT_FIELD "Accept" /** Accept-Encoding field */ -#define PCPP_SIP_ACCEPT_ENCODING_FIELD "Accept-Encoding" +#define PCPP_SIP_ACCEPT_ENCODING_FIELD "Accept-Encoding" /** Accept-Language field */ -#define PCPP_SIP_ACCEPT_LANGUAGE_FIELD "Accept-Language" +#define PCPP_SIP_ACCEPT_LANGUAGE_FIELD "Accept-Language" /** Allow field */ -#define PCPP_SIP_ALLOW_FIELD "Allow" +#define PCPP_SIP_ALLOW_FIELD "Allow" /** Authorization field */ -#define PCPP_SIP_AUTHORIZATION_FIELD "Authorization" +#define PCPP_SIP_AUTHORIZATION_FIELD "Authorization" /** Date field */ -#define PCPP_SIP_DATE_FIELD "Date" +#define PCPP_SIP_DATE_FIELD "Date" /** MIME-Version field */ -#define PCPP_SIP_MIME_VERSION_FIELD "MIME-Version" +#define PCPP_SIP_MIME_VERSION_FIELD "MIME-Version" /** Reason field */ -#define PCPP_SIP_REASON_FIELD "Reason" +#define PCPP_SIP_REASON_FIELD "Reason" /** Supported field */ -#define PCPP_SIP_SUPPORTED_FIELD "Supported" +#define PCPP_SIP_SUPPORTED_FIELD "Supported" /** Server field */ -#define PCPP_SIP_SERVER_FIELD "Server" +#define PCPP_SIP_SERVER_FIELD "Server" /** WWW-Authenticate fild */ -#define PCPP_SIP_WWW_AUTHENTICATE_FIELD "WWW-Authenticate" +#define PCPP_SIP_WWW_AUTHENTICATE_FIELD "WWW-Authenticate" /** Retry-After field */ -#define PCPP_SIP_RETRY_AFTER_FIELD "Retry-After" +#define PCPP_SIP_RETRY_AFTER_FIELD "Retry-After" /** Record-Route field */ -#define PCPP_SIP_RECORD_ROUTE_FIELD "Record-Route" - - - /** - * @class SipLayer - * Represents a general SIP message. It's an abstract class and cannot be instantiated. It's inherited by SipRequestLayer and SipResponseLayer - */ - class SipLayer : public TextBasedProtocolMessage - { - public: - - /** - * The length of the body of many SIP response messages is determined by a SIP header field called "Content-Length". This method - * parses this field, extracts its value and return it. If this field doesn't exist 0 is returned - * @return SIP response body length determined by "Content-Length" field - */ - int getContentLength() const; - - /** - * The length of the body of many SIP messages is determined by a header field called "Content-Length". This method sets - * The content-length field value. The method supports several cases: - * - If the "Content-Length" field exists - the method will only replace the existing value with the new value - * - If the "Content-Length" field doesn't exist - the method will create this field and put the value in it. Here are also 2 cases: - * - If prevFieldName is specified - the new "Content-Length" field will be created after it - * - If prevFieldName isn't specified or doesn't exist - the new "Content-Length" field will be created as the last field before - * end-of-header field - * - * @param[in] contentLength The content length value to set - * @param[in] prevFieldName Optional parameter, if specified and "Content-Length" field doesn't exist, it will be created after this field - * @return A pointer to the "Content-Length" field, or NULL if creation failed - */ - HeaderField* setContentLength(int contentLength, const std::string &prevFieldName = ""); - - // Overridden methods - - OsiModelLayer getOsiModelLayer() const { return OsiModelSesionLayer; } - - /** - * Currently identifies only SDP if content-length field exists and set to a value greater than zero. - * If content-length field doesn't exist or set to zero and still there is data after this layer, a PayloadLayer will be created - */ - void parseNextLayer(); - - /** - * Set the content-length only if a content-length field already exists and if its current value is different than the total length of the next layer(s) - */ - void computeCalculateFields(); - - /** - * A static method that checks whether the port is considered as SIP - * @param[in] port The port number to be checked - */ - static bool isSipPort(uint16_t port) { return port == 5060 || port == 5061; } - - protected: - SipLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : TextBasedProtocolMessage(data, dataLen, prevLayer, packet) {} - SipLayer() : TextBasedProtocolMessage() {} - SipLayer(const SipLayer& other) : TextBasedProtocolMessage(other) {} - SipLayer& operator=(const SipLayer& other) { TextBasedProtocolMessage::operator=(other); return *this; } - - // implementation of abstract methods - char getHeaderFieldNameValueSeparator() const { return ':'; } - bool spacesAllowedBetweenHeaderFieldNameAndValue() const { return true; } - }; - - - class SipRequestFirstLine; - - - /** - * @class SipRequestLayer - * Represents a SIP request header and inherits all basic functionality of SipLayer and TextBasedProtocolMessage. - * The functionality that is added for this class is the SIP first line concept. A SIP request has the following first line: - * INVITE sip:bla@bla.com:12345 SIP/2.0 - * Since it's not an "ordinary" header field, it requires a special treatment and gets a class of it's own: SipRequestFirstLine. - * In most cases a SIP request will be contained in a single packet but for cases it is not, only the first packet will be identified as SIP - * request layer. You can find out whether the header is complete by using SipLayer#isHeaderComplete() - */ - class SipRequestLayer : public SipLayer - { - friend class SipRequestFirstLine; - - public: - /** - * SIP request methods - */ - enum SipMethod - { - /** INVITE */ - SipINVITE, - /** ACK */ - SipACK, - /** BYE */ - SipBYE, - /** CANCEL */ - SipCANCEL, - /** REFISTER */ - SipREGISTER, - /** PRACK */ - SipPRACK, - /** OPTIONS */ - SipOPTIONS, - /** SUBSCRIBE */ - SipSUBSCRIBE, - /** NOTIFY */ - SipNOTIFY, - /** PUBLISH */ - SipPUBLISH, - /** INFO */ - SipINFO, - /** REFER */ - SipREFER, - /** MESSAGE */ - SipMESSAGE, - /** UPDATE */ - SipUPDATE, - /** Unknown SIP method */ - SipMethodUnknown - }; - - /** 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 - */ - SipRequestLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); - - /** - * A constructor that allocates a new SIP request with only the first line filled. The request will be created without further fields. - * The user can then add fields using addField() or insertField() methods - * @param[in] method The SIP method to be used in this SIP request - * @param[in] requestUri The URI of the request - * @param[in] version SIP version to be used in this request. Default is "SIP/2.0" - */ - SipRequestLayer(SipMethod method, const std::string& requestUri, const std::string& version = "SIP/2.0"); - - ~SipRequestLayer(); - - /** - * A copy constructor for this layer. Inherits base copy constructor SipLayer and adds the functionality - * of copying the first line - * @param[in] other The instance to copy from - */ - SipRequestLayer(const SipRequestLayer& other); - - /** - * An assignment operator overload for this layer. This method inherits base assignment operator SipLayer#operator=() and adds the functionality - * of copying the first line - * @param[in] other The instance to copy from - */ - SipRequestLayer& operator=(const SipRequestLayer& other); - - /** - * @return A pointer to the first line instance for this message - */ - SipRequestFirstLine* getFirstLine() const { return m_FirstLine; } - - // implement Layer's abstract methods - - std::string toString() const; - - private: - SipRequestFirstLine* m_FirstLine; - }; - - - - - class SipResponseFirstLine; - - - /** - * @class SipResponseLayer - * Represents an SIP response message and inherits all basic functionality of SipLayer and TextBasedProtocolMessage. - * The functionality that is added for this class is the SIP first line concept. A SIP response has the following first line: - * 200 OK SIP/2.0 - * Since it's not an "ordinary" header field, it requires a special treatment and gets a class of it's own: SipResponseFirstLine. - * In most cases a SIP response will be contained in a single packet but for cases it is not, only the first packet will be identified as SIP - * response layer. You can find out whether the header is complete by using SipLayer#isHeaderComplete() - */ - class SipResponseLayer : public SipLayer - { - friend class SipResponseFirstLine; - public: - - /** - * Enum for SIP response status codes. List is taken from Wikipedia: https://en.wikipedia.org/wiki/List_of_SIP_response_codes - */ - enum SipResponseStatusCode - { - /** Extended search being performed may take a significant time so a forking proxy must send a 100 Trying response */ - Sip100Trying, - /** Destination user agent received INVITE, and is alerting user of call */ - Sip180Ringing, - /** Servers can optionally send this response to indicate a call is being forwarded */ - Sip181CallisBeingForwarded, - /** Indicates that the destination was temporarily unavailable, so the server has queued the call until the destination is available. A server may send multiple 182 responses to update progress of the queue */ - Sip182Queued, - /** This response may be used to send extra information for a call which is still being set up */ - Sip183SessioninProgress, - /** Can be used by User Agent Server to indicate to upstream SIP entities (including the User Agent Client (UAC)) that an early dialog has been terminated */ - Sip199EarlyDialogTerminated, - /** Indicates the request was successful */ - Sip200OK, - /** Indicates that the request has been accepted for processing, but the processing has not been completed */ - Sip202Accepted, - /** Indicates the request was successful, but the corresponding response will not be received */ - Sip204NoNotification, - /** The address resolved to one of several options for the user or client to choose between, which are listed in the message body or the message's Contact fields */ - Sip300MultipleChoices, - /** The original Request-URI is no longer valid, the new address is given in the Contact header field, and the client should update any records of the original Request-URI with the new value */ - Sip301MovedPermanently, - /** The client should try at the address in the Contact field. If an Expires field is present, the client may cache the result for that period of time */ - Sip302MovedTemporarily, - /** The Contact field details a proxy that must be used to access the requested destination */ - Sip305UseProxy, - /** The call failed, but alternatives are detailed in the message body */ - Sip380AlternativeService, - /** The request could not be understood due to malformed syntax */ - Sip400BadRequest, - /** The request requires user authentication. This response is issued by UASs and registrars */ - Sip401Unauthorized, - /** Reserved for future use */ - Sip402PaymentRequired, - /** The server understood the request, but is refusing to fulfill it */ - Sip403Forbidden, - /** The server has definitive information that the user does not exist at the domain specified in the Request-URI. This status is also returned if the domain in the Request-URI does not match any of the domains handled by the recipient of the request */ - Sip404NotFound, - /** The method specified in the Request-Line is understood, but not allowed for the address identified by the Request-URI */ - Sip405MethodNotAllowed, - /** The resource identified by the request is only capable of generating response entities that have content characteristics but not acceptable according to the Accept header field sent in the request */ - Sip406NotAcceptable, - /** The request requires user authentication. This response is issued by proxys */ - Sip407ProxyAuthenticationRequired, - /** Couldn't find the user in time. The server could not produce a response within a suitable amount of time, for example, if it could not determine the location of the user in time. The client MAY repeat the request without modifications at any later time */ - Sip408RequestTimeout, - /** User already registered */ - Sip409Conflict, - /** The user existed once, but is not available here any more */ - Sip410Gone, - /** The server will not accept the request without a valid Content-Length */ - Sip411LengthRequired, - /** The given precondition has not been met */ - Sip412ConditionalRequestFailed, - /** Request body too large */ - Sip413RequestEntityTooLarge, - /** The server is refusing to service the request because the Request-URI is longer than the server is willing to interpret */ - Sip414RequestURITooLong, - /** Request body in a format not supported */ - Sip415UnsupportedMediaType, - /** Request-URI is unknown to the server */ - Sip416UnsupportedURIScheme, - /** There was a resource-priority option tag, but no Resource-Priority header */ - Sip417UnknownResourcePriority, - /** Bad SIP Protocol Extension used, not understood by the server */ - Sip420BadExtension, - /** The server needs a specific extension not listed in the Supported header */ - Sip421ExtensionRequired, - /** The received request contains a Session-Expires header field with a duration below the minimum timer */ - Sip422SessionIntervalTooSmall, - /** Expiration time of the resource is too short */ - Sip423IntervalTooBrief, - /** The request's location content was malformed or otherwise unsatisfactory */ - Sip424BadLocationInformation, - /** The server rejected a non-interactive emergency call, indicating that the request was malformed enough that no reasonable emergency response to the alert can be determined */ - Sip425BadAlertMessage, - /** The server policy requires an Identity header, and one has not been provided */ - Sip428UseIdentityHeader, - /** The server did not receive a valid Referred-By token on the request */ - Sip429ProvideReferrerIdentity, - /** A specific flow to a user agent has failed, although other flows may succeed. This response is intended for use between proxy devices, and should not be seen by an endpoint (and if it is seen by one, should be treated as a 400 Bad Request response) */ - Sip430FlowFailed, - /** The request has been rejected because it was anonymous */ - Sip433AnonymityDisallowed, - /** The request has an Identity-Info header, and the URI scheme in that header cannot be dereferenced */ - Sip436BadIdentityInfo, - /** The server was unable to validate a certificate for the domain that signed the request */ - Sip437UnsupportedCertificate, - /** The server obtained a valid certificate that the request claimed was used to sign the request, but was unable to verify that signature */ - Sip438InvalidIdentityHeader, - /** The first outbound proxy the user is attempting to register through does not support the "outbound" feature of RFC 5626, although the registrar does */ - Sip439FirstHopLacksOutboundSupport, - /** If a SIP proxy determines a response context has insufficient Incoming Max-Breadth to carry out a desired parallel fork, and the proxy is unwilling/unable to compensate by forking serially or sending a redirect, that proxy MUST return a 440 response. A client receiving a 440 response can infer that its request did not reach all possible destinations */ - Sip440MaxBreadthExceeded, - /** If a SIP UA receives an INFO request associated with an Info Package that the UA has not indicated willingness to receive, the UA MUST send a 469 response, which contains a Recv-Info header field with Info Packages for which the UA is willing to receive INFO requests */ - Sip469BadInfoPackage, - /** The source of the request did not have the permission of the recipient to make such a request */ - Sip470ConsentNeeded, - /** Callee currently unavailable */ - Sip480TemporarilyUnavailable, - /** Server received a request that does not match any dialog or transaction */ - Sip481Call_TransactionDoesNotExist, - /** Server has detected a loop */ - Sip482LoopDetected, - /** Max-Forwards header has reached the value '0' */ - Sip483TooManyHops, - /** Request-URI incomplete */ - Sip484AddressIncomplete, - /** Request-URI is ambiguous */ - Sip485Ambiguous, - /** Callee is busy */ - Sip486BusyHere, - /** Request has terminated by bye or cancel */ - Sip487RequestTerminated, - /** Some aspect of the session description or the Request-URI is not acceptable */ - Sip488NotAcceptableHere, - /** The server did not understand an event package specified in an Event header field */ - Sip489BadEvent, - /** Server has some pending request from the same dialog */ - Sip491RequestPending, - /** Request contains an encrypted MIME body, which recipient can not decrypt */ - Sip493Undecipherable, - /** The server has received a request that requires a negotiated security mechanism, and the response contains a list of suitable security mechanisms for the requester to choose between, or a digest authentication challenge */ - Sip494SecurityAgreementRequired, - /** The server could not fulfill the request due to some unexpected condition */ - Sip500ServerInternalError, - /** The server does not have the ability to fulfill the request, such as because it does not recognize the request method. (Compare with 405 Method Not Allowed, where the server recognizes the method but does not allow or support it.) */ - Sip501NotImplemented, - /** The server is acting as a gateway or proxy, and received an invalid response from a downstream server while attempting to fulfill the request */ - Sip502BadGateway, - /** The server is undergoing maintenance or is temporarily overloaded and so cannot process the request. A "Retry-After" header field may specify when the client may reattempt its request */ - Sip503ServiceUnavailable, - /** The server attempted to access another server in attempting to process the request, and did not receive a prompt response */ - Sip504ServerTimeout, - /** The SIP protocol version in the request is not supported by the server */ - Sip505VersionNotSupported, - /** The request message length is longer than the server can process */ - Sip513MessageTooLarge, - /** The server does not support the push notification service identified in a 'pn-provider' SIP URI parameter */ - Sip555PushNotificationServiceNotSupported, - /** The server is unable or unwilling to meet some constraints specified in the offer */ - Sip580PreconditionFailure, - /** All possible destinations are busy. Unlike the 486 response, this response indicates the destination knows there are no alternative destinations (such as a voicemail server) able to accept the call */ - Sip600BusyEverywhere, - /** The destination does not wish to participate in the call, or cannot do so, and additionally the destination knows there are no alternative destinations (such as a voicemail server) willing to accept the call */ - Sip603Decline, - /** The server has authoritative information that the requested user does not exist anywhere */ - Sip604DoesNotExistAnywhere, - /** The user's agent was contacted successfully but some aspects of the session description such as the requested media, bandwidth, or addressing style were not acceptable */ - Sip606NotAcceptable, - /** The called party did not want this call from the calling party. Future attempts from the calling party are likely to be similarly rejected */ - Sip607Unwanted, - /** An intermediary machine or process rejected the call attempt */ - Sip608Rejected, - /** Unknown SIP status code */ - SipStatusCodeUnknown - }; - - /** 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 - */ - SipResponseLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); - - /** - * A constructor that allocates a new SIP response with only the first line filled. The request will be created without further fields. - * The user can then add fields using addField() or insertField() methods - * @param[in] statusCode SIP status code to set - * @param[in] statusCodeString Most status codes have their default string, e.g 200 is usually "OK" etc. - * But the user can set a non-default status code string and it will be written in the header first line. Empty string ("") means using the - * default status code string. Also, the default is using the default status code string - * @param[in] sipVersion SIP version to set, default is SIP/2.0 - * - */ - explicit SipResponseLayer(SipResponseLayer::SipResponseStatusCode statusCode, std::string statusCodeString = "", const std::string& sipVersion = "SIP/2.0"); - - virtual ~SipResponseLayer(); - - /** - * A copy constructor for this layer. This copy constructor inherits base copy constructor SipLayer and adds the functionality - * of copying the first line as well - * @param[in] other The instance to copy from - */ - SipResponseLayer(const SipResponseLayer& other); - - /** - * An assignment operator overload for this layer. This method inherits base assignment operator SipLayer#operator=() and adds the functionality - * of copying the first line as well - * @param[in] other The instance to copy from - */ - SipResponseLayer& operator=(const SipResponseLayer& other); - - /** - * @return A pointer to the first line instance for this message - */ - SipResponseFirstLine* getFirstLine() const { return m_FirstLine; } - - // implement Layer's abstract methods - - std::string toString() const; - - private: - SipResponseFirstLine* m_FirstLine; - }; - - - - /** - * @class SipRequestFirstLine - * Represents an SIP request first line. The first line includes 3 parameters: SIP method (e.g INVITE, ACK, BYE, etc.), - * URI (e.g sip:bla@bla.com:12345) and SIP version (usually SIP/2.0). All these parameters are included in this class, and the user - * can retrieve or set them. - * This class cannot be instantiated by users, it's created inside SipRequestLayer and user can get a pointer to an instance of it. All "getters" - * of this class retrieve the actual data of the SIP request and the "setters" actually change the packet data. - * Since SIP is a textual protocol, most fields aren't of fixed size and this also applies to the first line parameters. So many "setter" methods - * of this class may need to shorten or extend the data in SipRequestLayer. These methods will return a false value if this action failed - */ - class SipRequestFirstLine - { - friend class SipRequestLayer; - public: - - /** - * @return The SIP request method - */ - SipRequestLayer::SipMethod getMethod() const { return m_Method; } - - /** - * Set the SIP request method - * @param[in] newMethod The method to set - * @return False if newMethod is SipRequestLayer#SipMethodUnknown or if shortening/extending the SipRequestLayer data failed. True otherwise - */ - bool setMethod(SipRequestLayer::SipMethod newMethod); - - /** - * @return A copied version of the URI (notice changing the return value won't change the actual data of the packet) - */ - std::string getUri() const; - - /** - * Set the URI - * @param[in] newUri The URI to set - * @return False if shortening/extending the SipRequestLayer data failed. True otherwise - */ - bool setUri(const std::string& newUri); - - /** - * @return The SIP version - */ - std::string getVersion() const { return m_Version; } - - /** - * A static method for parsing the SIP method out of raw data - * @param[in] data The raw data - * @param[in] dataLen The raw data length - * @return The parsed SIP method - */ - static SipRequestLayer::SipMethod parseMethod(const char* data, size_t dataLen); - - /** - * @return The size in bytes of the SIP request first line - */ - int getSize() const { return m_FirstLineEndOffset; } - - /** - * As explained in SipRequestLayer, a SIP message can sometimes spread over more than 1 packet, so when looking at a single packet - * the header can be partial. Same goes for the first line - it can spread over more than 1 packet. This method returns an indication - * whether the first line is partial - * @return False if the first line is partial, true if it's complete - */ - bool isComplete() const { return m_IsComplete; } - - /** - * @class SipRequestFirstLineException - * This exception can be thrown while constructing SipRequestFirstLine (the constructor is private, so the construction happens - * only in SipRequestLayer). This kind of exception is thrown if trying to construct with SIP method of - * SipRequestLayer#SipMethodUnknown or with empty SIP version - */ - class SipRequestFirstLineException : public std::exception - { - public: - ~SipRequestFirstLineException() throw() {} - void setMessage(const std::string &message) { m_Message = message; } - virtual const char* what() const throw() - { - return m_Message.c_str(); - } - private: - std::string m_Message; - }; - - private: - SipRequestFirstLine(SipRequestLayer* sipRequest); - SipRequestFirstLine(SipRequestLayer* sipRequest, SipRequestLayer::SipMethod method, const std::string& version, const std::string& uri); - - void parseVersion(); - - SipRequestLayer* m_SipRequest; - SipRequestLayer::SipMethod m_Method; - std::string m_Version; - int m_VersionOffset; - int m_UriOffset; - int m_FirstLineEndOffset; - bool m_IsComplete; - SipRequestFirstLineException m_Exception; - }; - - - - - /** - * @class SipResponseFirstLine - * Represents an SIP response message first line. The first line includes 2 parameters: status code (e.g 100 Trying ,200 OK, etc.), - * and SIP version (usually SIP/2.0). These 2 parameters are included in this class, and the user can retrieve or set them. - * This class cannot be instantiated by users, it's created inside SipResponseLayer and user can get a pointer to an instance of it. The "getter" - * methods of this class will retrieve the actual data of the SIP response and the "setter" methods will change the packet data. - * Since SIP is a textual protocol, most fields aren't of fixed size and this also applies to the first line parameters. So most "setter" methods - * of this class may need to shorten or extend the data in SipResponseLayer. These methods will return a false value if this action failed - */ - class SipResponseFirstLine - { - friend class SipResponseLayer; - public: - /** - * @return The status code as SipResponseLayer#SipResponseStatusCode enum - */ - SipResponseLayer::SipResponseStatusCode getStatusCode() const { return m_StatusCode; } - - /** - * @return The status code number as integer (e.g 200, 100, etc.) - */ - int getStatusCodeAsInt() const; - - /** - * @return The status code message (e.g "OK", "Trying", etc.) - */ - std::string getStatusCodeString() const; - - /** - * Set the status code - * @param[in] newStatusCode The new status code to set - * @param[in] statusCodeString An optional parameter: set a non-default status code message (e.g "Bla Bla" instead of "Not Found"). If - * this parameter isn't supplied or supplied as empty string (""), the default message for the status code will be set - */ - bool setStatusCode(SipResponseLayer::SipResponseStatusCode newStatusCode, std::string statusCodeString = ""); - - /** - * @return The SIP version - */ - std::string getVersion() const { return m_Version; } - - /** - * Set the SIP version. The version to set is expected to be in the format of SIP/x.y otherwise an error will be written to log - * @param[in] newVersion The SIP version to set - */ - void setVersion(const std::string& newVersion); - - /** - * A static method for parsing the SIP status code out of raw data - * @param[in] data The raw data - * @param[in] dataLen The raw data length - * @return The parsed SIP status code as enum - */ - static SipResponseLayer::SipResponseStatusCode parseStatusCode(const char* data, size_t dataLen); - - /** - * A static method for parsing the SIP version out of raw data - * @param[in] data The raw data - * @param[in] dataLen The raw data length - * @return The parsed SIP version string or an empty string if version cannot be extracted - */ - static std::string parseVersion(const char* data, size_t dataLen); - - /** - * @return The size in bytes of the SIP response first line - */ - int getSize() const { return m_FirstLineEndOffset; } - - /** - * As explained in SipResponseLayer, A SIP message can sometimes spread over more than 1 packet, so when looking at a single packet - * the header can be partial. Same goes for the first line - it can spread over more than 1 packet. This method returns an indication - * whether the first line is partial - * @return False if the first line is partial, true if it's complete - */ - bool isComplete() const { return m_IsComplete; } - - /** - * @class SipResponseFirstLineException - * This exception can be thrown while constructing SipResponseFirstLine (the constructor is private, so the construction happens - * only in SipResponseLayer). This kind of exception will be thrown if trying to construct with SIP status code of - * SipResponseLayer#SipStatusCodeUnknown or with an empty SIP version - */ - class SipResponseFirstLineException : public std::exception - { - public: - ~SipResponseFirstLineException() throw() {} - void setMessage(const std::string &message) { m_Message = message; } - virtual const char* what() const throw() - { - return m_Message.c_str(); - } - private: - std::string m_Message; - }; - - private: - SipResponseFirstLine(SipResponseLayer* sipResponse); - SipResponseFirstLine(SipResponseLayer* sipResponse, const std::string& version, SipResponseLayer::SipResponseStatusCode statusCode, std::string statusCodeString = ""); - - SipResponseLayer* m_SipResponse; - std::string m_Version; - SipResponseLayer::SipResponseStatusCode m_StatusCode; - int m_FirstLineEndOffset; - bool m_IsComplete; - SipResponseFirstLineException m_Exception; - }; - -} +#define PCPP_SIP_RECORD_ROUTE_FIELD "Record-Route" + +/** + * @class SipLayer + * Represents a general SIP message. It's an abstract class and cannot be + * instantiated. It's inherited by SipRequestLayer and SipResponseLayer + */ +class SipLayer : public TextBasedProtocolMessage { + public: + /** + * The length of the body of many SIP response messages is determined by a SIP + * header field called "Content-Length". This method parses this field, + * extracts its value and return it. If this field doesn't exist 0 is returned + * @return SIP response body length determined by "Content-Length" field + */ + int getContentLength() const; + + /** + * The length of the body of many SIP messages is determined by a header field + * called "Content-Length". This method sets The content-length field value. + * The method supports several cases: + * - If the "Content-Length" field exists - the method will only replace the + * existing value with the new value + * - If the "Content-Length" field doesn't exist - the method will create this + * field and put the value in it. Here are also 2 cases: + * - If prevFieldName is specified - the new "Content-Length" field will be + * created after it + * - If prevFieldName isn't specified or doesn't exist - the new + * "Content-Length" field will be created as the last field before + * end-of-header field + * + * @param[in] contentLength The content length value to set + * @param[in] prevFieldName Optional parameter, if specified and + * "Content-Length" field doesn't exist, it will be created after this field + * @return A pointer to the "Content-Length" field, or NULL if creation failed + */ + HeaderField* setContentLength(int contentLength, + const std::string& prevFieldName = ""); + + // Overridden methods + + OsiModelLayer getOsiModelLayer() const { return OsiModelSesionLayer; } + + /** + * Currently identifies only SDP if content-length field exists and set to a + * value greater than zero. If content-length field doesn't exist or set to + * zero and still there is data after this layer, a PayloadLayer will be + * created + */ + void parseNextLayer(); + + /** + * Set the content-length only if a content-length field already exists and if + * its current value is different than the total length of the next layer(s) + */ + void computeCalculateFields(); + + /** + * A static method that checks whether the port is considered as SIP + * @param[in] port The port number to be checked + */ + static bool isSipPort(uint16_t port) { return port == 5060 || port == 5061; } + + protected: + SipLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : TextBasedProtocolMessage(data, dataLen, prevLayer, packet) {} + SipLayer() : TextBasedProtocolMessage() {} + SipLayer(const SipLayer& other) : TextBasedProtocolMessage(other) {} + SipLayer& operator=(const SipLayer& other) { + TextBasedProtocolMessage::operator=(other); + return *this; + } + + // implementation of abstract methods + char getHeaderFieldNameValueSeparator() const { return ':'; } + bool spacesAllowedBetweenHeaderFieldNameAndValue() const { return true; } +}; + +class SipRequestFirstLine; + +/** + * @class SipRequestLayer + * Represents a SIP request header and inherits all basic functionality of + * SipLayer and TextBasedProtocolMessage. The functionality that is added for + * this class is the SIP first line concept. A SIP request has the following + * first line: INVITE sip:bla@bla.com:12345 SIP/2.0 Since it's not an + * "ordinary" header field, it requires a special treatment and gets a class of + * it's own: SipRequestFirstLine. In most cases a SIP request will be contained + * in a single packet but for cases it is not, only the first packet will be + * identified as SIP request layer. You can find out whether the header is + * complete by using SipLayer#isHeaderComplete() + */ +class SipRequestLayer : public SipLayer { + friend class SipRequestFirstLine; + + public: + /** + * SIP request methods + */ + enum SipMethod { + /** INVITE */ + SipINVITE, + /** ACK */ + SipACK, + /** BYE */ + SipBYE, + /** CANCEL */ + SipCANCEL, + /** REFISTER */ + SipREGISTER, + /** PRACK */ + SipPRACK, + /** OPTIONS */ + SipOPTIONS, + /** SUBSCRIBE */ + SipSUBSCRIBE, + /** NOTIFY */ + SipNOTIFY, + /** PUBLISH */ + SipPUBLISH, + /** INFO */ + SipINFO, + /** REFER */ + SipREFER, + /** MESSAGE */ + SipMESSAGE, + /** UPDATE */ + SipUPDATE, + /** Unknown SIP method */ + SipMethodUnknown + }; + + /** 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 + */ + SipRequestLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet); + + /** + * A constructor that allocates a new SIP request with only the first line + * filled. The request will be created without further fields. The user can + * then add fields using addField() or insertField() methods + * @param[in] method The SIP method to be used in this SIP request + * @param[in] requestUri The URI of the request + * @param[in] version SIP version to be used in this request. Default is + * "SIP/2.0" + */ + SipRequestLayer(SipMethod method, const std::string& requestUri, + const std::string& version = "SIP/2.0"); + + ~SipRequestLayer(); + + /** + * A copy constructor for this layer. Inherits base copy constructor SipLayer + * and adds the functionality of copying the first line + * @param[in] other The instance to copy from + */ + SipRequestLayer(const SipRequestLayer& other); + + /** + * An assignment operator overload for this layer. This method inherits base + * assignment operator SipLayer#operator=() and adds the functionality of + * copying the first line + * @param[in] other The instance to copy from + */ + SipRequestLayer& operator=(const SipRequestLayer& other); + + /** + * @return A pointer to the first line instance for this message + */ + SipRequestFirstLine* getFirstLine() const { return m_FirstLine; } + + // implement Layer's abstract methods + + std::string toString() const; + + private: + SipRequestFirstLine* m_FirstLine; +}; + +class SipResponseFirstLine; + +/** + * @class SipResponseLayer + * Represents an SIP response message and inherits all basic functionality of + * SipLayer and TextBasedProtocolMessage. The functionality that is added for + * this class is the SIP first line concept. A SIP response has the following + * first line: 200 OK SIP/2.0 Since it's not an "ordinary" header field, + * it requires a special treatment and gets a class of it's own: + * SipResponseFirstLine. In most cases a SIP response will be contained in a + * single packet but for cases it is not, only the first packet will be + * identified as SIP response layer. You can find out whether the header is + * complete by using SipLayer#isHeaderComplete() + */ +class SipResponseLayer : public SipLayer { + friend class SipResponseFirstLine; + + public: + /** + * Enum for SIP response status codes. List is taken from Wikipedia: + * https://en.wikipedia.org/wiki/List_of_SIP_response_codes + */ + enum SipResponseStatusCode { + /** Extended search being performed may take a significant time so a forking + proxy must send a 100 Trying response */ + Sip100Trying, + /** Destination user agent received INVITE, and is alerting user of call */ + Sip180Ringing, + /** Servers can optionally send this response to indicate a call is being + forwarded */ + Sip181CallisBeingForwarded, + /** Indicates that the destination was temporarily unavailable, so the + server has queued the call until the destination is available. A server + may send multiple 182 responses to update progress of the queue */ + Sip182Queued, + /** This response may be used to send extra information for a call which is + still being set up */ + Sip183SessioninProgress, + /** Can be used by User Agent Server to indicate to upstream SIP entities + (including the User Agent Client (UAC)) that an early dialog has been + terminated */ + Sip199EarlyDialogTerminated, + /** Indicates the request was successful */ + Sip200OK, + /** Indicates that the request has been accepted for processing, but the + processing has not been completed */ + Sip202Accepted, + /** Indicates the request was successful, but the corresponding response + will not be received */ + Sip204NoNotification, + /** The address resolved to one of several options for the user or client to + choose between, which are listed in the message body or the message's + Contact fields */ + Sip300MultipleChoices, + /** The original Request-URI is no longer valid, the new address is given in + the Contact header field, and the client should update any records of the + original Request-URI with the new value */ + Sip301MovedPermanently, + /** The client should try at the address in the Contact field. If an Expires + field is present, the client may cache the result for that period of time + */ + Sip302MovedTemporarily, + /** The Contact field details a proxy that must be used to access the + requested destination */ + Sip305UseProxy, + /** The call failed, but alternatives are detailed in the message body */ + Sip380AlternativeService, + /** The request could not be understood due to malformed syntax */ + Sip400BadRequest, + /** The request requires user authentication. This response is issued by + UASs and registrars */ + Sip401Unauthorized, + /** Reserved for future use */ + Sip402PaymentRequired, + /** The server understood the request, but is refusing to fulfill it */ + Sip403Forbidden, + /** The server has definitive information that the user does not exist at + the domain specified in the Request-URI. This status is also returned if + the domain in the Request-URI does not match any of the domains handled + by the recipient of the request */ + Sip404NotFound, + /** The method specified in the Request-Line is understood, but not allowed + for the address identified by the Request-URI */ + Sip405MethodNotAllowed, + /** The resource identified by the request is only capable of generating + response entities that have content characteristics but not acceptable + according to the Accept header field sent in the request */ + Sip406NotAcceptable, + /** The request requires user authentication. This response is issued by + proxys */ + Sip407ProxyAuthenticationRequired, + /** Couldn't find the user in time. The server could not produce a response + within a suitable amount of time, for example, if it could not determine + the location of the user in time. The client MAY repeat the request + without modifications at any later time */ + Sip408RequestTimeout, + /** User already registered */ + Sip409Conflict, + /** The user existed once, but is not available here any more */ + Sip410Gone, + /** The server will not accept the request without a valid Content-Length */ + Sip411LengthRequired, + /** The given precondition has not been met */ + Sip412ConditionalRequestFailed, + /** Request body too large */ + Sip413RequestEntityTooLarge, + /** The server is refusing to service the request because the Request-URI is + longer than the server is willing to interpret */ + Sip414RequestURITooLong, + /** Request body in a format not supported */ + Sip415UnsupportedMediaType, + /** Request-URI is unknown to the server */ + Sip416UnsupportedURIScheme, + /** There was a resource-priority option tag, but no Resource-Priority + header */ + Sip417UnknownResourcePriority, + /** Bad SIP Protocol Extension used, not understood by the server */ + Sip420BadExtension, + /** The server needs a specific extension not listed in the Supported header + */ + Sip421ExtensionRequired, + /** The received request contains a Session-Expires header field with a + duration below the minimum timer */ + Sip422SessionIntervalTooSmall, + /** Expiration time of the resource is too short */ + Sip423IntervalTooBrief, + /** The request's location content was malformed or otherwise unsatisfactory + */ + Sip424BadLocationInformation, + /** The server rejected a non-interactive emergency call, indicating that + the request was malformed enough that no reasonable emergency response to + the alert can be determined */ + Sip425BadAlertMessage, + /** The server policy requires an Identity header, and one has not been + provided */ + Sip428UseIdentityHeader, + /** The server did not receive a valid Referred-By token on the request */ + Sip429ProvideReferrerIdentity, + /** A specific flow to a user agent has failed, although other flows may + succeed. This response is intended for use between proxy devices, and + should not be seen by an endpoint (and if it is seen by one, should be + treated as a 400 Bad Request response) */ + Sip430FlowFailed, + /** The request has been rejected because it was anonymous */ + Sip433AnonymityDisallowed, + /** The request has an Identity-Info header, and the URI scheme in that + header cannot be dereferenced */ + Sip436BadIdentityInfo, + /** The server was unable to validate a certificate for the domain that + signed the request */ + Sip437UnsupportedCertificate, + /** The server obtained a valid certificate that the request claimed was + used to sign the request, but was unable to verify that signature */ + Sip438InvalidIdentityHeader, + /** The first outbound proxy the user is attempting to register through does + not support the "outbound" feature of RFC 5626, although the registrar + does */ + Sip439FirstHopLacksOutboundSupport, + /** If a SIP proxy determines a response context has insufficient Incoming + Max-Breadth to carry out a desired parallel fork, and the proxy is + unwilling/unable to compensate by forking serially or sending a redirect, + that proxy MUST return a 440 response. A client receiving a 440 response + can infer that its request did not reach all possible destinations */ + Sip440MaxBreadthExceeded, + /** If a SIP UA receives an INFO request associated with an Info Package + that the UA has not indicated willingness to receive, the UA MUST send a + 469 response, which contains a Recv-Info header field with Info Packages + for which the UA is willing to receive INFO requests */ + Sip469BadInfoPackage, + /** The source of the request did not have the permission of the recipient + to make such a request */ + Sip470ConsentNeeded, + /** Callee currently unavailable */ + Sip480TemporarilyUnavailable, + /** Server received a request that does not match any dialog or transaction + */ + Sip481Call_TransactionDoesNotExist, + /** Server has detected a loop */ + Sip482LoopDetected, + /** Max-Forwards header has reached the value '0' */ + Sip483TooManyHops, + /** Request-URI incomplete */ + Sip484AddressIncomplete, + /** Request-URI is ambiguous */ + Sip485Ambiguous, + /** Callee is busy */ + Sip486BusyHere, + /** Request has terminated by bye or cancel */ + Sip487RequestTerminated, + /** Some aspect of the session description or the Request-URI is not + acceptable */ + Sip488NotAcceptableHere, + /** The server did not understand an event package specified in an Event + header field */ + Sip489BadEvent, + /** Server has some pending request from the same dialog */ + Sip491RequestPending, + /** Request contains an encrypted MIME body, which recipient can not decrypt + */ + Sip493Undecipherable, + /** The server has received a request that requires a negotiated security + mechanism, and the response contains a list of suitable security + mechanisms for the requester to choose between, or a digest + authentication challenge */ + Sip494SecurityAgreementRequired, + /** The server could not fulfill the request due to some unexpected + condition */ + Sip500ServerInternalError, + /** The server does not have the ability to fulfill the request, such as + because it does not recognize the request method. (Compare with 405 + Method Not Allowed, where the server recognizes the method but does not + allow or support it.) */ + Sip501NotImplemented, + /** The server is acting as a gateway or proxy, and received an invalid + response from a downstream server while attempting to fulfill the request + */ + Sip502BadGateway, + /** The server is undergoing maintenance or is temporarily overloaded and so + cannot process the request. A "Retry-After" header field may specify when + the client may reattempt its request */ + Sip503ServiceUnavailable, + /** The server attempted to access another server in attempting to process + the request, and did not receive a prompt response */ + Sip504ServerTimeout, + /** The SIP protocol version in the request is not supported by the server + */ + Sip505VersionNotSupported, + /** The request message length is longer than the server can process */ + Sip513MessageTooLarge, + /** The server does not support the push notification service identified in + a 'pn-provider' SIP URI parameter */ + Sip555PushNotificationServiceNotSupported, + /** The server is unable or unwilling to meet some constraints specified in + the offer */ + Sip580PreconditionFailure, + /** All possible destinations are busy. Unlike the 486 response, this + response indicates the destination knows there are no alternative + destinations (such as a voicemail server) able to accept the call */ + Sip600BusyEverywhere, + /** The destination does not wish to participate in the call, or cannot do + so, and additionally the destination knows there are no alternative + destinations (such as a voicemail server) willing to accept the call */ + Sip603Decline, + /** The server has authoritative information that the requested user does + not exist anywhere */ + Sip604DoesNotExistAnywhere, + /** The user's agent was contacted successfully but some aspects of the + session description such as the requested media, bandwidth, or addressing + style were not acceptable */ + Sip606NotAcceptable, + /** The called party did not want this call from the calling party. Future + attempts from the calling party are likely to be similarly rejected */ + Sip607Unwanted, + /** An intermediary machine or process rejected the call attempt */ + Sip608Rejected, + /** Unknown SIP status code */ + SipStatusCodeUnknown + }; + + /** 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 + */ + SipResponseLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet); + + /** + * A constructor that allocates a new SIP response with only the first line + * filled. The request will be created without further fields. The user can + * then add fields using addField() or insertField() methods + * @param[in] statusCode SIP status code to set + * @param[in] statusCodeString Most status codes have their default string, + * e.g 200 is usually "OK" etc. But the user can set a non-default status code + * string and it will be written in the header first line. Empty string ("") + * means using the default status code string. Also, the default is using the + * default status code string + * @param[in] sipVersion SIP version to set, default is SIP/2.0 + * + */ + explicit SipResponseLayer(SipResponseLayer::SipResponseStatusCode statusCode, + std::string statusCodeString = "", + const std::string& sipVersion = "SIP/2.0"); + + virtual ~SipResponseLayer(); + + /** + * A copy constructor for this layer. This copy constructor inherits base copy + * constructor SipLayer and adds the functionality of copying the first line + * as well + * @param[in] other The instance to copy from + */ + SipResponseLayer(const SipResponseLayer& other); + + /** + * An assignment operator overload for this layer. This method inherits base + * assignment operator SipLayer#operator=() and adds the functionality of + * copying the first line as well + * @param[in] other The instance to copy from + */ + SipResponseLayer& operator=(const SipResponseLayer& other); + + /** + * @return A pointer to the first line instance for this message + */ + SipResponseFirstLine* getFirstLine() const { return m_FirstLine; } + + // implement Layer's abstract methods + + std::string toString() const; + + private: + SipResponseFirstLine* m_FirstLine; +}; + +/** + * @class SipRequestFirstLine + * Represents an SIP request first line. The first line includes 3 parameters: + * SIP method (e.g INVITE, ACK, BYE, etc.), URI (e.g sip:bla@bla.com:12345) and + * SIP version (usually SIP/2.0). All these parameters are included in this + * class, and the user can retrieve or set them. This class cannot be + * instantiated by users, it's created inside SipRequestLayer and user can get a + * pointer to an instance of it. All "getters" of this class retrieve the actual + * data of the SIP request and the "setters" actually change the packet data. + * Since SIP is a textual protocol, most fields aren't of fixed size and this + * also applies to the first line parameters. So many "setter" methods of this + * class may need to shorten or extend the data in SipRequestLayer. These + * methods will return a false value if this action failed + */ +class SipRequestFirstLine { + friend class SipRequestLayer; + + public: + /** + * @return The SIP request method + */ + SipRequestLayer::SipMethod getMethod() const { return m_Method; } + + /** + * Set the SIP request method + * @param[in] newMethod The method to set + * @return False if newMethod is SipRequestLayer#SipMethodUnknown or if + * shortening/extending the SipRequestLayer data failed. True otherwise + */ + bool setMethod(SipRequestLayer::SipMethod newMethod); + + /** + * @return A copied version of the URI (notice changing the return value won't + * change the actual data of the packet) + */ + std::string getUri() const; + + /** + * Set the URI + * @param[in] newUri The URI to set + * @return False if shortening/extending the SipRequestLayer data failed. True + * otherwise + */ + bool setUri(const std::string& newUri); + + /** + * @return The SIP version + */ + std::string getVersion() const { return m_Version; } + + /** + * A static method for parsing the SIP method out of raw data + * @param[in] data The raw data + * @param[in] dataLen The raw data length + * @return The parsed SIP method + */ + static SipRequestLayer::SipMethod parseMethod(const char* data, + size_t dataLen); + + /** + * @return The size in bytes of the SIP request first line + */ + int getSize() const { return m_FirstLineEndOffset; } + + /** + * As explained in SipRequestLayer, a SIP message can sometimes spread over + * more than 1 packet, so when looking at a single packet the header can be + * partial. Same goes for the first line - it can spread over more than 1 + * packet. This method returns an indication whether the first line is partial + * @return False if the first line is partial, true if it's complete + */ + bool isComplete() const { return m_IsComplete; } + + /** + * @class SipRequestFirstLineException + * This exception can be thrown while constructing SipRequestFirstLine (the + * constructor is private, so the construction happens only in + * SipRequestLayer). This kind of exception is thrown if trying to construct + * with SIP method of SipRequestLayer#SipMethodUnknown or with empty SIP + * version + */ + class SipRequestFirstLineException : public std::exception { + public: + ~SipRequestFirstLineException() throw() {} + void setMessage(const std::string& message) { m_Message = message; } + virtual const char* what() const throw() { return m_Message.c_str(); } + + private: + std::string m_Message; + }; + + private: + SipRequestFirstLine(SipRequestLayer* sipRequest); + SipRequestFirstLine(SipRequestLayer* sipRequest, + SipRequestLayer::SipMethod method, + const std::string& version, const std::string& uri); + + void parseVersion(); + + SipRequestLayer* m_SipRequest; + SipRequestLayer::SipMethod m_Method; + std::string m_Version; + int m_VersionOffset; + int m_UriOffset; + int m_FirstLineEndOffset; + bool m_IsComplete; + SipRequestFirstLineException m_Exception; +}; + +/** + * @class SipResponseFirstLine + * Represents an SIP response message first line. The first line includes 2 + * parameters: status code (e.g 100 Trying ,200 OK, etc.), and SIP version + * (usually SIP/2.0). These 2 parameters are included in this class, and the + * user can retrieve or set them. This class cannot be instantiated by users, + * it's created inside SipResponseLayer and user can get a pointer to an + * instance of it. The "getter" methods of this class will retrieve the actual + * data of the SIP response and the "setter" methods will change the packet + * data. Since SIP is a textual protocol, most fields aren't of fixed size and + * this also applies to the first line parameters. So most "setter" methods of + * this class may need to shorten or extend the data in SipResponseLayer. These + * methods will return a false value if this action failed + */ +class SipResponseFirstLine { + friend class SipResponseLayer; + + public: + /** + * @return The status code as SipResponseLayer#SipResponseStatusCode enum + */ + SipResponseLayer::SipResponseStatusCode getStatusCode() const { + return m_StatusCode; + } + + /** + * @return The status code number as integer (e.g 200, 100, etc.) + */ + int getStatusCodeAsInt() const; + + /** + * @return The status code message (e.g "OK", "Trying", etc.) + */ + std::string getStatusCodeString() const; + + /** + * Set the status code + * @param[in] newStatusCode The new status code to set + * @param[in] statusCodeString An optional parameter: set a non-default status + * code message (e.g "Bla Bla" instead of "Not Found"). If this parameter + * isn't supplied or supplied as empty string (""), the default message for + * the status code will be set + */ + bool setStatusCode(SipResponseLayer::SipResponseStatusCode newStatusCode, + std::string statusCodeString = ""); + + /** + * @return The SIP version + */ + std::string getVersion() const { return m_Version; } + + /** + * Set the SIP version. The version to set is expected to be in the format of + * SIP/x.y otherwise an error will be written to log + * @param[in] newVersion The SIP version to set + */ + void setVersion(const std::string& newVersion); + + /** + * A static method for parsing the SIP status code out of raw data + * @param[in] data The raw data + * @param[in] dataLen The raw data length + * @return The parsed SIP status code as enum + */ + static SipResponseLayer::SipResponseStatusCode + parseStatusCode(const char* data, size_t dataLen); + + /** + * A static method for parsing the SIP version out of raw data + * @param[in] data The raw data + * @param[in] dataLen The raw data length + * @return The parsed SIP version string or an empty string if version cannot + * be extracted + */ + static std::string parseVersion(const char* data, size_t dataLen); + + /** + * @return The size in bytes of the SIP response first line + */ + int getSize() const { return m_FirstLineEndOffset; } + + /** + * As explained in SipResponseLayer, A SIP message can sometimes spread over + * more than 1 packet, so when looking at a single packet the header can be + * partial. Same goes for the first line - it can spread over more than 1 + * packet. This method returns an indication whether the first line is partial + * @return False if the first line is partial, true if it's complete + */ + bool isComplete() const { return m_IsComplete; } + + /** + * @class SipResponseFirstLineException + * This exception can be thrown while constructing SipResponseFirstLine (the + * constructor is private, so the construction happens only in + * SipResponseLayer). This kind of exception will be thrown if trying to + * construct with SIP status code of SipResponseLayer#SipStatusCodeUnknown or + * with an empty SIP version + */ + class SipResponseFirstLineException : public std::exception { + public: + ~SipResponseFirstLineException() throw() {} + void setMessage(const std::string& message) { m_Message = message; } + virtual const char* what() const throw() { return m_Message.c_str(); } + + private: + std::string m_Message; + }; + + private: + SipResponseFirstLine(SipResponseLayer* sipResponse); + SipResponseFirstLine(SipResponseLayer* sipResponse, + const std::string& version, + SipResponseLayer::SipResponseStatusCode statusCode, + std::string statusCodeString = ""); + + SipResponseLayer* m_SipResponse; + std::string m_Version; + SipResponseLayer::SipResponseStatusCode m_StatusCode; + int m_FirstLineEndOffset; + bool m_IsComplete; + SipResponseFirstLineException m_Exception; +}; + +} // namespace pcpp #endif // PACKETPP_SIP_LAYER diff --git a/Packet++/header/Sll2Layer.h b/Packet++/header/Sll2Layer.h index 4d825f896d..a7044f99bb 100644 --- a/Packet++/header/Sll2Layer.h +++ b/Packet++/header/Sll2Layer.h @@ -1,8 +1,8 @@ #ifndef PACKETPP_SLL2_LAYER #define PACKETPP_SLL2_LAYER -#include "MacAddress.h" #include "Layer.h" +#include "MacAddress.h" /// @file @@ -10,182 +10,194 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - /** - * @struct sll2_header - * Represents SLL2 header - */ +namespace pcpp { +/** + * @struct sll2_header + * Represents SLL2 header + */ #pragma pack(push, 1) - struct sll2_header - { - /** Contains an Ethernet protocol type of the next layer */ - uint16_t protocol_type; - /** The "Reserved (MBZ)" field is reserved, and must be set to zero */ - uint16_t reserved; - /** The interface index field is a signed integer in network byte - * order and contains the 1-based index of the interface on which the packet was observed - **/ - uint32_t interface_index; - /** Contains a Linux ARPHRD_ value for the link-layer device type */ - uint16_t ARPHRD_type; - /** Specifies whether packet was: specifically sent to us by somebody else (value=0); - * broadcast by somebody else (value=1); multicast, but not broadcast, by somebody else (value=2); - * sent to somebody else by somebody else (value=3); sent by us (value=4) - **/ - uint8_t packet_type; - /** Contains the length of the link-layer address of the sender of the packet. That length could be zero */ - uint8_t link_layer_addr_len; - /** Contains the link-layer address of the sender of the packet; the number of bytes of that field that are - * meaningful is specified by the link-layer address length field - **/ - uint8_t link_layer_addr[8]; - }; +struct sll2_header { + /** Contains an Ethernet protocol type of the next layer */ + uint16_t protocol_type; + /** The "Reserved (MBZ)" field is reserved, and must be set to zero */ + uint16_t reserved; + /** The interface index field is a signed integer in network byte + * order and contains the 1-based index of the interface on which the packet + *was observed + **/ + uint32_t interface_index; + /** Contains a Linux ARPHRD_ value for the link-layer device type */ + uint16_t ARPHRD_type; + /** Specifies whether packet was: specifically sent to us by somebody else + *(value=0); broadcast by somebody else (value=1); multicast, but not + *broadcast, by somebody else (value=2); sent to somebody else by somebody + *else (value=3); sent by us (value=4) + **/ + uint8_t packet_type; + /** Contains the length of the link-layer address of the sender of the packet. + * That length could be zero */ + uint8_t link_layer_addr_len; + /** Contains the link-layer address of the sender of the packet; the number of + *bytes of that field that are meaningful is specified by the link-layer + *address length field + **/ + uint8_t link_layer_addr[8]; +}; #pragma pack(pop) - /** - * @class Sll2Layer - * Represents an SLL2 (Linux cooked capture) protocol layer - */ - class Sll2Layer : public Layer - { - public: - /** - * A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data (will be casted to ether_header) - * @param[in] dataLen Size of the data in bytes - * @param[in] packet A pointer to the Packet instance where layer will be stored in - */ - Sll2Layer(uint8_t* data, size_t dataLen, Packet* packet) : Layer(data, dataLen, nullptr, packet) { m_Protocol = SLL2; } - - /** - * A constructor that creates a new SLL2 header and allocates the data - * @param[in] interfaceIndex The interface index - * @param[in] ARPHRDType The ARPHRD type - * @param[in] packetType The packet type - */ - Sll2Layer(uint32_t interfaceIndex, uint16_t ARPHRDType, uint8_t packetType); - - ~Sll2Layer() {} - - /** - * Get a pointer to the Sll header. Notice this points directly to the data, so every change will change the actual packet data - * @return A pointer to the sll2_header - */ - sll2_header* getSll2Header() const { return (sll2_header*)m_Data; } - - /** - * A static method that validates the input data - * @param[in] data The pointer to the beginning of a byte stream of an IEEE 802.3 Eth packet - * @param[in] dataLen The length of the byte stream - * @return True if the data is valid and can represent an IEEE 802.3 Eth packet - */ - static bool isDataValid(const uint8_t* data, size_t dataLen); - - /** - * Get a protocol type of this layer - * @return protocol type - */ - uint16_t getProtocolType() const; - - /** - * Set protocol type of this layer - * @param[in] protocolType type to set - */ - void setProtocolType(uint16_t protocolType); - - /** - * Get interface index of this layer - * @return interface index - */ - uint32_t getInterfaceIndex() const; - - /** - * Set interface index of this layer - * @param[in] interfaceIndex interface index to set - */ - void setInterfaceIndex(uint32_t interfaceIndex); - - /** - * Get arphrd type of this layer - * @return arphrd type - */ - uint16_t getArphrdType() const; - - /** - * Set arphrd type of this layer - * @param[in] arphrdType arphrd type to set - */ - void setArphrdType(uint16_t arphrdType); - - /** - * Get packet type of this layer - * @return packet type - */ - uint8_t getPacketType() const; - - /** - * Set packet type of this layer - * @param[in] packetType packet type to set - */ - void setPacketType(uint8_t packetType); - - /** - * Get link layer address length - * @return link layer address length - */ - uint8_t getLinkLayerAddrLen() const; - - /** - * Get link layer address data pointer - * @return link layer address data pointer - */ - const uint8_t* getLinkLayerAddr() const; - - /** - * A setter for the link layer address field - * @param[in] addr The address to set. Memory will be copied to packet - * @param[in] addrLength Address length, must be lower or equal to 8 (which is max length for SLL2 address) - * @return True if address was set successfully, or false of addrLength is out of bounds (0 or larger than 8) - */ - bool setLinkLayerAddr(const uint8_t* addr, size_t addrLength); - - /** - * Get a MAC address in the link layer address field - * @return return macAddress pointer was set successfully, null pointer if d MAC address isn't valid or if set failed - */ - MacAddress getLinkLayerAsMacAddress(); - - /** - * Set a MAC address in the link layer address field - * @param[in] macAddr MAC address to set - * @return True if address was set successfully, false if MAC address isn't valid or if set failed - */ - bool setMacAddressAsLinkLayer(const MacAddress& macAddr); - - // implement abstract methods - - /** - * Currently identifies the following next layers: IPv4Layer, IPv6Layer, ArpLayer, VlanLayer, PPPoESessionLayer, PPPoEDiscoveryLayer, - * MplsLayer. - * Otherwise sets PayloadLayer - */ - void parseNextLayer(); - - /** - * Calculate the next protocol type for known protocols: IPv4, IPv6, ARP, VLAN - */ - void computeCalculateFields(); - - /** - * @return Size of sll2_header - */ - size_t getHeaderLen() const { return sizeof(sll2_header); } - - std::string toString() const; - - OsiModelLayer getOsiModelLayer() const { return OsiModelDataLinkLayer; } - }; +/** + * @class Sll2Layer + * Represents an SLL2 (Linux cooked capture) protocol layer + */ +class Sll2Layer : public Layer { + public: + /** + * A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data (will be casted to ether_header) + * @param[in] dataLen Size of the data in bytes + * @param[in] packet A pointer to the Packet instance where layer will be + * stored in + */ + Sll2Layer(uint8_t* data, size_t dataLen, Packet* packet) + : Layer(data, dataLen, nullptr, packet) { + m_Protocol = SLL2; + } + + /** + * A constructor that creates a new SLL2 header and allocates the data + * @param[in] interfaceIndex The interface index + * @param[in] ARPHRDType The ARPHRD type + * @param[in] packetType The packet type + */ + Sll2Layer(uint32_t interfaceIndex, uint16_t ARPHRDType, uint8_t packetType); + + ~Sll2Layer() {} + + /** + * Get a pointer to the Sll header. Notice this points directly to the data, + * so every change will change the actual packet data + * @return A pointer to the sll2_header + */ + sll2_header* getSll2Header() const { return (sll2_header*)m_Data; } + + /** + * A static method that validates the input data + * @param[in] data The pointer to the beginning of a byte stream of an IEEE + * 802.3 Eth packet + * @param[in] dataLen The length of the byte stream + * @return True if the data is valid and can represent an IEEE 802.3 Eth + * packet + */ + static bool isDataValid(const uint8_t* data, size_t dataLen); + + /** + * Get a protocol type of this layer + * @return protocol type + */ + uint16_t getProtocolType() const; + + /** + * Set protocol type of this layer + * @param[in] protocolType type to set + */ + void setProtocolType(uint16_t protocolType); + + /** + * Get interface index of this layer + * @return interface index + */ + uint32_t getInterfaceIndex() const; + + /** + * Set interface index of this layer + * @param[in] interfaceIndex interface index to set + */ + void setInterfaceIndex(uint32_t interfaceIndex); + + /** + * Get arphrd type of this layer + * @return arphrd type + */ + uint16_t getArphrdType() const; + + /** + * Set arphrd type of this layer + * @param[in] arphrdType arphrd type to set + */ + void setArphrdType(uint16_t arphrdType); + + /** + * Get packet type of this layer + * @return packet type + */ + uint8_t getPacketType() const; + + /** + * Set packet type of this layer + * @param[in] packetType packet type to set + */ + void setPacketType(uint8_t packetType); + + /** + * Get link layer address length + * @return link layer address length + */ + uint8_t getLinkLayerAddrLen() const; + + /** + * Get link layer address data pointer + * @return link layer address data pointer + */ + const uint8_t* getLinkLayerAddr() const; + + /** + * A setter for the link layer address field + * @param[in] addr The address to set. Memory will be copied to packet + * @param[in] addrLength Address length, must be lower or equal to 8 (which is + * max length for SLL2 address) + * @return True if address was set successfully, or false of addrLength is out + * of bounds (0 or larger than 8) + */ + bool setLinkLayerAddr(const uint8_t* addr, size_t addrLength); + + /** + * Get a MAC address in the link layer address field + * @return return macAddress pointer was set successfully, null pointer if d + * MAC address isn't valid or if set failed + */ + MacAddress getLinkLayerAsMacAddress(); + + /** + * Set a MAC address in the link layer address field + * @param[in] macAddr MAC address to set + * @return True if address was set successfully, false if MAC address isn't + * valid or if set failed + */ + bool setMacAddressAsLinkLayer(const MacAddress& macAddr); + + // implement abstract methods + + /** + * Currently identifies the following next layers: IPv4Layer, IPv6Layer, + * ArpLayer, VlanLayer, PPPoESessionLayer, PPPoEDiscoveryLayer, MplsLayer. + * Otherwise sets PayloadLayer + */ + void parseNextLayer(); + + /** + * Calculate the next protocol type for known protocols: IPv4, IPv6, ARP, VLAN + */ + void computeCalculateFields(); + + /** + * @return Size of sll2_header + */ + size_t getHeaderLen() const { return sizeof(sll2_header); } + + std::string toString() const; + + OsiModelLayer getOsiModelLayer() const { return OsiModelDataLinkLayer; } +}; } // namespace pcpp diff --git a/Packet++/header/SllLayer.h b/Packet++/header/SllLayer.h index b744a04548..bee444272c 100644 --- a/Packet++/header/SllLayer.h +++ b/Packet++/header/SllLayer.h @@ -1,8 +1,8 @@ #ifndef PACKETPP_SLL_LAYER #define PACKETPP_SLL_LAYER -#include "MacAddress.h" #include "Layer.h" +#include "MacAddress.h" /// @file @@ -10,100 +10,108 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ +namespace pcpp { - /** - * @struct sll_header - * Represents SLL header - */ +/** + * @struct sll_header + * Represents SLL header + */ #pragma pack(push, 1) - struct sll_header - { - /** Specifies whether packet was: specifically sent to us by somebody else (value=0); - * broadcast by somebody else (value=1); multicast, but not broadcast, by somebody else (value=2); - * sent to somebody else by somebody else (value=3); sent by us (value=4) - **/ - uint16_t packet_type; - /** Contains a Linux ARPHRD_ value for the link-layer device type */ - uint16_t ARPHRD_type; - /** Contains the length of the link-layer address of the sender of the packet. That length could be zero */ - uint16_t link_layer_addr_len; - /** contains the link-layer address of the sender of the packet; the number of bytes of that field that are - * meaningful is specified by the link-layer address length field - **/ - uint8_t link_layer_addr[8]; - /** Contains an Ethernet protocol type of the next layer */ - uint16_t protocol_type; - }; +struct sll_header { + /** Specifies whether packet was: specifically sent to us by somebody else + *(value=0); broadcast by somebody else (value=1); multicast, but not + *broadcast, by somebody else (value=2); sent to somebody else by somebody + *else (value=3); sent by us (value=4) + **/ + uint16_t packet_type; + /** Contains a Linux ARPHRD_ value for the link-layer device type */ + uint16_t ARPHRD_type; + /** Contains the length of the link-layer address of the sender of the packet. + * That length could be zero */ + uint16_t link_layer_addr_len; + /** contains the link-layer address of the sender of the packet; the number of + *bytes of that field that are meaningful is specified by the link-layer + *address length field + **/ + uint8_t link_layer_addr[8]; + /** Contains an Ethernet protocol type of the next layer */ + uint16_t protocol_type; +}; #pragma pack(pop) - /** - * @class SllLayer - * Represents an SLL (Linux cooked capture) protocol layer - */ - class SllLayer : public Layer - { - public: - /** - * A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data (will be casted to ether_header) - * @param[in] dataLen Size of the data in bytes - * @param[in] packet A pointer to the Packet instance where layer will be stored in - */ - SllLayer(uint8_t* data, size_t dataLen, Packet* packet) : Layer(data, dataLen, nullptr, packet) { m_Protocol = SLL; } - - /** - * A constructor that creates a new SLL header and allocates the data - * @param[in] packetType The packet type - * @param[in] ARPHRDType The ARPHRD type - */ - SllLayer(uint16_t packetType, uint16_t ARPHRDType); - - ~SllLayer() {} - - /** - * Get a pointer to the Sll header. Notice this points directly to the data, so every change will change the actual packet data - * @return A pointer to the sll_header - */ - sll_header* getSllHeader() const { return (sll_header*)m_Data; } - - /** - * A setter for the link layer address field - * @param[in] addr The address to set. Memory will be copied to packet - * @param[in] addrLength Address length, must be lower or equal to 8 (which is max length for SLL address) - * @return True if address was set successfully, or false of addrLength is out of bounds (0 or larger than 8) - */ - bool setLinkLayerAddr(uint8_t* addr, size_t addrLength); - - /** - * Set a MAC address in the link layer address field - * @param[in] macAddr MAC address to set - * @return True if address was set successfully, false if MAC address isn't valid or if set failed - */ - bool setMacAddressAsLinkLayer(const MacAddress& macAddr); - - /** - * Currently identifies the following next layers: IPv4Layer, IPv6Layer, ArpLayer, VlanLayer, PPPoESessionLayer, PPPoEDiscoveryLayer, - * MplsLayer. - * Otherwise sets PayloadLayer - */ - void parseNextLayer(); - - /** - * @return Size of sll_header - */ - size_t getHeaderLen() const { return sizeof(sll_header); } - - /** - * Calculate the next protocol type for known protocols: IPv4, IPv6, ARP, VLAN - */ - void computeCalculateFields(); - - std::string toString() const; - - OsiModelLayer getOsiModelLayer() const { return OsiModelDataLinkLayer; } - }; +/** + * @class SllLayer + * Represents an SLL (Linux cooked capture) protocol layer + */ +class SllLayer : public Layer { + public: + /** + * A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data (will be casted to ether_header) + * @param[in] dataLen Size of the data in bytes + * @param[in] packet A pointer to the Packet instance where layer will be + * stored in + */ + SllLayer(uint8_t* data, size_t dataLen, Packet* packet) + : Layer(data, dataLen, nullptr, packet) { + m_Protocol = SLL; + } + + /** + * A constructor that creates a new SLL header and allocates the data + * @param[in] packetType The packet type + * @param[in] ARPHRDType The ARPHRD type + */ + SllLayer(uint16_t packetType, uint16_t ARPHRDType); + + ~SllLayer() {} + + /** + * Get a pointer to the Sll header. Notice this points directly to the data, + * so every change will change the actual packet data + * @return A pointer to the sll_header + */ + sll_header* getSllHeader() const { return (sll_header*)m_Data; } + + /** + * A setter for the link layer address field + * @param[in] addr The address to set. Memory will be copied to packet + * @param[in] addrLength Address length, must be lower or equal to 8 (which is + * max length for SLL address) + * @return True if address was set successfully, or false of addrLength is out + * of bounds (0 or larger than 8) + */ + bool setLinkLayerAddr(uint8_t* addr, size_t addrLength); + + /** + * Set a MAC address in the link layer address field + * @param[in] macAddr MAC address to set + * @return True if address was set successfully, false if MAC address isn't + * valid or if set failed + */ + bool setMacAddressAsLinkLayer(const MacAddress& macAddr); + + /** + * Currently identifies the following next layers: IPv4Layer, IPv6Layer, + * ArpLayer, VlanLayer, PPPoESessionLayer, PPPoEDiscoveryLayer, MplsLayer. + * Otherwise sets PayloadLayer + */ + void parseNextLayer(); + + /** + * @return Size of sll_header + */ + size_t getHeaderLen() const { return sizeof(sll_header); } + + /** + * Calculate the next protocol type for known protocols: IPv4, IPv6, ARP, VLAN + */ + void computeCalculateFields(); + + std::string toString() const; + + OsiModelLayer getOsiModelLayer() const { return OsiModelDataLinkLayer; } +}; } // namespace pcpp diff --git a/Packet++/header/SomeIpLayer.h b/Packet++/header/SomeIpLayer.h index 2a9b94a554..d262f3800d 100644 --- a/Packet++/header/SomeIpLayer.h +++ b/Packet++/header/SomeIpLayer.h @@ -10,451 +10,469 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ +namespace pcpp { /** * @class SomeIpLayer * Represents a SOME/IP protocol layer */ -class SomeIpLayer : public Layer -{ -public: - /** - * SOME/IP message types - */ - enum class MsgType : uint8_t - { - /** A request expecting a response (even void) */ - REQUEST = 0x00, - /** Acknowledgment for REQUEST(optional) */ - REQUEST_ACK = 0x40, - /** A fire&forget request */ - REQUEST_NO_RETURN = 0x01, - /** Acknowledgment for REQUEST_NO_RETURN(informational) */ - REQUEST_NO_RETURN_ACK = 0x41, - /** A request of a notification expecting no response */ - NOTIFICATION = 0x02, - /** Acknowledgment for NOTIFICATION(informational) */ - NOTIFICATION_ACK = 0x42, - /** The response message */ - RESPONSE = 0x80, - /** The Acknowledgment for RESPONSE(informational) */ - RESPONSE_ACK = 0xC0, - /** The response containing an error */ - ERRORS = 0x81, - /** Acknowledgment for ERROR(informational) */ - ERROR_ACK = 0xC1, - /** A TP request expecting a response (even void) */ - TP_REQUEST = 0x20, - /** A TP fire&forget request */ - TP_REQUEST_NO_RETURN = 0x21, - /** A TP request of a notification/event callback expecting no response */ - TP_NOTIFICATION = 0x22, - /** The TP response message */ - TP_RESPONSE = 0xa0, - /** The TP response containing an error */ - TP_ERROR = 0xa1, - }; - - /** - * @struct someiphdr - * Represents a SOME/IP protocol header - */ +class SomeIpLayer : public Layer { + public: + /** + * SOME/IP message types + */ + enum class MsgType : uint8_t { + /** A request expecting a response (even void) */ + REQUEST = 0x00, + /** Acknowledgment for REQUEST(optional) */ + REQUEST_ACK = 0x40, + /** A fire&forget request */ + REQUEST_NO_RETURN = 0x01, + /** Acknowledgment for REQUEST_NO_RETURN(informational) */ + REQUEST_NO_RETURN_ACK = 0x41, + /** A request of a notification expecting no response */ + NOTIFICATION = 0x02, + /** Acknowledgment for NOTIFICATION(informational) */ + NOTIFICATION_ACK = 0x42, + /** The response message */ + RESPONSE = 0x80, + /** The Acknowledgment for RESPONSE(informational) */ + RESPONSE_ACK = 0xC0, + /** The response containing an error */ + ERRORS = 0x81, + /** Acknowledgment for ERROR(informational) */ + ERROR_ACK = 0xC1, + /** A TP request expecting a response (even void) */ + TP_REQUEST = 0x20, + /** A TP fire&forget request */ + TP_REQUEST_NO_RETURN = 0x21, + /** A TP request of a notification/event callback expecting no response */ + TP_NOTIFICATION = 0x22, + /** The TP response message */ + TP_RESPONSE = 0xa0, + /** The TP response containing an error */ + TP_ERROR = 0xa1, + }; + + /** + * @struct someiphdr + * Represents a SOME/IP protocol header + */ #pragma pack(push, 1) - struct someiphdr - { - /** Service ID */ - uint16_t serviceID; - /** Method ID. Most significant bit 0 when E2E communication. 1 when SOME/IP event */ - uint16_t methodID; - /** Length. Also covers payload. Excludes serviceID, methodID and length field itself */ - uint32_t length; - /** Client ID */ - uint16_t clientID; - /** Session ID */ - uint16_t sessionID; - /** Protocol Version */ - uint8_t protocolVersion; - /** Interface Version */ - uint8_t interfaceVersion; - /** Message Type */ - uint8_t msgType; - /** Return Code */ - uint8_t returnCode; - }; + struct someiphdr { + /** Service ID */ + uint16_t serviceID; + /** Method ID. Most significant bit 0 when E2E communication. 1 when SOME/IP + * event */ + uint16_t methodID; + /** Length. Also covers payload. Excludes serviceID, methodID and length + * field itself */ + uint32_t length; + /** Client ID */ + uint16_t clientID; + /** Session ID */ + uint16_t sessionID; + /** Protocol Version */ + uint8_t protocolVersion; + /** Interface Version */ + uint8_t interfaceVersion; + /** Message Type */ + uint8_t msgType; + /** Return Code */ + uint8_t returnCode; + }; #pragma pack(pop) - /** - * A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data (will be casted to someiphdr) - * @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 - */ - SomeIpLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) - : Layer(data, dataLen, prevLayer, packet) - { - m_Protocol = SomeIP; - } - - /** - * Construct a new layer object - * @param[in] serviceID Service ID - * @param[in] methodID Method ID - * @param[in] clientID Client ID - * @param[in] sessionID Session ID - * @param[in] interfaceVersion Interface Version - * @param[in] type Type of the message - * @param[in] returnCode Return Code - * @param[in] data A pointer to the raw data - * @param[in] dataLen Size of the data in bytes - * holds the reference to a data buffer. This option can be used to reduce the number of copies to generate packets. - */ - SomeIpLayer(uint16_t serviceID, uint16_t methodID, uint16_t clientID, uint16_t sessionID, uint8_t interfaceVersion, - MsgType type, uint8_t returnCode, const uint8_t *const data = nullptr, size_t dataLen = 0); - - /** - * Destroy the layer object - */ - ~SomeIpLayer() {} - - /** - * A static method that creates a SOME/IP or SOME/IP-TP layer from packet raw data. Returns PayloadLayer if data is - * not valid. - * @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 - * @return Layer* A newly allocated layer - */ - static Layer* parseSomeIpLayer(uint8_t *data, size_t dataLen, Layer* prevLayer, Packet* packet); - - /** - * Get a pointer to the basic SOME/IP header. Notice this points directly to the data, so every change will change - * the actual packet data - * @return A pointer to the someiphdr - */ - someiphdr *getSomeIpHeader() const { return (someiphdr *)m_Data; } - - /** - * Checks if given port is a SOME/IP protocol port (only Service Discovery ports are checked for now) - * @param[in] port Port to check - * @return true if SOME/IP protocol port, false if not - */ - static bool isSomeIpPort(uint16_t port); - - /** - * Adds port to a list of ports where pcap checks for SOME/IP communication. - * Each port must be removed at the end in order to have no memory leak. - * @param[in] port Port to add - */ - static void addSomeIpPort(uint16_t port); - - /** - * Removes port from a list of ports where pcap checks for SOME/IP communication. - * @param[in] port Port to remove - */ - static void removeSomeIpPort(uint16_t port); - - /** - * Removes all ports from a list of ports where pcap checks for SOME/IP communication. - */ - static void removeAllSomeIpPorts(); - - /** - * Get the messageID - * @return uint32_t returned in host endian - */ - uint32_t getMessageID() const; - - /** - * Set the Message ID - * @param[in] messageID messageID to set - */ - void setMessageID(uint32_t messageID); - - /** - * Get the serviceID - * @return uint16_t returned in host endian - */ - uint16_t getServiceID() const; - - /** - * Set the Service ID - * @param[in] serviceID serviceID to set - */ - void setServiceID(uint16_t serviceID); - - /** - * Get the methodID - * @return uint16_t returned in host endian - */ - uint16_t getMethodID() const; - - /** - * Set the Method ID - * @param[in] methodID methodID to set - */ - void setMethodID(uint16_t methodID); - - /** - * Get the Length Field of the SOME/IP header - * @return uint32_t The length field of the SOME/IP header - */ - uint32_t getLengthField() const; - - /** - * Get the requestID - * @return uint32_t returned in host endian - */ - uint32_t getRequestID() const; - - /** - * Set the Request ID - * @param[in] requestID requestID to set - */ - void setRequestID(uint32_t requestID); - - /** - * Get the sessionID - * @return uint16_t returned in host endian - */ - uint16_t getSessionID() const; - - /** - * Set the Session ID - * @param[in] sessionID sessionID to set - */ - void setSessionID(uint16_t sessionID); - - /** - * Get the clientID - * @return uint16_t returned in host endian - */ - uint16_t getClientID() const; - - /** - * Set the Client ID - * @param[in] clientID clientID to set - */ - void setClientID(uint16_t clientID); - - /** - * Get the protocolVersion - * @return uint8_t - */ - uint8_t getProtocolVersion() const; - - /** - * Set the Protocol Version - * @param[in] version version to set - */ - void setProtocolVersion(uint8_t version); - - /** - * Get the interfaceVersion - * @return uint8_t - */ - uint8_t getInterfaceVersion() const; - - /** - * Set the Interface Version - * @param[in] version version to set - */ - void setInterfaceVersion(uint8_t version); - - /** - * Get the message type - * @return uint8_t - */ - uint8_t getMessageTypeAsInt() const; - - /** - * Get the message type - * @return SomeIpLayer::MsgType - */ - SomeIpLayer::MsgType getMessageType() const; - - /** - * Set the Message Type - * @param[in] type Type to set - */ - void setMessageType(MsgType type); - - /** - * Set the Message Type - * @param[in] type Type to set - */ - void setMessageType(uint8_t type); - - /** - * Get the returnCode - * @return uint8_t - */ - uint8_t getReturnCode() const; - - /** - * Set the returnCode - * @param[in] returnCode ReturnCode to set - */ - void setReturnCode(uint8_t returnCode); - - /** - * Set the length field of the SOME/IP header - * @param[in] payloadLength Length of the payload - */ - void setPayloadLength(uint32_t payloadLength); - - /** - * @return A pointer for the layer payload, meaning the first byte after the header - */ - uint8_t *getPduPayload() const { return m_Data + getSomeIpHeaderLen(); } - - /** - * @return The size in bytes of the payload - */ - size_t getPduPayloadSize() const { return getHeaderLen() - getSomeIpHeaderLen(); } - - /** - * Get the Length of the SOME/IP header inc payload - * @return size_t - */ - size_t getHeaderLen() const { return sizeof(uint32_t) * 2 + getLengthField(); } - - /** - * Does nothing for this layer - */ - virtual void computeCalculateFields() {} - - /** - * Identifies the following next layers: SomeIpLayer, SomeIpTpLayer, SomeIpSdLayer. Otherwise sets PayloadLayer - */ - void parseNextLayer(); - - /** - * @return The string representation of the SOME/IP layer - */ - virtual std::string toString() const; - - /** - * @return The OSI model layer of this layer - */ - OsiModelLayer getOsiModelLayer() const { return OsiModelApplicationLayer; } - -protected: - SomeIpLayer() {} - -private: - static const uint8_t SOMEIP_PROTOCOL_VERSION = 1; - virtual size_t getSomeIpHeaderLen() const { return sizeof(someiphdr); } - - /* Using unordered_set since insertion and search should be almost constant time */ - static std::unordered_set m_SomeIpPorts; + /** + * A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data (will be casted to someiphdr) + * @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 + */ + SomeIpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : Layer(data, dataLen, prevLayer, packet) { + m_Protocol = SomeIP; + } + + /** + * Construct a new layer object + * @param[in] serviceID Service ID + * @param[in] methodID Method ID + * @param[in] clientID Client ID + * @param[in] sessionID Session ID + * @param[in] interfaceVersion Interface Version + * @param[in] type Type of the message + * @param[in] returnCode Return Code + * @param[in] data A pointer to the raw data + * @param[in] dataLen Size of the data in bytes + * holds the reference to a data buffer. This option can be used to reduce the + * number of copies to generate packets. + */ + SomeIpLayer(uint16_t serviceID, uint16_t methodID, uint16_t clientID, + uint16_t sessionID, uint8_t interfaceVersion, MsgType type, + uint8_t returnCode, const uint8_t* const data = nullptr, + size_t dataLen = 0); + + /** + * Destroy the layer object + */ + ~SomeIpLayer() {} + + /** + * A static method that creates a SOME/IP or SOME/IP-TP layer from packet raw + * data. Returns PayloadLayer if data is not valid. + * @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 + * @return Layer* A newly allocated layer + */ + static Layer* parseSomeIpLayer(uint8_t* data, size_t dataLen, + Layer* prevLayer, Packet* packet); + + /** + * Get a pointer to the basic SOME/IP header. Notice this points directly to + * the data, so every change will change the actual packet data + * @return A pointer to the someiphdr + */ + someiphdr* getSomeIpHeader() const { return (someiphdr*)m_Data; } + + /** + * Checks if given port is a SOME/IP protocol port (only Service Discovery + * ports are checked for now) + * @param[in] port Port to check + * @return true if SOME/IP protocol port, false if not + */ + static bool isSomeIpPort(uint16_t port); + + /** + * Adds port to a list of ports where pcap checks for SOME/IP communication. + * Each port must be removed at the end in order to have no memory leak. + * @param[in] port Port to add + */ + static void addSomeIpPort(uint16_t port); + + /** + * Removes port from a list of ports where pcap checks for SOME/IP + * communication. + * @param[in] port Port to remove + */ + static void removeSomeIpPort(uint16_t port); + + /** + * Removes all ports from a list of ports where pcap checks for SOME/IP + * communication. + */ + static void removeAllSomeIpPorts(); + + /** + * Get the messageID + * @return uint32_t returned in host endian + */ + uint32_t getMessageID() const; + + /** + * Set the Message ID + * @param[in] messageID messageID to set + */ + void setMessageID(uint32_t messageID); + + /** + * Get the serviceID + * @return uint16_t returned in host endian + */ + uint16_t getServiceID() const; + + /** + * Set the Service ID + * @param[in] serviceID serviceID to set + */ + void setServiceID(uint16_t serviceID); + + /** + * Get the methodID + * @return uint16_t returned in host endian + */ + uint16_t getMethodID() const; + + /** + * Set the Method ID + * @param[in] methodID methodID to set + */ + void setMethodID(uint16_t methodID); + + /** + * Get the Length Field of the SOME/IP header + * @return uint32_t The length field of the SOME/IP header + */ + uint32_t getLengthField() const; + + /** + * Get the requestID + * @return uint32_t returned in host endian + */ + uint32_t getRequestID() const; + + /** + * Set the Request ID + * @param[in] requestID requestID to set + */ + void setRequestID(uint32_t requestID); + + /** + * Get the sessionID + * @return uint16_t returned in host endian + */ + uint16_t getSessionID() const; + + /** + * Set the Session ID + * @param[in] sessionID sessionID to set + */ + void setSessionID(uint16_t sessionID); + + /** + * Get the clientID + * @return uint16_t returned in host endian + */ + uint16_t getClientID() const; + + /** + * Set the Client ID + * @param[in] clientID clientID to set + */ + void setClientID(uint16_t clientID); + + /** + * Get the protocolVersion + * @return uint8_t + */ + uint8_t getProtocolVersion() const; + + /** + * Set the Protocol Version + * @param[in] version version to set + */ + void setProtocolVersion(uint8_t version); + + /** + * Get the interfaceVersion + * @return uint8_t + */ + uint8_t getInterfaceVersion() const; + + /** + * Set the Interface Version + * @param[in] version version to set + */ + void setInterfaceVersion(uint8_t version); + + /** + * Get the message type + * @return uint8_t + */ + uint8_t getMessageTypeAsInt() const; + + /** + * Get the message type + * @return SomeIpLayer::MsgType + */ + SomeIpLayer::MsgType getMessageType() const; + + /** + * Set the Message Type + * @param[in] type Type to set + */ + void setMessageType(MsgType type); + + /** + * Set the Message Type + * @param[in] type Type to set + */ + void setMessageType(uint8_t type); + + /** + * Get the returnCode + * @return uint8_t + */ + uint8_t getReturnCode() const; + + /** + * Set the returnCode + * @param[in] returnCode ReturnCode to set + */ + void setReturnCode(uint8_t returnCode); + + /** + * Set the length field of the SOME/IP header + * @param[in] payloadLength Length of the payload + */ + void setPayloadLength(uint32_t payloadLength); + + /** + * @return A pointer for the layer payload, meaning the first byte after the + * header + */ + uint8_t* getPduPayload() const { return m_Data + getSomeIpHeaderLen(); } + + /** + * @return The size in bytes of the payload + */ + size_t getPduPayloadSize() const { + return getHeaderLen() - getSomeIpHeaderLen(); + } + + /** + * Get the Length of the SOME/IP header inc payload + * @return size_t + */ + size_t getHeaderLen() const { + return sizeof(uint32_t) * 2 + getLengthField(); + } + + /** + * Does nothing for this layer + */ + virtual void computeCalculateFields() {} + + /** + * Identifies the following next layers: SomeIpLayer, SomeIpTpLayer, + * SomeIpSdLayer. Otherwise sets PayloadLayer + */ + void parseNextLayer(); + + /** + * @return The string representation of the SOME/IP layer + */ + virtual std::string toString() const; + + /** + * @return The OSI model layer of this layer + */ + OsiModelLayer getOsiModelLayer() const { return OsiModelApplicationLayer; } + + protected: + SomeIpLayer() {} + + private: + static const uint8_t SOMEIP_PROTOCOL_VERSION = 1; + virtual size_t getSomeIpHeaderLen() const { return sizeof(someiphdr); } + + /* Using unordered_set since insertion and search should be almost constant + * time */ + static std::unordered_set m_SomeIpPorts; }; /** * @class SomeIpTpLayer * Represents an SOME/IP Transport Protocol Layer */ -class SomeIpTpLayer : public SomeIpLayer -{ -public: - /** - * @struct someiptphdr - * Represents an SOME/IP-TP protocol header. - */ +class SomeIpTpLayer : public SomeIpLayer { + public: + /** + * @struct someiptphdr + * Represents an SOME/IP-TP protocol header. + */ #pragma pack(push, 1) - struct someiptphdr : someiphdr - { - /** Contains the offset and the more segments flag. 28 bit offset field measured in 16 bytes + 3 bit reserved + - * 1 bit more segments flag */ - uint32_t offsetAndFlag; - }; + struct someiptphdr : someiphdr { + /** Contains the offset and the more segments flag. 28 bit offset field + * measured in 16 bytes + 3 bit reserved + 1 bit more segments flag */ + uint32_t offsetAndFlag; + }; #pragma pack(pop) - /** - * A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data (will be casted to @ref someiptphdr) - * @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 - */ - SomeIpTpLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) - : SomeIpLayer(data, dataLen, prevLayer, packet) {} - - /** - * A constructor that creates empty layer and sets values - * @param[in] serviceID Service ID - * @param[in] methodID Method ID - * @param[in] clientID Client ID - * @param[in] sessionID Session ID - * @param[in] interfaceVersion Interface Version - * @param[in] type Type of the message - * @param[in] returnCode Return Code - * @param[in] offset Offset indicating the data offset in increments of 16 bytes - * @param[in] moreSegmentsFlag Flag indicating whether more SOME/IP-TP Packets will follow - * @param[in] data A pointer to the raw data - * @param[in] dataLen Size of the data in bytes - */ - SomeIpTpLayer(uint16_t serviceID, uint16_t methodID, uint16_t clientID, uint16_t sessionID, - uint8_t interfaceVersion, MsgType type, uint8_t returnCode, uint32_t offset, bool moreSegmentsFlag, - const uint8_t *const data = nullptr, size_t dataLen = 0); - - /** - * Destroy the layer object - */ - ~SomeIpTpLayer() {} - - /** - * Get a pointer to the basic SOME/IP-TP header. Notice this points directly to the data, so every change will - * change the actual packet data - * @return A pointer to the @ref someiptphdr - */ - someiptphdr *getSomeIpTpHeader() const { return (someiptphdr *)m_Data; } - - /** - * Get the Offset. Offset is returned in multiple of 16 bytes. - * @return The offset value - */ - uint32_t getOffset() const; - - /** - * Set the Offset. Already has to be in multiples of 16 bytes. - * If 32 bytes have already been transmitted, the offset has to be set to 2. - * @param[in] offset Offset to set. Already has to be in multiples of 16 bytes. - */ - void setOffset(uint32_t offset); - - /** - * Get the More Segments Flag - * @return true if the More Segments Flag is set, false if it is not set - */ - bool getMoreSegmentsFlag() const; - - /** - * Set the More Segments Flag - * @param[in] flag True if the More Segments Flag shall be set, false for resetting - */ - void setMoreSegmentsFlag(bool flag); - - /** - * Sets the message type in this layer with enabling the TP flag - */ - void computeCalculateFields(); - - /** - * @return The string representation of the SOME/IP-TP layer - */ - std::string toString() const; - -private: - static const uint32_t SOMEIP_TP_MORE_FLAG_MASK = 0x01; - static const uint32_t SOMEIP_TP_OFFSET_MASK = 0xFFFFFFF0; - - size_t getSomeIpHeaderLen() const { return sizeof(someiptphdr); } - - static uint8_t setTpFlag(uint8_t messageType); + /** + * A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data (will be casted to @ref + * someiptphdr) + * @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 + */ + SomeIpTpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : SomeIpLayer(data, dataLen, prevLayer, packet) {} + + /** + * A constructor that creates empty layer and sets values + * @param[in] serviceID Service ID + * @param[in] methodID Method ID + * @param[in] clientID Client ID + * @param[in] sessionID Session ID + * @param[in] interfaceVersion Interface Version + * @param[in] type Type of the message + * @param[in] returnCode Return Code + * @param[in] offset Offset indicating the data offset in increments of 16 + * bytes + * @param[in] moreSegmentsFlag Flag indicating whether more SOME/IP-TP Packets + * will follow + * @param[in] data A pointer to the raw data + * @param[in] dataLen Size of the data in bytes + */ + SomeIpTpLayer(uint16_t serviceID, uint16_t methodID, uint16_t clientID, + uint16_t sessionID, uint8_t interfaceVersion, MsgType type, + uint8_t returnCode, uint32_t offset, bool moreSegmentsFlag, + const uint8_t* const data = nullptr, size_t dataLen = 0); + + /** + * Destroy the layer object + */ + ~SomeIpTpLayer() {} + + /** + * Get a pointer to the basic SOME/IP-TP header. Notice this points directly + * to the data, so every change will change the actual packet data + * @return A pointer to the @ref someiptphdr + */ + someiptphdr* getSomeIpTpHeader() const { return (someiptphdr*)m_Data; } + + /** + * Get the Offset. Offset is returned in multiple of 16 bytes. + * @return The offset value + */ + uint32_t getOffset() const; + + /** + * Set the Offset. Already has to be in multiples of 16 bytes. + * If 32 bytes have already been transmitted, the offset has to be set to 2. + * @param[in] offset Offset to set. Already has to be in multiples of 16 + * bytes. + */ + void setOffset(uint32_t offset); + + /** + * Get the More Segments Flag + * @return true if the More Segments Flag is set, false if it is not set + */ + bool getMoreSegmentsFlag() const; + + /** + * Set the More Segments Flag + * @param[in] flag True if the More Segments Flag shall be set, false for + * resetting + */ + void setMoreSegmentsFlag(bool flag); + + /** + * Sets the message type in this layer with enabling the TP flag + */ + void computeCalculateFields(); + + /** + * @return The string representation of the SOME/IP-TP layer + */ + std::string toString() const; + + private: + static const uint32_t SOMEIP_TP_MORE_FLAG_MASK = 0x01; + static const uint32_t SOMEIP_TP_OFFSET_MASK = 0xFFFFFFF0; + + size_t getSomeIpHeaderLen() const { return sizeof(someiptphdr); } + + static uint8_t setTpFlag(uint8_t messageType); }; } // namespace pcpp diff --git a/Packet++/header/SomeIpSdLayer.h b/Packet++/header/SomeIpSdLayer.h index b5e88736d5..3060037bdd 100644 --- a/Packet++/header/SomeIpSdLayer.h +++ b/Packet++/header/SomeIpSdLayer.h @@ -18,17 +18,15 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ +namespace pcpp { /** * Types of protocols that can be referenced in SOME/IP-SD */ -enum SomeIpSdProtocolType : uint8_t -{ - /** TCP */ - SD_TCP = 0x06, - /** UDP */ - SD_UDP = 0x11 +enum SomeIpSdProtocolType : uint8_t { + /** TCP */ + SD_TCP = 0x06, + /** UDP */ + SD_UDP = 0x11 }; class SomeIpSdLayer; @@ -37,249 +35,248 @@ class SomeIpSdLayer; * @class SomeIpSdOption * Base class of the SOME/IP-SD options. Cannot be instantiated. */ -class SomeIpSdOption -{ -public: - friend class SomeIpSdLayer; - - /** - * Types of options currently available for the SOME/IP-SD protocol - */ - enum class OptionType : uint8_t - { - /** Unknown Option Type */ - Unknown = 0x00, - /** Configuration Option */ - ConfigurationString = 0x01, - /** Load Balancing Option */ - LoadBalancing = 0x02, - /** IPv4 Endpoint Option */ - IPv4Endpoint = 0x04, - /** IPv6 Endpoint Option */ - IPv6Endpoint = 0x06, - /** IPv4 Multicast Option */ - IPv4Multicast = 0x14, - /** IPv6 Multicast Option */ - IPv6Multicast = 0x16, - /** IPv4 SD Endpoint Option */ - IPv4SdEndpoint = 0x24, - /** IPv6 SD Endpoint Option */ - IPv6SdEndpoint = 0x26 - }; - - /** - * @struct someipsdhdroptionsbase - * Represents the common base for SOME/IP-SD header options - */ +class SomeIpSdOption { + public: + friend class SomeIpSdLayer; + + /** + * Types of options currently available for the SOME/IP-SD protocol + */ + enum class OptionType : uint8_t { + /** Unknown Option Type */ + Unknown = 0x00, + /** Configuration Option */ + ConfigurationString = 0x01, + /** Load Balancing Option */ + LoadBalancing = 0x02, + /** IPv4 Endpoint Option */ + IPv4Endpoint = 0x04, + /** IPv6 Endpoint Option */ + IPv6Endpoint = 0x06, + /** IPv4 Multicast Option */ + IPv4Multicast = 0x14, + /** IPv6 Multicast Option */ + IPv6Multicast = 0x16, + /** IPv4 SD Endpoint Option */ + IPv4SdEndpoint = 0x24, + /** IPv6 SD Endpoint Option */ + IPv6SdEndpoint = 0x26 + }; + + /** + * @struct someipsdhdroptionsbase + * Represents the common base for SOME/IP-SD header options + */ #pragma pack(push, 1) - struct someipsdhdroptionsbase - { - /** Length - excluding the 16 bit Length field and the 8 bit type flag */ - uint16_t length; - /** Type */ - uint8_t type; - /** Reserved */ - uint8_t reserved; - }; + struct someipsdhdroptionsbase { + /** Length - excluding the 16 bit Length field and the 8 bit type flag */ + uint16_t length; + /** Type */ + uint8_t type; + /** Reserved */ + uint8_t reserved; + }; #pragma pack(pop) - /** - * Destroy the SOME/IP-SD Option object and delete allocated data if it has been allocated by a constructor - */ - virtual ~SomeIpSdOption(); - - /** - * Get the Option Type - * @return OptionType - */ - OptionType getType() const; - - /** - * Get the Length of the SOME/IP-SD option - * @return size_t - */ - size_t getLength() const { return m_DataLen; } - - /** - * Get the internal data of the SOME/IP-SD Option - * @return uint8_t* - */ - uint8_t *getDataPtr() const; - - /** - * Get a pointer to the SOME/IP-SD Option base header - * @return someipsdhdroptionsbase* - */ - someipsdhdroptionsbase *getSomeIpSdOptionHeader() const; - -protected: - const IDataContainer *m_DataContainer; - size_t m_Offset; - uint8_t *m_ShadowData; - size_t m_DataLen; - - SomeIpSdOption() : m_DataContainer(nullptr), m_Offset(0), m_ShadowData(nullptr), m_DataLen(0) {} - - SomeIpSdOption(const IDataContainer *dataContainer, size_t offset) - : m_DataContainer(dataContainer), m_Offset(offset), m_ShadowData(nullptr), m_DataLen(0) {} - - void initStdFields(OptionType type); - - SomeIpSdOption(const SomeIpSdOption &) = delete; - SomeIpSdOption &operator=(const SomeIpSdOption &) = delete; + /** + * Destroy the SOME/IP-SD Option object and delete allocated data if it has + * been allocated by a constructor + */ + virtual ~SomeIpSdOption(); + + /** + * Get the Option Type + * @return OptionType + */ + OptionType getType() const; + + /** + * Get the Length of the SOME/IP-SD option + * @return size_t + */ + size_t getLength() const { return m_DataLen; } + + /** + * Get the internal data of the SOME/IP-SD Option + * @return uint8_t* + */ + uint8_t* getDataPtr() const; + + /** + * Get a pointer to the SOME/IP-SD Option base header + * @return someipsdhdroptionsbase* + */ + someipsdhdroptionsbase* getSomeIpSdOptionHeader() const; + + protected: + const IDataContainer* m_DataContainer; + size_t m_Offset; + uint8_t* m_ShadowData; + size_t m_DataLen; + + SomeIpSdOption() + : m_DataContainer(nullptr), m_Offset(0), m_ShadowData(nullptr), + m_DataLen(0) {} + + SomeIpSdOption(const IDataContainer* dataContainer, size_t offset) + : m_DataContainer(dataContainer), m_Offset(offset), m_ShadowData(nullptr), + m_DataLen(0) {} + + void initStdFields(OptionType type); + + SomeIpSdOption(const SomeIpSdOption&) = delete; + SomeIpSdOption& operator=(const SomeIpSdOption&) = delete; }; /** * @class SomeIpSdIPv4Option - * Implements the following SOME/IP-SD Options: IPv4 Endpoint, IPv4 Multicast, IPv4 SD Endpoint + * Implements the following SOME/IP-SD Options: IPv4 Endpoint, IPv4 Multicast, + * IPv4 SD Endpoint */ -class SomeIpSdIPv4Option : public SomeIpSdOption -{ -public: - friend class SomeIpSdLayer; - - /** - * Types of options which are implemented with this class - */ - enum IPv4OptionType - { - /** IPv4 Endpoint Option */ - IPv4Endpoint, - /** IPv4 Multicast Option */ - IPv4Multicast, - /** IPv4 SD Endpoint Option */ - IPv4SdEndpoint, - }; - - /** - * Construct a new SomeIpSdIPv4 Option object - * @param[in] type IPv4 Option type - * @param[in] ipAddress Ipv4 address to use - * @param[in] port Port to use - * @param[in] l4Protocol Protocol to use - */ - SomeIpSdIPv4Option(IPv4OptionType type, IPv4Address ipAddress, uint16_t port, SomeIpSdProtocolType l4Protocol); - - /** - * Construct a new SomeIpSdIPv4 Option object from already existing memory - * @param[in] dataContainer Data containing the SomeIpSdIPv4 Option object - * @param[in] offset Offset for dataContainer - */ - SomeIpSdIPv4Option(const IDataContainer *dataContainer, size_t offset); - - /** - * Get the Ip Address - * @return IPv4Address - */ - IPv4Address getIpAddress() const; - - /** - * Get the Port - * @return uint16_t - */ - uint16_t getPort() const; - - /** - * Get the Protocol - * @return SomeIpSdProtocolType - */ - SomeIpSdProtocolType getProtocol() const; - -private: - /** - * @struct someipsdhdroptionsipv4 - * Represents the IPv4 option types for the SOME/IP-SD header - */ +class SomeIpSdIPv4Option : public SomeIpSdOption { + public: + friend class SomeIpSdLayer; + + /** + * Types of options which are implemented with this class + */ + enum IPv4OptionType { + /** IPv4 Endpoint Option */ + IPv4Endpoint, + /** IPv4 Multicast Option */ + IPv4Multicast, + /** IPv4 SD Endpoint Option */ + IPv4SdEndpoint, + }; + + /** + * Construct a new SomeIpSdIPv4 Option object + * @param[in] type IPv4 Option type + * @param[in] ipAddress Ipv4 address to use + * @param[in] port Port to use + * @param[in] l4Protocol Protocol to use + */ + SomeIpSdIPv4Option(IPv4OptionType type, IPv4Address ipAddress, uint16_t port, + SomeIpSdProtocolType l4Protocol); + + /** + * Construct a new SomeIpSdIPv4 Option object from already existing memory + * @param[in] dataContainer Data containing the SomeIpSdIPv4 Option object + * @param[in] offset Offset for dataContainer + */ + SomeIpSdIPv4Option(const IDataContainer* dataContainer, size_t offset); + + /** + * Get the Ip Address + * @return IPv4Address + */ + IPv4Address getIpAddress() const; + + /** + * Get the Port + * @return uint16_t + */ + uint16_t getPort() const; + + /** + * Get the Protocol + * @return SomeIpSdProtocolType + */ + SomeIpSdProtocolType getProtocol() const; + + private: + /** + * @struct someipsdhdroptionsipv4 + * Represents the IPv4 option types for the SOME/IP-SD header + */ #pragma pack(push, 1) - struct someipsdhdroptionsipv4 : someipsdhdroptionsbase - { - /* IPv4-Address field */ - uint32_t ipv4Address; - /* Reserved */ - // cppcheck-suppress duplInheritedMember - uint8_t reserved; - /* Layer 4 Protocol field (L4-Proto) - Either UDP or TCP */ - SomeIpSdProtocolType l4Protocol; - /* Port number of UDP or TCP */ - uint16_t portNumber; - }; + struct someipsdhdroptionsipv4 : someipsdhdroptionsbase { + /* IPv4-Address field */ + uint32_t ipv4Address; + /* Reserved */ + // cppcheck-suppress duplInheritedMember + uint8_t reserved; + /* Layer 4 Protocol field (L4-Proto) - Either UDP or TCP */ + SomeIpSdProtocolType l4Protocol; + /* Port number of UDP or TCP */ + uint16_t portNumber; + }; #pragma pack(pop) }; /** * @class SomeIpSdIPv6Option - * Implements the following SOME/IP-SD Options: IPv6 Endpoint, IPv6 Multicast, IPv6 SD Endpoint + * Implements the following SOME/IP-SD Options: IPv6 Endpoint, IPv6 Multicast, + * IPv6 SD Endpoint */ -class SomeIpSdIPv6Option : public SomeIpSdOption -{ -public: - friend class SomeIpSdLayer; - - /** - * Types of options which are implemented with this class - */ - enum IPv6OptionType - { - /** IPv6 Endpoint Option */ - IPv6Endpoint, - /** IPv6 Multicast Option */ - IPv6Multicast, - /** IPv6 SD Endpoint Option */ - IPv6SdEndpoint, - }; - - /** - * Construct a new SomeIpSdIPv6 Option object - * @param[in] type IPv6 Option type - * @param[in] ipAddress Ipv6 address to use - * @param[in] port Port to use - * @param[in] l4Protocol Protocol to use - */ - SomeIpSdIPv6Option(IPv6OptionType type, IPv6Address ipAddress, uint16_t port, SomeIpSdProtocolType l4Protocol); - - /** - * Construct a new SomeIpSdIPv6 Option object from already existing memory - * @param[in] dataContainer Data containing the SomeIpSdIPv6 Option object - * @param[in] offset Offset for dataContainer - */ - SomeIpSdIPv6Option(const IDataContainer *dataContainer, size_t offset); - - /** - * Get the Ip Address - * @return IPv6Address - */ - IPv6Address getIpAddress() const; - - /** - * Get the Port - * @return uint16_t - */ - uint16_t getPort() const; - - /** - * Get the Protocol - * @return SomeIpSdProtocolType - */ - SomeIpSdProtocolType getProtocol() const; - -private: - /** - * @struct someipsdhdroptionsipv6 - * Represents the IPv6 option types for the SOME/IP-SD header - */ +class SomeIpSdIPv6Option : public SomeIpSdOption { + public: + friend class SomeIpSdLayer; + + /** + * Types of options which are implemented with this class + */ + enum IPv6OptionType { + /** IPv6 Endpoint Option */ + IPv6Endpoint, + /** IPv6 Multicast Option */ + IPv6Multicast, + /** IPv6 SD Endpoint Option */ + IPv6SdEndpoint, + }; + + /** + * Construct a new SomeIpSdIPv6 Option object + * @param[in] type IPv6 Option type + * @param[in] ipAddress Ipv6 address to use + * @param[in] port Port to use + * @param[in] l4Protocol Protocol to use + */ + SomeIpSdIPv6Option(IPv6OptionType type, IPv6Address ipAddress, uint16_t port, + SomeIpSdProtocolType l4Protocol); + + /** + * Construct a new SomeIpSdIPv6 Option object from already existing memory + * @param[in] dataContainer Data containing the SomeIpSdIPv6 Option object + * @param[in] offset Offset for dataContainer + */ + SomeIpSdIPv6Option(const IDataContainer* dataContainer, size_t offset); + + /** + * Get the Ip Address + * @return IPv6Address + */ + IPv6Address getIpAddress() const; + + /** + * Get the Port + * @return uint16_t + */ + uint16_t getPort() const; + + /** + * Get the Protocol + * @return SomeIpSdProtocolType + */ + SomeIpSdProtocolType getProtocol() const; + + private: + /** + * @struct someipsdhdroptionsipv6 + * Represents the IPv6 option types for the SOME/IP-SD header + */ #pragma pack(push, 1) - struct someipsdhdroptionsipv6 : someipsdhdroptionsbase - { - /* IPv6-Address field */ - uint8_t ipv6Address[16]; - /* Reserved */ - // cppcheck-suppress duplInheritedMember - uint8_t reserved; - /* Layer 4 Protocol field (L4-Proto) - Either UDP or TCP */ - SomeIpSdProtocolType l4Protocol; - /* Port number of UDP or TCP */ - uint16_t portNumber; - }; + struct someipsdhdroptionsipv6 : someipsdhdroptionsbase { + /* IPv6-Address field */ + uint8_t ipv6Address[16]; + /* Reserved */ + // cppcheck-suppress duplInheritedMember + uint8_t reserved; + /* Layer 4 Protocol field (L4-Proto) - Either UDP or TCP */ + SomeIpSdProtocolType l4Protocol; + /* Port number of UDP or TCP */ + uint16_t portNumber; + }; #pragma pack(pop) }; @@ -287,79 +284,78 @@ class SomeIpSdIPv6Option : public SomeIpSdOption * @class SomeIpSdConfigurationOption * Implements the Configuration option of SOME/IP-SD protocol */ -class SomeIpSdConfigurationOption : public SomeIpSdOption -{ -public: - friend class SomeIpSdLayer; - - /** - * Construct a new Configuration Option object - * @param[in] configurationString the configuration string - */ - explicit SomeIpSdConfigurationOption(const std::string &configurationString); - - /** - * Construct a new Configuration Option object from already existing memory - * @param[in] dataContainer Data containing the Configuration Option object - * @param[in] offset Offset for dataContainer - */ - SomeIpSdConfigurationOption(const IDataContainer *dataContainer, size_t offset); - - /** - * Get the configuration string - * @return std::string - */ - std::string getConfigurationString() const; +class SomeIpSdConfigurationOption : public SomeIpSdOption { + public: + friend class SomeIpSdLayer; + + /** + * Construct a new Configuration Option object + * @param[in] configurationString the configuration string + */ + explicit SomeIpSdConfigurationOption(const std::string& configurationString); + + /** + * Construct a new Configuration Option object from already existing memory + * @param[in] dataContainer Data containing the Configuration Option object + * @param[in] offset Offset for dataContainer + */ + SomeIpSdConfigurationOption(const IDataContainer* dataContainer, + size_t offset); + + /** + * Get the configuration string + * @return std::string + */ + std::string getConfigurationString() const; }; /** * @class SomeIpSdLoadBalancingOption * Implements the Load Balancing option of SOME/IP-SD protocol */ -class SomeIpSdLoadBalancingOption : public SomeIpSdOption -{ -public: - friend class SomeIpSdLayer; - - /** - * Construct a new Load Balancing object - * @param[in] priority Priority of this instance - * @param[in] weight Weight of this instance - */ - SomeIpSdLoadBalancingOption(uint16_t priority, uint16_t weight); - - /** - * Construct a new Option object from already existing memory - * @param[in] dataContainer Data containing the option object - * @param[in] offset Offset for dataContainer - */ - SomeIpSdLoadBalancingOption(const IDataContainer *dataContainer, size_t offset); - - /** - * Get the priority fild - * @return uint16_t - */ - uint16_t getPriority() const; - - /** - * Get the weight field - * @return uint16_t - */ - uint16_t getWeight() const; - -private: - /** - * @struct someipsdhdroptionsload - * Represents the Load Balancing option header for SOME/IP-SD - */ +class SomeIpSdLoadBalancingOption : public SomeIpSdOption { + public: + friend class SomeIpSdLayer; + + /** + * Construct a new Load Balancing object + * @param[in] priority Priority of this instance + * @param[in] weight Weight of this instance + */ + SomeIpSdLoadBalancingOption(uint16_t priority, uint16_t weight); + + /** + * Construct a new Option object from already existing memory + * @param[in] dataContainer Data containing the option object + * @param[in] offset Offset for dataContainer + */ + SomeIpSdLoadBalancingOption(const IDataContainer* dataContainer, + size_t offset); + + /** + * Get the priority fild + * @return uint16_t + */ + uint16_t getPriority() const; + + /** + * Get the weight field + * @return uint16_t + */ + uint16_t getWeight() const; + + private: + /** + * @struct someipsdhdroptionsload + * Represents the Load Balancing option header for SOME/IP-SD + */ #pragma pack(push, 1) - struct someipsdhdroptionsload : someipsdhdroptionsbase - { - /* Priority field */ - uint16_t priority; - /* Weight field */ - uint16_t weight; - }; + struct someipsdhdroptionsload : someipsdhdroptionsbase { + /* Priority field */ + uint16_t priority; + /* Weight field */ + uint16_t weight; + }; #pragma pack(pop) }; @@ -367,412 +363,416 @@ class SomeIpSdLoadBalancingOption : public SomeIpSdOption * @class SomeIpSdEntry * Implementation of the SOME/IP-SD Service Entry and Eventgroup Entry Type */ -class SomeIpSdEntry -{ -public: - friend class SomeIpSdLayer; - - /** - * Types of entries that can occur in SOME/IP-SD - */ - enum class EntryType : uint8_t - { - /** Find Service */ - FindService, - /** Offer Service */ - OfferService, - /** Stop Offer Service */ - StopOfferService, - /** Subscribe Eventgroup */ - SubscribeEventgroup, - /** Stop Subscribe Eventgroup */ - StopSubscribeEventgroup, - /** Subscribe Eventgroup Acknowledgment */ - SubscribeEventgroupAck, - /** Subscribe Eventgroup Negative Acknowledgement */ - SubscribeEventgroupNack, - /** Unknown Entry Type */ - UnknownEntryType - }; - - /** - * @struct someipsdhdrentry - * Represents the Service Entry Type and Eventgroup Entry Type - */ +class SomeIpSdEntry { + public: + friend class SomeIpSdLayer; + + /** + * Types of entries that can occur in SOME/IP-SD + */ + enum class EntryType : uint8_t { + /** Find Service */ + FindService, + /** Offer Service */ + OfferService, + /** Stop Offer Service */ + StopOfferService, + /** Subscribe Eventgroup */ + SubscribeEventgroup, + /** Stop Subscribe Eventgroup */ + StopSubscribeEventgroup, + /** Subscribe Eventgroup Acknowledgment */ + SubscribeEventgroupAck, + /** Subscribe Eventgroup Negative Acknowledgement */ + SubscribeEventgroupNack, + /** Unknown Entry Type */ + UnknownEntryType + }; + + /** + * @struct someipsdhdrentry + * Represents the Service Entry Type and Eventgroup Entry Type + */ #pragma pack(push, 1) - struct someipsdhdrentry - { - /** Type */ - uint8_t type; - /** Index 1st option */ - uint8_t indexFirstOption; - /** Index 2nd option */ - uint8_t indexSecondOption; + struct someipsdhdrentry { + /** Type */ + uint8_t type; + /** Index 1st option */ + uint8_t indexFirstOption; + /** Index 2nd option */ + uint8_t indexSecondOption; #if (BYTE_ORDER == LITTLE_ENDIAN) - uint8_t - /** Numbers of Option #2 (4bit) */ - nrOpt2 : 4, - /** Numbers of Option #1 (4bit) */ - nrOpt1 : 4; + uint8_t + /** Numbers of Option #2 (4bit) */ + nrOpt2 : 4, + /** Numbers of Option #1 (4bit) */ + nrOpt1 : 4; #else - uint8_t - /** Numbers of Option #1 (4bit) */ - nrOpt1 : 4, - /** Numbers of Option #2 (4bit) */ - nrOpt2 : 4; + uint8_t + /** Numbers of Option #1 (4bit) */ + nrOpt1 : 4, + /** Numbers of Option #2 (4bit) */ + nrOpt2 : 4; #endif - /** Service ID */ - uint16_t serviceID; - /** Instance ID */ - uint16_t instanceID; - /** Major Version (8 bit) + TTL (24 bit) */ - uint32_t majorVersion_ttl; - /** Minor Version (Service Entry Type) or Counter + Eventgroup ID (Eventgroup Entry Type) */ - uint32_t data; - }; + /** Service ID */ + uint16_t serviceID; + /** Instance ID */ + uint16_t instanceID; + /** Major Version (8 bit) + TTL (24 bit) */ + uint32_t majorVersion_ttl; + /** Minor Version (Service Entry Type) or Counter + Eventgroup ID + * (Eventgroup Entry Type) */ + uint32_t data; + }; #pragma pack(pop) - /** - * Construct a new SOME/IP-SD Service Entry Type - * @param[in] type Type to create - * @param[in] serviceID ServiceID to use - * @param[in] instanceID InstanceID to use - * @param[in] majorVersion MajorVersion to use - * @param[in] TTL TTL to use. Has to be 0 for all Stop* entry types - * @param[in] minorVersion MinorVersion to use - */ - SomeIpSdEntry(EntryType type, uint16_t serviceID, uint16_t instanceID, uint8_t majorVersion, uint32_t TTL, - uint32_t minorVersion); - - /** - * Construct a new SOME/IP-SD Eventgroup Entry Type - * @param[in] type Type to create - * @param[in] serviceID ServiceID to use - * @param[in] instanceID InstanceID to use - * @param[in] majorVersion MajorVersion to use - * @param[in] TTL TTL to use. Has to be 0 for all Stop* entry types - * @param[in] counter Counter value to use - * @param[in] eventGroupID EventgroupId to use - */ - SomeIpSdEntry(EntryType type, uint16_t serviceID, uint16_t instanceID, uint8_t majorVersion, uint32_t TTL, - uint8_t counter, uint16_t eventGroupID); - - /** - * Construct a new SomeIpSdEntry object from existing data - * @param[in] pSomeIpSdLayer Layer that this entry is created for - * @param[in] offset Offset for pSomeIpSdLayer - */ - SomeIpSdEntry(const SomeIpSdLayer *pSomeIpSdLayer, size_t offset); - - /** - * Destroy the SomeIpSd Entry object and delete allocated data if it has been allocated by a constructor - */ - ~SomeIpSdEntry(); - - /** - * Get the internal data of the SOME/IP-SD Entry - * @return uint8_t* - */ - uint8_t *getDataPtr() const; - - /** - * Get a pointer to the SOME/IP-SD Entry header - * @return someipsdhdrentry* - */ - someipsdhdrentry *getSomeIpSdEntryHeader() const; - - /** - * Get the Entry Type - * @return EntryType - */ - EntryType getType() const { return m_EntryType; } - - /** - * Get the Length of the SomeIpSd Entry - * @return size_t - */ - size_t getLength() const { return sizeof(someipsdhdrentry); } - - /** - * Get the number of Options of this Entry - * @return uint32_t - */ - uint32_t getNumOptions() const; - - /** - * Get the Service Id in host endianness - * @return uint16_t - */ - uint16_t getServiceId() const; - - /** - * Set the Service Id - * @param[in] serviceId - */ - void setServiceId(uint16_t serviceId); - - /** - * Get the Instance Id in host endianness - * @return uint16_t - */ - uint16_t getInstanceId() const; - - /** - * Set the Instance Id - * @param[in] instanceId - */ - void setInstanceId(uint16_t instanceId); - - /** - * Get the Major version field in host endianness - * @return uint16_t - */ - uint8_t getMajorVersion() const; - - /** - * Set the Major Version - * @param[in] majorVersion - */ - void setMajorVersion(uint8_t majorVersion); - - /** - * Get the Ttl field - * @return uint32_t - */ - uint32_t getTtl() const; - - /** - * Set the Ttl field - * @param[in] ttl - */ - void setTtl(uint32_t ttl); - - /** - * Get the minor version - * @return uint32_t - */ - uint32_t getMinorVersion() const; - - /** - * Set the minor version - * @param[in] minorVersion - */ - void setMinorVersion(uint32_t minorVersion); - - /** - * Get the counter value - * @return uint32_t - */ - uint8_t getCounter() const; - - /** - * Set the counter value - * @param[in] counter - */ - void setCounter(uint8_t counter); - - /** - * Get the eventgroup id - * @return uint32_t - */ - uint16_t getEventgroupId() const; - - /** - * Set the eventgroup id - * @param[in] eventgroupID - */ - void setEventgroupId(uint16_t eventgroupID); - -private: - /** - * These are the entry types used by SOME/IP-SD. They cannot be used for parameter passing since the values - * are not unique. - */ - enum class TypeInternal : uint8_t - { - /** Find Service */ - FindService_Internal = 0x00, - /** Offer Service / Stop Offer Service */ - OfferService_Internal = 0x01, - /** Subscribe Eventgroup & Stop Subscribe Eventgroup */ - SubscribeEventgroup_Internal = 0x06, - /** Subscribe Eventgroup Acknowledgment / Negative Acknowledgement */ - SubscribeEventgroupAck_Internal = 0x07, - }; - - EntryType m_EntryType; - const SomeIpSdLayer *m_Layer; - size_t m_Offset; - uint8_t *m_ShadowData; - - void initStdFields(EntryType type, uint16_t serviceID, uint16_t instanceID, uint8_t majorVersion, uint32_t TTL); - - SomeIpSdEntry(const SomeIpSdEntry &) = delete; - SomeIpSdEntry &operator=(const SomeIpSdEntry &) = delete; - - static const uint32_t SOMEIPSD_HDR_ENTRY_MASK_TTL = 0x00FFFFFF; + /** + * Construct a new SOME/IP-SD Service Entry Type + * @param[in] type Type to create + * @param[in] serviceID ServiceID to use + * @param[in] instanceID InstanceID to use + * @param[in] majorVersion MajorVersion to use + * @param[in] TTL TTL to use. Has to be 0 for all Stop* entry types + * @param[in] minorVersion MinorVersion to use + */ + SomeIpSdEntry(EntryType type, uint16_t serviceID, uint16_t instanceID, + uint8_t majorVersion, uint32_t TTL, uint32_t minorVersion); + + /** + * Construct a new SOME/IP-SD Eventgroup Entry Type + * @param[in] type Type to create + * @param[in] serviceID ServiceID to use + * @param[in] instanceID InstanceID to use + * @param[in] majorVersion MajorVersion to use + * @param[in] TTL TTL to use. Has to be 0 for all Stop* entry types + * @param[in] counter Counter value to use + * @param[in] eventGroupID EventgroupId to use + */ + SomeIpSdEntry(EntryType type, uint16_t serviceID, uint16_t instanceID, + uint8_t majorVersion, uint32_t TTL, uint8_t counter, + uint16_t eventGroupID); + + /** + * Construct a new SomeIpSdEntry object from existing data + * @param[in] pSomeIpSdLayer Layer that this entry is created for + * @param[in] offset Offset for pSomeIpSdLayer + */ + SomeIpSdEntry(const SomeIpSdLayer* pSomeIpSdLayer, size_t offset); + + /** + * Destroy the SomeIpSd Entry object and delete allocated data if it has been + * allocated by a constructor + */ + ~SomeIpSdEntry(); + + /** + * Get the internal data of the SOME/IP-SD Entry + * @return uint8_t* + */ + uint8_t* getDataPtr() const; + + /** + * Get a pointer to the SOME/IP-SD Entry header + * @return someipsdhdrentry* + */ + someipsdhdrentry* getSomeIpSdEntryHeader() const; + + /** + * Get the Entry Type + * @return EntryType + */ + EntryType getType() const { return m_EntryType; } + + /** + * Get the Length of the SomeIpSd Entry + * @return size_t + */ + size_t getLength() const { return sizeof(someipsdhdrentry); } + + /** + * Get the number of Options of this Entry + * @return uint32_t + */ + uint32_t getNumOptions() const; + + /** + * Get the Service Id in host endianness + * @return uint16_t + */ + uint16_t getServiceId() const; + + /** + * Set the Service Id + * @param[in] serviceId + */ + void setServiceId(uint16_t serviceId); + + /** + * Get the Instance Id in host endianness + * @return uint16_t + */ + uint16_t getInstanceId() const; + + /** + * Set the Instance Id + * @param[in] instanceId + */ + void setInstanceId(uint16_t instanceId); + + /** + * Get the Major version field in host endianness + * @return uint16_t + */ + uint8_t getMajorVersion() const; + + /** + * Set the Major Version + * @param[in] majorVersion + */ + void setMajorVersion(uint8_t majorVersion); + + /** + * Get the Ttl field + * @return uint32_t + */ + uint32_t getTtl() const; + + /** + * Set the Ttl field + * @param[in] ttl + */ + void setTtl(uint32_t ttl); + + /** + * Get the minor version + * @return uint32_t + */ + uint32_t getMinorVersion() const; + + /** + * Set the minor version + * @param[in] minorVersion + */ + void setMinorVersion(uint32_t minorVersion); + + /** + * Get the counter value + * @return uint32_t + */ + uint8_t getCounter() const; + + /** + * Set the counter value + * @param[in] counter + */ + void setCounter(uint8_t counter); + + /** + * Get the eventgroup id + * @return uint32_t + */ + uint16_t getEventgroupId() const; + + /** + * Set the eventgroup id + * @param[in] eventgroupID + */ + void setEventgroupId(uint16_t eventgroupID); + + private: + /** + * These are the entry types used by SOME/IP-SD. They cannot be used for + * parameter passing since the values are not unique. + */ + enum class TypeInternal : uint8_t { + /** Find Service */ + FindService_Internal = 0x00, + /** Offer Service / Stop Offer Service */ + OfferService_Internal = 0x01, + /** Subscribe Eventgroup & Stop Subscribe Eventgroup */ + SubscribeEventgroup_Internal = 0x06, + /** Subscribe Eventgroup Acknowledgment / Negative Acknowledgement */ + SubscribeEventgroupAck_Internal = 0x07, + }; + + EntryType m_EntryType; + const SomeIpSdLayer* m_Layer; + size_t m_Offset; + uint8_t* m_ShadowData; + + void initStdFields(EntryType type, uint16_t serviceID, uint16_t instanceID, + uint8_t majorVersion, uint32_t TTL); + + SomeIpSdEntry(const SomeIpSdEntry&) = delete; + SomeIpSdEntry& operator=(const SomeIpSdEntry&) = delete; + + static const uint32_t SOMEIPSD_HDR_ENTRY_MASK_TTL = 0x00FFFFFF; }; /** * @class SomeIpSdLayer * Implementation of the SOME/IP-SD protocol */ -class SomeIpSdLayer : public SomeIpLayer -{ -public: - friend class SomeIpSdEntry; - - typedef SomeIpSdEntry* EntryPtr; - typedef std::vector EntriesVec; - typedef SomeIpSdOption* OptionPtr; - typedef std::vector OptionsVec; - - /** - * 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 - */ - SomeIpSdLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet); - - /** - * Construct a new SomeIpSdLayer object - * @param[in] serviceID Service ID - * @param[in] methodID Method ID - * @param[in] clientID Client ID - * @param[in] sessionID Session ID - * @param[in] interfaceVersion Interface Version - * @param[in] type Type of the message - * @param[in] returnCode Return Code - * @param[in] flags Flags that shall be used in the header - */ - SomeIpSdLayer(uint16_t serviceID, uint16_t methodID, uint16_t clientID, uint16_t sessionID, - uint8_t interfaceVersion, MsgType type, uint8_t returnCode, uint8_t flags); - - /** - * Destroy the layer object - */ - ~SomeIpSdLayer() {} - - /** - * Checks if given port is a SOME/IP-SD protocol port - * @param[in] port Port to check - * @return true if SOME/IP-SD protocol port, false if not - */ - static bool isSomeIpSdPort(uint16_t port) { return port == 30490; } - - /** - * The static method makes validation of input data - * @param[in] data The pointer to the beginning of byte stream of IP packet - * @param[in] dataLen The length of byte stream - * @return True if the data is valid and can represent the packet - */ - static bool isDataValid(const uint8_t* data, size_t dataLen); - - /** - * Get the Flags of the layer - * @return uint8_t Flags - */ - uint8_t getFlags() const; - - /** - * Set the Flags of the layer - * @param[in] flags Flags to set - */ - void setFlags(uint8_t flags); - - /** - * Get the number of entries in this layer - * @return uint32_t - */ - uint32_t getNumEntries() const; - - /** - * Get the number of options in this layer - * @return uint32_t - */ - uint32_t getNumOptions() const; - - /** - * Get the Entries from this layer - * @return EntriesVec Vector holding pointers to the options - */ - const EntriesVec getEntries() const; - - /** - * Get the Options from this layer - * @return OptionsVec Vector holding pointers to the options - */ - const OptionsVec getOptions() const; - - /** - * Get the Options from a specific Entry - * @param[in] index Index of the Entry, starting with 0. - * @return OptionsVec Vector holding pointers to the options - */ - const OptionsVec getOptionsFromEntry(uint32_t index) const; - - /** - * Adds a given entry to the layer and returns the index of the entry - * @param[in] entry Pointer to the entry that shall be added to the layer - * @return uint32_t Returns the index of the entry starting with 0 - */ - uint32_t addEntry(const SomeIpSdEntry &entry); - - /** - * Adds an option to an entry that has already been added to the layer by using addEntry(). The option - * is also added to the layer itself. If the option cannot by assigned to the entry, the option is not - * copied into the layer. - * @param[in] indexEntry Index of the entry where the option shall be added. First Entry has index 0 - * @param[in] option Pointer to the option that shall be added - * @return True if the option could be assigned to the entry and was copied into the layer, false otherwise - */ - bool addOptionTo(uint32_t indexEntry, const SomeIpSdOption &option); - - /** - * Does nothing for this layer - */ - void computeCalculateFields() {}; - - /** - * @return The string representation of the SOME/IP-SD layer - */ - std::string toString() const; - -private: - /** - * @struct someipsdhdr - * Represents an SOME/IP-SD protocol header - */ +class SomeIpSdLayer : public SomeIpLayer { + public: + friend class SomeIpSdEntry; + + typedef SomeIpSdEntry* EntryPtr; + typedef std::vector EntriesVec; + typedef SomeIpSdOption* OptionPtr; + typedef std::vector OptionsVec; + + /** + * 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 + */ + SomeIpSdLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet); + + /** + * Construct a new SomeIpSdLayer object + * @param[in] serviceID Service ID + * @param[in] methodID Method ID + * @param[in] clientID Client ID + * @param[in] sessionID Session ID + * @param[in] interfaceVersion Interface Version + * @param[in] type Type of the message + * @param[in] returnCode Return Code + * @param[in] flags Flags that shall be used in the header + */ + SomeIpSdLayer(uint16_t serviceID, uint16_t methodID, uint16_t clientID, + uint16_t sessionID, uint8_t interfaceVersion, MsgType type, + uint8_t returnCode, uint8_t flags); + + /** + * Destroy the layer object + */ + ~SomeIpSdLayer() {} + + /** + * Checks if given port is a SOME/IP-SD protocol port + * @param[in] port Port to check + * @return true if SOME/IP-SD protocol port, false if not + */ + static bool isSomeIpSdPort(uint16_t port) { return port == 30490; } + + /** + * The static method makes validation of input data + * @param[in] data The pointer to the beginning of byte stream of IP packet + * @param[in] dataLen The length of byte stream + * @return True if the data is valid and can represent the packet + */ + static bool isDataValid(const uint8_t* data, size_t dataLen); + + /** + * Get the Flags of the layer + * @return uint8_t Flags + */ + uint8_t getFlags() const; + + /** + * Set the Flags of the layer + * @param[in] flags Flags to set + */ + void setFlags(uint8_t flags); + + /** + * Get the number of entries in this layer + * @return uint32_t + */ + uint32_t getNumEntries() const; + + /** + * Get the number of options in this layer + * @return uint32_t + */ + uint32_t getNumOptions() const; + + /** + * Get the Entries from this layer + * @return EntriesVec Vector holding pointers to the options + */ + const EntriesVec getEntries() const; + + /** + * Get the Options from this layer + * @return OptionsVec Vector holding pointers to the options + */ + const OptionsVec getOptions() const; + + /** + * Get the Options from a specific Entry + * @param[in] index Index of the Entry, starting with 0. + * @return OptionsVec Vector holding pointers to the options + */ + const OptionsVec getOptionsFromEntry(uint32_t index) const; + + /** + * Adds a given entry to the layer and returns the index of the entry + * @param[in] entry Pointer to the entry that shall be added to the layer + * @return uint32_t Returns the index of the entry starting with 0 + */ + uint32_t addEntry(const SomeIpSdEntry& entry); + + /** + * Adds an option to an entry that has already been added to the layer by + * using addEntry(). The option is also added to the layer itself. If the + * option cannot by assigned to the entry, the option is not copied into the + * layer. + * @param[in] indexEntry Index of the entry where the option shall be added. + * First Entry has index 0 + * @param[in] option Pointer to the option that shall be added + * @return True if the option could be assigned to the entry and was copied + * into the layer, false otherwise + */ + bool addOptionTo(uint32_t indexEntry, const SomeIpSdOption& option); + + /** + * Does nothing for this layer + */ + void computeCalculateFields(){}; + + /** + * @return The string representation of the SOME/IP-SD layer + */ + std::string toString() const; + + private: + /** + * @struct someipsdhdr + * Represents an SOME/IP-SD protocol header + */ #pragma pack(push, 1) - struct someipsdhdr : someiphdr - { - /** Flags (8 bit) */ - uint8_t flags; - /** Reserved1 field (Bits 0-7 of 24-bits reserved field) */ - uint8_t reserved1; - /** Reserved2 field (Bits 8-15 of 24-bits reserved field) */ - uint8_t reserved2; - /** Reserved3 field (Bits 16-23 of 24-bits reserved field) */ - uint8_t reserved3; - }; + struct someipsdhdr : someiphdr { + /** Flags (8 bit) */ + uint8_t flags; + /** Reserved1 field (Bits 0-7 of 24-bits reserved field) */ + uint8_t reserved1; + /** Reserved2 field (Bits 8-15 of 24-bits reserved field) */ + uint8_t reserved2; + /** Reserved3 field (Bits 16-23 of 24-bits reserved field) */ + uint8_t reserved3; + }; #pragma pack(pop) - uint32_t m_NumOptions; + uint32_t m_NumOptions; - static bool countOptions(uint32_t& count, const uint8_t* data); - uint32_t findOption(const SomeIpSdOption &option); - void addOption(const SomeIpSdOption &option); - bool addOptionIndex(uint32_t indexEntry, uint32_t indexOffset); - OptionPtr parseOption(SomeIpSdOption::OptionType type, size_t offset) const; + static bool countOptions(uint32_t& count, const uint8_t* data); + uint32_t findOption(const SomeIpSdOption& option); + void addOption(const SomeIpSdOption& option); + bool addOptionIndex(uint32_t indexEntry, uint32_t indexOffset); + OptionPtr parseOption(SomeIpSdOption::OptionType type, size_t offset) const; - static size_t getLenEntries(const uint8_t* data); - size_t getLenEntries() const; - static size_t getLenOptions(const uint8_t* data); - size_t getLenOptions() const; - void setLenEntries(uint32_t length); - void setLenOptions(uint32_t length); + static size_t getLenEntries(const uint8_t* data); + size_t getLenEntries() const; + static size_t getLenOptions(const uint8_t* data); + size_t getLenOptions() const; + void setLenEntries(uint32_t length); + void setLenOptions(uint32_t length); }; } // namespace pcpp diff --git a/Packet++/header/StpLayer.h b/Packet++/header/StpLayer.h index 5953a2fd04..6955096d78 100644 --- a/Packet++/header/StpLayer.h +++ b/Packet++/header/StpLayer.h @@ -10,23 +10,21 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ +namespace pcpp { /** * @struct stp_tcn_bpdu * Represents payload of network changes announcements of BPDU */ #pragma pack(push, 1) - struct stp_tcn_bpdu - { - /// Protocol ID. Fixed at 0x0, which represents IEEE 802.1d - uint16_t protoId; - /// Protocol version. 0x0 for STP, 0x2 for RSTP, 0x3 for MSTP - uint8_t version; - /// Type of the BPDU. 0x0 for configuration, 0x2 for RSTP/MSTP, 0x80 for TCN - uint8_t type; - }; +struct stp_tcn_bpdu { + /// Protocol ID. Fixed at 0x0, which represents IEEE 802.1d + uint16_t protoId; + /// Protocol version. 0x0 for STP, 0x2 for RSTP, 0x3 for MSTP + uint8_t version; + /// Type of the BPDU. 0x0 for configuration, 0x2 for RSTP/MSTP, 0x80 for TCN + uint8_t type; +}; #pragma pack(pop) /// Spanning Tree protocol common header @@ -37,27 +35,26 @@ typedef stp_tcn_bpdu stp_header; * Represents payload configuration of BPDU for STP */ #pragma pack(push, 1) - struct stp_conf_bpdu : stp_tcn_bpdu - { - /// Flag for indicate purpose of BPDU - uint8_t flag; - /// Root bridge ID - uint64_t rootId; - /// Cost of path - uint32_t pathCost; - /// Bridge ID - uint64_t bridgeId; - /// Port ID - uint16_t portId; - /// Age of the BPDU - uint16_t msgAge; - /// Maximum age of the BPDU - uint16_t maxAge; - /// BPDU transmission interval - uint16_t helloTime; - /// Delay for STP - uint16_t forwardDelay; - }; +struct stp_conf_bpdu : stp_tcn_bpdu { + /// Flag for indicate purpose of BPDU + uint8_t flag; + /// Root bridge ID + uint64_t rootId; + /// Cost of path + uint32_t pathCost; + /// Bridge ID + uint64_t bridgeId; + /// Port ID + uint16_t portId; + /// Age of the BPDU + uint16_t msgAge; + /// Maximum age of the BPDU + uint16_t maxAge; + /// BPDU transmission interval + uint16_t helloTime; + /// Delay for STP + uint16_t forwardDelay; +}; #pragma pack(pop) /** @@ -65,11 +62,10 @@ typedef stp_tcn_bpdu stp_header; * Represents payload configuration of BPDU for Rapid STP (RSTP) */ #pragma pack(push, 1) - struct rstp_conf_bpdu : stp_conf_bpdu - { - /// Version1 length. The value is 0x0 - uint8_t version1Len; - }; +struct rstp_conf_bpdu : stp_conf_bpdu { + /// Version1 length. The value is 0x0 + uint8_t version1Len; +}; #pragma pack(pop) /** @@ -77,726 +73,761 @@ typedef stp_tcn_bpdu stp_header; * Represents payload configuration of BPDU for Multiple STP (MSTP) */ #pragma pack(push, 1) - struct mstp_conf_bpdu : rstp_conf_bpdu - { - /// Version3 length. - uint16_t version3Len; - /// Configuration id format selector - uint8_t mstConfigFormatSelector; - /// Configuration id name - uint8_t mstConfigName[32]; - /// Configuration id revision - uint16_t mstConfigRevision; - /// Configuration id digest - uint8_t mstConfigDigest[16]; - /// CIST internal root path cost - uint32_t irpc; - /// CIST bridge id - uint64_t cistBridgeId; - /// CIST remaining hop count - uint8_t remainId; - }; +struct mstp_conf_bpdu : rstp_conf_bpdu { + /// Version3 length. + uint16_t version3Len; + /// Configuration id format selector + uint8_t mstConfigFormatSelector; + /// Configuration id name + uint8_t mstConfigName[32]; + /// Configuration id revision + uint16_t mstConfigRevision; + /// Configuration id digest + uint8_t mstConfigDigest[16]; + /// CIST internal root path cost + uint32_t irpc; + /// CIST bridge id + uint64_t cistBridgeId; + /// CIST remaining hop count + uint8_t remainId; +}; #pragma pack(pop) /** * @struct msti_conf_msg - * Represents MSTI configuration messages. Each message contains 16 bytes and MSTP can contain 0 to 64 MSTI messages. + * Represents MSTI configuration messages. Each message contains 16 bytes and + * MSTP can contain 0 to 64 MSTI messages. */ #pragma pack(push, 1) - struct msti_conf_msg - { - /// MSTI flags - uint8_t flags; - /// Regional root switching id (Priority (4 bits) + ID (12 bits) + Regional root (48 bits - MAC address)) - uint64_t regionalRootId; - /// Total path cost from local port to regional port - uint32_t pathCost; - /// Priority value of switching device - uint8_t bridgePriority; - /// Priority value of port - uint8_t portPriority; - /// Remaining hops of BPDU - uint8_t remainingHops; - }; +struct msti_conf_msg { + /// MSTI flags + uint8_t flags; + /// Regional root switching id (Priority (4 bits) + ID (12 bits) + Regional + /// root (48 bits - MAC address)) + uint64_t regionalRootId; + /// Total path cost from local port to regional port + uint32_t pathCost; + /// Priority value of switching device + uint8_t bridgePriority; + /// Priority value of port + uint8_t portPriority; + /// Remaining hops of BPDU + uint8_t remainingHops; +}; #pragma pack(pop) - /** - * @class StpLayer - * Represents an Spanning Tree Protocol Layer - */ - class StpLayer : public Layer - { - protected: - StpLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) - : Layer(data, dataLen, prevLayer, packet) - { - m_Protocol = STP; - } - - explicit StpLayer(size_t dataLen) - { - m_DataLen = dataLen; - m_Data = new uint8_t[dataLen]; - memset(m_Data, 0, dataLen); - m_Protocol = STP; - } - - static pcpp::MacAddress IDtoMacAddress(uint64_t id); - static uint64_t macAddressToID(const pcpp::MacAddress &addr); - - public: - /// STP protocol uses "01:80:C2:00:00:00" multicast address as destination MAC - static pcpp::MacAddress StpMulticastDstMAC; - /// STP Uplink Fast protocol uses "01:00:0C:CD:CD:CD" as destination MAC - static pcpp::MacAddress StpUplinkFastMulticastDstMAC; - - /** - * Get a pointer to base Spanning tree header - * @return A pointer to spanning tree header - */ - stp_header *getStpHeader() const { return (stp_header *)(m_Data); } - - /** - * Returns the protocol id. Fixed at 0x0 for STP messages which represents IEEE 802.1d - * @return ID of the protocol - */ - uint16_t getProtoId() const { return getStpHeader()->protoId; } - - /** - * Sets the protocol id - * @param[in] value ID of the protocol - */ - void setProtoId(uint16_t value) { getStpHeader()->protoId = value; } - - /** - * Returns the version. Fixed at 0x0 for STP messages - * @return Version number - */ - uint8_t getVersion() const { return getStpHeader()->version; } - - /** - * Sets the version - * @param[in] value Version number - */ - void setVersion(uint8_t value) { getStpHeader()->version = value; } - - /** - * Returns the type of configuration message. - * @return Type of configuration message - */ - uint8_t getType() const { return getStpHeader()->type; } - - /** - * Sets the type of configuration message - * @param[in] value Type of configuration message - */ - void setType(uint8_t value) { getStpHeader()->type = value; } - - // overridden methods - - /** - * @return The size of STP packet - */ - size_t getHeaderLen() const { return m_DataLen; } - - /// Does nothing for this layer - void computeCalculateFields() {} - - /** - * @return The OSI layer level of STP (Data Link Layer). - */ - OsiModelLayer getOsiModelLayer() const { return OsiModelDataLinkLayer; } - - /** - * A static method that validates the input data - * @param[in] data The pointer to the beginning of a byte stream of an Spanning Tree packet - * @param[in] dataLen The length of the byte stream - * @return True if the data is valid and can represent an Spanning Tree packet - */ - static bool isDataValid(const uint8_t *data, size_t dataLen); - - /** - * A method to create STP layer from existing packet - * @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 - * @return A newly allocated STP layer of one of the following types (according to the message type): - * StpConfigurationBPDULayer, StpTopologyChangeBPDULayer, RapidStpLayer, MultipleStpLayer - */ - static StpLayer *parseStpLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet); - }; - - /** - * @class StpTopologyChangeBPDULayer - * Represents network topology change BPDU message of Spanning Tree Protocol - */ - class StpTopologyChangeBPDULayer : public StpLayer - { - protected: - explicit StpTopologyChangeBPDULayer(size_t dataLen) : StpLayer(dataLen) {} - - public: - /** - * 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 - */ - StpTopologyChangeBPDULayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) - : StpLayer(data, dataLen, prevLayer, packet) - { - } - - /** - * Empty c'tor to create a new network topology change (TCN) BPDU layer. - * Initializes the protocol identifier, version and STP type fields with correct values - */ - StpTopologyChangeBPDULayer(); - - /** - * Get a pointer to network topology change (TCN) BPDU message - * @return A pointer to TCN BPDU message - */ - stp_tcn_bpdu* getStpTcnHeader() { return getStpHeader(); } - - // overridden methods - - /** - * @return The size of STP TCN message - */ - size_t getHeaderLen() const { return sizeof(stp_tcn_bpdu); } - - /// Parses next layer - void parseNextLayer(); - - /** - * @return Returns the protocol info as readable string - */ - std::string toString() const { return "Spanning Tree Topology Change Notification"; } - - /** - * A static method that validates the input data - * @param[in] data The pointer to the beginning of a byte stream of an Spanning Tree Topology Change BPDU packet - * @param[in] dataLen The length of the byte stream - * @return True if the data is valid and can represent an Spanning Tree packet - */ - static bool isDataValid(const uint8_t *data, size_t dataLen) { return data && dataLen >= sizeof(stp_tcn_bpdu); } - }; - - /** - * @class StpConfigurationBPDULayer - * Represents configuration BPDU message of Spanning Tree Protocol - */ - class StpConfigurationBPDULayer : public StpTopologyChangeBPDULayer - { - protected: - explicit StpConfigurationBPDULayer(size_t dataLen) : StpTopologyChangeBPDULayer(dataLen) {} - - public: - /** - * 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 - */ - StpConfigurationBPDULayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) - : StpTopologyChangeBPDULayer(data, dataLen, prevLayer, packet) - { - } - - /** - * Empty c'tor to create a new configuration BPDU layer. - * Initializes the protocol identifier, version and STP type fields with correct values - */ - StpConfigurationBPDULayer(); - - /** - * Get a pointer to configuration BPDU message - * @return A pointer to configuration BPDU message - */ - stp_conf_bpdu *getStpConfHeader() const { return (stp_conf_bpdu *)(m_Data); } - - /** - * Returns the flags of configuration message which indicates purpose of BPDU - * @return Flags of the configuration message - */ - uint8_t getFlag() const { return getStpConfHeader()->flag; } - - /** - * Returns the flags of configuration message which indicates purpose of BPDU - * @param[in] value Flags of the configuration message - */ - void setFlag(uint8_t value) { getStpConfHeader()->flag = value; } - - /** - * Returns the root bridge identifier - * @return Root bridge identifier - */ - uint64_t getRootId() const; - - /** - * Sets the root bridge identifier - * @param[in] value Root bridge identifier - */ - void setRootId(uint64_t value); - - /** - * Returns the priority of root bridge - * @return Priority of root bridge - */ - uint16_t getRootPriority() const; - - /** - * Sets the priority of root bridge - * @param[in] value Priority of root bridge - */ - void setRootPriority(uint16_t value); - - /** - * Returns the system identifier extension of root bridge - * @return System extension of root bridge - */ - uint16_t getRootSystemIDExtension() const; - - /** - * Sets the system identifier extension of root bridge - * @param[in] value System extension of root bridge - */ - void setRootSystemIDExtension(uint16_t value); - - /** - * Returns the system identifier of root bridge - * @return System identifier of root bridge - */ - pcpp::MacAddress getRootSystemID() const { return IDtoMacAddress(getRootId()); } - - /** - * Sets the system identifier of root bridge - * @param[in] value System identifier of root bridge - */ - void setRootSystemID(const pcpp::MacAddress &value); - - /** - * Returns the value of the cost of path - * @return Cost of path - */ - uint32_t getPathCost() const; - - /** - * Sets the value of the cost of path - * @param[in] value Cost of path - */ - void setPathCost(uint32_t value); - - /** - * Returns the bridge identifier - * @return Bridge identifier - */ - uint64_t getBridgeId() const; - - /** - * Sets the bridge identifier - * @param[in] value Bridge identifier - */ - void setBridgeId(uint64_t value); - - /** - * Returns the priority of bridge - * @return Priority of bridge - */ - uint16_t getBridgePriority() const; - - /** - * Sets the priority of bridge - * @param[in] value Priority of bridge - */ - void setBridgePriority(uint16_t value); - - /** - * Returns the system identifier extension of bridge - * @return System extension of bridge - */ - uint16_t getBridgeSystemIDExtension() const; - - /** - * Sets the system identifier extension of bridge - * @param[in] value System extension of bridge - */ - void setBridgeSystemIDExtension(uint16_t value); - - /** - * Returns the system identifier of bridge - * @return System identifier of bridge - */ - pcpp::MacAddress getBridgeSystemID() const { return IDtoMacAddress(getBridgeId()); } - - /** - * Sets the system identifier of bridge - * @param[in] value System identifier of bridge - */ - void setBridgeSystemID(const pcpp::MacAddress &value); - - /** - * Returns the port identifier - * @return Port identifier - */ - uint16_t getPortId() const; - - /** - * Sets the port identifier - * @param[in] value Port identifier - */ - void setPortId(uint16_t value); - - /** - * Returns age of the BPDU message - * @return Age of BPDU in seconds - */ - double getMessageAge() const; - - /** - * Sets age of the BPDU message - * @param[in] value Age of BPDU in seconds - */ - void setMessageAge(double value); - - /** - * Returns maximum age of the BPDU message - * @return Maximum age of BPDU in seconds - */ - double getMaximumAge() const; - - /** - * Sets maximum age of the BPDU message - * @param[in] value Maximum age of BPDU in seconds - */ - void setMaximumAge(double value); - - /** - * Returns the BPDU transmission interval - * @return Value of the transmission interval in seconds - */ - double getTransmissionInterval() const; - - /** - * Sets the BPDU transmission interval - * @param[in] value Value of the transmission interval in seconds - */ - void setTransmissionInterval(double value); - - /** - * Returns the delay for STP message - * @return Value of the forward delay in seconds - */ - double getForwardDelay() const; - - /** - * Sets the delay for STP message - * @param[in] value Value of the forward delay in seconds - */ - void setForwardDelay(double value); - - // overridden methods - - /** - * @return The size of STP configuration BPDU message - */ - size_t getHeaderLen() const { return sizeof(stp_conf_bpdu); } - - /// Parses next layer - void parseNextLayer(); - - /** - * @return Returns the protocol info as readable string - */ - std::string toString() const { return "Spanning Tree Configuration"; } - - /** - * A static method that validates the input data - * @param[in] data The pointer to the beginning of a byte stream of an Spanning Tree Configuration BPDU packet - * @param[in] dataLen The length of the byte stream - * @return True if the data is valid and can represent an Spanning Tree packet - */ - static bool isDataValid(const uint8_t *data, size_t dataLen) - { - return data && dataLen >= sizeof(stp_conf_bpdu); - } - }; - - /** - * @class RapidStpLayer - * Represents Rapid Spanning Tree Protocol (RSTP) - */ - class RapidStpLayer : public StpConfigurationBPDULayer - { - protected: - explicit RapidStpLayer(size_t dataLen) : StpConfigurationBPDULayer(dataLen) {} - - public: - /** - * 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 - */ - RapidStpLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) - : StpConfigurationBPDULayer(data, dataLen, prevLayer, packet) - { - } - - /** - * Empty c'tor to create a new Rapid STP layer. - * Initializes the protocol identifier, version and STP type fields with correct values - */ - RapidStpLayer(); - - /** - * Get a pointer to Rapid STP header - * @return A pointer to Rapid STP header - */ - rstp_conf_bpdu *getRstpConfHeader() const { return (rstp_conf_bpdu *)(m_Data); } - - /** - * Returns the length of version1 field. Fixed at 0x0 for Rapid STP - * @return Length of the version1 field - */ - uint8_t getVersion1Len() const { return getRstpConfHeader()->version1Len; } - - /** - * Returns the length of version1 field - * @param[in] value Length of the version1 field - */ - void setVersion1Len(uint8_t value) { getRstpConfHeader()->version1Len = value; } - - // overridden methods - - /** - * @return The size of Rapid STP message - */ - size_t getHeaderLen() const { return sizeof(rstp_conf_bpdu); } - - /// Parses next layer - void parseNextLayer(); - - /** - * @return Returns the protocol info as readable string - */ - std::string toString() const { return "Rapid Spanning Tree"; } - - /** - * A static method that validates the input data - * @param[in] data The pointer to the beginning of a byte stream of an Rapid STP packet - * @param[in] dataLen The length of the byte stream - * @return True if the data is valid and can represent an Spanning Tree packet - */ - static bool isDataValid(const uint8_t *data, size_t dataLen) - { - return data && dataLen >= sizeof(rstp_conf_bpdu); - } - }; - - /** - * @class MultipleStpLayer - * Represents Multiple Spanning Tree Protocol (MSTP). It has limited capabilities (no crafting / limited editing) over MSTI configuration - */ - class MultipleStpLayer : public RapidStpLayer - { - public: - /** - * 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 - */ - MultipleStpLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) - : RapidStpLayer(data, dataLen, prevLayer, packet) - { - } - - /** - * Empty c'tor to create a new Multiple STP layer. - * Initializes the protocol identifier, version and STP type fields with correct values - */ - MultipleStpLayer(); - - /** - * Get a pointer to Multiple STP header - * @return A pointer to Multiple STP header - */ - mstp_conf_bpdu *getMstpHeader() const { return (mstp_conf_bpdu *)(m_Data); } - - /** - * @return Length of version3 field - */ - uint16_t getVersion3Len() const; - - /** - * Sets the length of version3 field - * @param[in] value Length of version3 field - */ - void setVersion3Len(uint16_t value); - - /** - * Returns the configuration ID format selector - * @return Configuration ID of format selector - */ - uint8_t getMstConfigurationFormatSelector() const { return getMstpHeader()->mstConfigFormatSelector; } - - /** - * Sets the configuration ID format selector - * @param[in] value Configuration ID of format selector - */ - void setMstConfigurationFormatSelector(uint8_t value) { getMstpHeader()->mstConfigFormatSelector = value; } - - /** - * Returns the pointer to configuration name field - * @return Configuration name - */ - std::string getMstConfigurationName() const; - - /** - * Sets the configuration name field - * @param[in] value Configuration name. Length should be less than 32, if longer value provided first 32 - * characters are used - */ - void setMstConfigurationName(const std::string &value); - - /** - * Returns the revision of configuration ID - * @return Revision of configuration ID - */ - uint16_t getMstConfigRevision() const; - - /** - * Sets the revision of configuration ID - * @param[in] value Revision of configuration ID - */ - void setMstConfigRevision(uint16_t value); - - /** - * Returns the pointer to configuration message digest. The field itself always 16 bytes long. - * @return A pointer to configuration digest - */ - uint8_t *getMstConfigDigest() const { return getMstpHeader()->mstConfigDigest; } - - /** - * Sets the pointer to configuration message digest. The field itself always 16 bytes long. - * @param[in] value Pointer to digest - * @param[in] len Length of the digest, should be less than 16. If longer first 16 bytes are used - */ - void setMstConfigDigest(const uint8_t *value, uint8_t len); - - /** - * Returns CIST internal root path cost - * @return Value of the internal root path cost - */ - uint32_t getCISTIrpc() const; - - /** - * Sets CIST internal root path cost - * @param[in] value Value of the internal root path cost - */ - void setCISTIrpc(uint32_t value); - - /** - * Returns CIST bridge identifier - * @return Value of the bridge identifier - */ - uint64_t getCISTBridgeId() const; - - /** - * Sets CIST bridge identifier - * @param[in] value Value of the bridge identifier - */ - void setCISTBridgeId(uint64_t value); - - /** - * Returns the priority of CIST bridge - * @return Priority of CIST bridge - */ - uint16_t getCISTBridgePriority() const; - - /** - * Sets the priority of CIST bridge - * @param[in] value Priority of CIST bridge - */ - void setCISTBridgePriority(uint16_t value); - - /** - * Returns the system identifier extension of CIST bridge - * @return System extension of CIST bridge - */ - uint16_t getCISTBridgeSystemIDExtension() const; - - /** - * Sets the system identifier extension of CIST bridge - * @param[in] value System extension of CIST bridge - */ - void setCISTBridgeSystemIDExtension(uint16_t value); - - /** - * Returns the system identifier of CIST bridge - * @return System identifier of CIST bridge - */ - pcpp::MacAddress getCISTBridgeSystemID() const { return IDtoMacAddress(getCISTBridgeId()); } - - /** - * Sets the system identifier of CIST bridge - * @param[in] value System identifier of CIST bridge - */ - void setCISTBridgeSystemID(const pcpp::MacAddress &value); - - /** - * Returns the remaining hop count - * @return Value of remaining hop count - */ - uint8_t getRemainingHopCount() const { return getMstpHeader()->remainId; } - - /** - * Returns the remaining hop count - * @param[in] value Value of remaining hop count - */ - void setRemainingHopCount(uint8_t value) { getMstpHeader()->remainId = value; } - - /** - * Returns the total number of MSTI configuration messages - * @return Number of MSTI configuration messages. Can be between 0 and 64. - */ - uint8_t getNumberOfMSTIConfMessages() const { return (getVersion3Len() - (sizeof(mstp_conf_bpdu) - sizeof(rstp_conf_bpdu) - sizeof(uint16_t))) / sizeof(msti_conf_msg); } - - /** - * Returns a reference to MSTI configuration messages. An MSTP packet can contain between 0 to 64 MSTI messages. - * The number of messages can be obtained by using getNumberOfMSTIConfMessages() - * @return An array pointer to MSTI configuration messages. Returns nullptr if there is no MSTI message. - */ - msti_conf_msg *getMstiConfMessages() const; - - // overridden methods - - /// Parses next layer - void parseNextLayer() {} - - /** - * @return Returns the protocol info as readable string - */ - std::string toString() const { return "Multiple Spanning Tree"; } - - /** - * A static method that validates the input data - * @param[in] data The pointer to the beginning of a byte stream of an Multiple STP packet - * @param[in] dataLen The length of the byte stream - * @return True if the data is valid and can represent an Spanning Tree packet - */ - static bool isDataValid(const uint8_t *data, size_t dataLen) - { - return data && dataLen >= sizeof(mstp_conf_bpdu); - } - }; +/** + * @class StpLayer + * Represents an Spanning Tree Protocol Layer + */ +class StpLayer : public Layer { + protected: + StpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : Layer(data, dataLen, prevLayer, packet) { + m_Protocol = STP; + } + + explicit StpLayer(size_t dataLen) { + m_DataLen = dataLen; + m_Data = new uint8_t[dataLen]; + memset(m_Data, 0, dataLen); + m_Protocol = STP; + } + + static pcpp::MacAddress IDtoMacAddress(uint64_t id); + static uint64_t macAddressToID(const pcpp::MacAddress& addr); + + public: + /// STP protocol uses "01:80:C2:00:00:00" multicast address as destination MAC + static pcpp::MacAddress StpMulticastDstMAC; + /// STP Uplink Fast protocol uses "01:00:0C:CD:CD:CD" as destination MAC + static pcpp::MacAddress StpUplinkFastMulticastDstMAC; + + /** + * Get a pointer to base Spanning tree header + * @return A pointer to spanning tree header + */ + stp_header* getStpHeader() const { return (stp_header*)(m_Data); } + + /** + * Returns the protocol id. Fixed at 0x0 for STP messages which represents + * IEEE 802.1d + * @return ID of the protocol + */ + uint16_t getProtoId() const { return getStpHeader()->protoId; } + + /** + * Sets the protocol id + * @param[in] value ID of the protocol + */ + void setProtoId(uint16_t value) { getStpHeader()->protoId = value; } + + /** + * Returns the version. Fixed at 0x0 for STP messages + * @return Version number + */ + uint8_t getVersion() const { return getStpHeader()->version; } + + /** + * Sets the version + * @param[in] value Version number + */ + void setVersion(uint8_t value) { getStpHeader()->version = value; } + + /** + * Returns the type of configuration message. + * @return Type of configuration message + */ + uint8_t getType() const { return getStpHeader()->type; } + + /** + * Sets the type of configuration message + * @param[in] value Type of configuration message + */ + void setType(uint8_t value) { getStpHeader()->type = value; } + + // overridden methods + + /** + * @return The size of STP packet + */ + size_t getHeaderLen() const { return m_DataLen; } + + /// Does nothing for this layer + void computeCalculateFields() {} + + /** + * @return The OSI layer level of STP (Data Link Layer). + */ + OsiModelLayer getOsiModelLayer() const { return OsiModelDataLinkLayer; } + + /** + * A static method that validates the input data + * @param[in] data The pointer to the beginning of a byte stream of an + * Spanning Tree packet + * @param[in] dataLen The length of the byte stream + * @return True if the data is valid and can represent an Spanning Tree packet + */ + static bool isDataValid(const uint8_t* data, size_t dataLen); + + /** + * A method to create STP layer from existing packet + * @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 + * @return A newly allocated STP layer of one of the following types + * (according to the message type): StpConfigurationBPDULayer, + * StpTopologyChangeBPDULayer, RapidStpLayer, MultipleStpLayer + */ + static StpLayer* parseStpLayer(uint8_t* data, size_t dataLen, + Layer* prevLayer, Packet* packet); +}; + +/** + * @class StpTopologyChangeBPDULayer + * Represents network topology change BPDU message of Spanning Tree Protocol + */ +class StpTopologyChangeBPDULayer : public StpLayer { + protected: + explicit StpTopologyChangeBPDULayer(size_t dataLen) : StpLayer(dataLen) {} + + public: + /** + * 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 + */ + StpTopologyChangeBPDULayer(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet) + : StpLayer(data, dataLen, prevLayer, packet) {} + + /** + * Empty c'tor to create a new network topology change (TCN) BPDU layer. + * Initializes the protocol identifier, version and STP type fields with + * correct values + */ + StpTopologyChangeBPDULayer(); + + /** + * Get a pointer to network topology change (TCN) BPDU message + * @return A pointer to TCN BPDU message + */ + stp_tcn_bpdu* getStpTcnHeader() { return getStpHeader(); } + + // overridden methods + + /** + * @return The size of STP TCN message + */ + size_t getHeaderLen() const { return sizeof(stp_tcn_bpdu); } + + /// Parses next layer + void parseNextLayer(); + + /** + * @return Returns the protocol info as readable string + */ + std::string toString() const { + return "Spanning Tree Topology Change Notification"; + } + + /** + * A static method that validates the input data + * @param[in] data The pointer to the beginning of a byte stream of an + * Spanning Tree Topology Change BPDU packet + * @param[in] dataLen The length of the byte stream + * @return True if the data is valid and can represent an Spanning Tree packet + */ + static bool isDataValid(const uint8_t* data, size_t dataLen) { + return data && dataLen >= sizeof(stp_tcn_bpdu); + } +}; + +/** + * @class StpConfigurationBPDULayer + * Represents configuration BPDU message of Spanning Tree Protocol + */ +class StpConfigurationBPDULayer : public StpTopologyChangeBPDULayer { + protected: + explicit StpConfigurationBPDULayer(size_t dataLen) + : StpTopologyChangeBPDULayer(dataLen) {} + + public: + /** + * 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 + */ + StpConfigurationBPDULayer(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet) + : StpTopologyChangeBPDULayer(data, dataLen, prevLayer, packet) {} + + /** + * Empty c'tor to create a new configuration BPDU layer. + * Initializes the protocol identifier, version and STP type fields with + * correct values + */ + StpConfigurationBPDULayer(); + + /** + * Get a pointer to configuration BPDU message + * @return A pointer to configuration BPDU message + */ + stp_conf_bpdu* getStpConfHeader() const { return (stp_conf_bpdu*)(m_Data); } + + /** + * Returns the flags of configuration message which indicates purpose of BPDU + * @return Flags of the configuration message + */ + uint8_t getFlag() const { return getStpConfHeader()->flag; } + + /** + * Returns the flags of configuration message which indicates purpose of BPDU + * @param[in] value Flags of the configuration message + */ + void setFlag(uint8_t value) { getStpConfHeader()->flag = value; } + + /** + * Returns the root bridge identifier + * @return Root bridge identifier + */ + uint64_t getRootId() const; + + /** + * Sets the root bridge identifier + * @param[in] value Root bridge identifier + */ + void setRootId(uint64_t value); + + /** + * Returns the priority of root bridge + * @return Priority of root bridge + */ + uint16_t getRootPriority() const; + + /** + * Sets the priority of root bridge + * @param[in] value Priority of root bridge + */ + void setRootPriority(uint16_t value); + + /** + * Returns the system identifier extension of root bridge + * @return System extension of root bridge + */ + uint16_t getRootSystemIDExtension() const; + + /** + * Sets the system identifier extension of root bridge + * @param[in] value System extension of root bridge + */ + void setRootSystemIDExtension(uint16_t value); + + /** + * Returns the system identifier of root bridge + * @return System identifier of root bridge + */ + pcpp::MacAddress getRootSystemID() const { + return IDtoMacAddress(getRootId()); + } + + /** + * Sets the system identifier of root bridge + * @param[in] value System identifier of root bridge + */ + void setRootSystemID(const pcpp::MacAddress& value); + + /** + * Returns the value of the cost of path + * @return Cost of path + */ + uint32_t getPathCost() const; + + /** + * Sets the value of the cost of path + * @param[in] value Cost of path + */ + void setPathCost(uint32_t value); + + /** + * Returns the bridge identifier + * @return Bridge identifier + */ + uint64_t getBridgeId() const; + + /** + * Sets the bridge identifier + * @param[in] value Bridge identifier + */ + void setBridgeId(uint64_t value); + + /** + * Returns the priority of bridge + * @return Priority of bridge + */ + uint16_t getBridgePriority() const; + + /** + * Sets the priority of bridge + * @param[in] value Priority of bridge + */ + void setBridgePriority(uint16_t value); + + /** + * Returns the system identifier extension of bridge + * @return System extension of bridge + */ + uint16_t getBridgeSystemIDExtension() const; + + /** + * Sets the system identifier extension of bridge + * @param[in] value System extension of bridge + */ + void setBridgeSystemIDExtension(uint16_t value); + + /** + * Returns the system identifier of bridge + * @return System identifier of bridge + */ + pcpp::MacAddress getBridgeSystemID() const { + return IDtoMacAddress(getBridgeId()); + } + + /** + * Sets the system identifier of bridge + * @param[in] value System identifier of bridge + */ + void setBridgeSystemID(const pcpp::MacAddress& value); + + /** + * Returns the port identifier + * @return Port identifier + */ + uint16_t getPortId() const; + + /** + * Sets the port identifier + * @param[in] value Port identifier + */ + void setPortId(uint16_t value); + + /** + * Returns age of the BPDU message + * @return Age of BPDU in seconds + */ + double getMessageAge() const; + + /** + * Sets age of the BPDU message + * @param[in] value Age of BPDU in seconds + */ + void setMessageAge(double value); + + /** + * Returns maximum age of the BPDU message + * @return Maximum age of BPDU in seconds + */ + double getMaximumAge() const; + + /** + * Sets maximum age of the BPDU message + * @param[in] value Maximum age of BPDU in seconds + */ + void setMaximumAge(double value); + + /** + * Returns the BPDU transmission interval + * @return Value of the transmission interval in seconds + */ + double getTransmissionInterval() const; + + /** + * Sets the BPDU transmission interval + * @param[in] value Value of the transmission interval in seconds + */ + void setTransmissionInterval(double value); + + /** + * Returns the delay for STP message + * @return Value of the forward delay in seconds + */ + double getForwardDelay() const; + + /** + * Sets the delay for STP message + * @param[in] value Value of the forward delay in seconds + */ + void setForwardDelay(double value); + + // overridden methods + + /** + * @return The size of STP configuration BPDU message + */ + size_t getHeaderLen() const { return sizeof(stp_conf_bpdu); } + + /// Parses next layer + void parseNextLayer(); + + /** + * @return Returns the protocol info as readable string + */ + std::string toString() const { return "Spanning Tree Configuration"; } + + /** + * A static method that validates the input data + * @param[in] data The pointer to the beginning of a byte stream of an + * Spanning Tree Configuration BPDU packet + * @param[in] dataLen The length of the byte stream + * @return True if the data is valid and can represent an Spanning Tree packet + */ + static bool isDataValid(const uint8_t* data, size_t dataLen) { + return data && dataLen >= sizeof(stp_conf_bpdu); + } +}; + +/** + * @class RapidStpLayer + * Represents Rapid Spanning Tree Protocol (RSTP) + */ +class RapidStpLayer : public StpConfigurationBPDULayer { + protected: + explicit RapidStpLayer(size_t dataLen) : StpConfigurationBPDULayer(dataLen) {} + + public: + /** + * 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 + */ + RapidStpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : StpConfigurationBPDULayer(data, dataLen, prevLayer, packet) {} + + /** + * Empty c'tor to create a new Rapid STP layer. + * Initializes the protocol identifier, version and STP type fields with + * correct values + */ + RapidStpLayer(); + + /** + * Get a pointer to Rapid STP header + * @return A pointer to Rapid STP header + */ + rstp_conf_bpdu* getRstpConfHeader() const { + return (rstp_conf_bpdu*)(m_Data); + } + + /** + * Returns the length of version1 field. Fixed at 0x0 for Rapid STP + * @return Length of the version1 field + */ + uint8_t getVersion1Len() const { return getRstpConfHeader()->version1Len; } + + /** + * Returns the length of version1 field + * @param[in] value Length of the version1 field + */ + void setVersion1Len(uint8_t value) { + getRstpConfHeader()->version1Len = value; + } + + // overridden methods + + /** + * @return The size of Rapid STP message + */ + size_t getHeaderLen() const { return sizeof(rstp_conf_bpdu); } + + /// Parses next layer + void parseNextLayer(); + + /** + * @return Returns the protocol info as readable string + */ + std::string toString() const { return "Rapid Spanning Tree"; } + + /** + * A static method that validates the input data + * @param[in] data The pointer to the beginning of a byte stream of an Rapid + * STP packet + * @param[in] dataLen The length of the byte stream + * @return True if the data is valid and can represent an Spanning Tree packet + */ + static bool isDataValid(const uint8_t* data, size_t dataLen) { + return data && dataLen >= sizeof(rstp_conf_bpdu); + } +}; + +/** + * @class MultipleStpLayer + * Represents Multiple Spanning Tree Protocol (MSTP). It has limited + * capabilities (no crafting / limited editing) over MSTI configuration + */ +class MultipleStpLayer : public RapidStpLayer { + public: + /** + * 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 + */ + MultipleStpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet) + : RapidStpLayer(data, dataLen, prevLayer, packet) {} + + /** + * Empty c'tor to create a new Multiple STP layer. + * Initializes the protocol identifier, version and STP type fields with + * correct values + */ + MultipleStpLayer(); + + /** + * Get a pointer to Multiple STP header + * @return A pointer to Multiple STP header + */ + mstp_conf_bpdu* getMstpHeader() const { return (mstp_conf_bpdu*)(m_Data); } + + /** + * @return Length of version3 field + */ + uint16_t getVersion3Len() const; + + /** + * Sets the length of version3 field + * @param[in] value Length of version3 field + */ + void setVersion3Len(uint16_t value); + + /** + * Returns the configuration ID format selector + * @return Configuration ID of format selector + */ + uint8_t getMstConfigurationFormatSelector() const { + return getMstpHeader()->mstConfigFormatSelector; + } + + /** + * Sets the configuration ID format selector + * @param[in] value Configuration ID of format selector + */ + void setMstConfigurationFormatSelector(uint8_t value) { + getMstpHeader()->mstConfigFormatSelector = value; + } + + /** + * Returns the pointer to configuration name field + * @return Configuration name + */ + std::string getMstConfigurationName() const; + + /** + * Sets the configuration name field + * @param[in] value Configuration name. Length should be less than 32, if + * longer value provided first 32 characters are used + */ + void setMstConfigurationName(const std::string& value); + + /** + * Returns the revision of configuration ID + * @return Revision of configuration ID + */ + uint16_t getMstConfigRevision() const; + + /** + * Sets the revision of configuration ID + * @param[in] value Revision of configuration ID + */ + void setMstConfigRevision(uint16_t value); + + /** + * Returns the pointer to configuration message digest. The field itself + * always 16 bytes long. + * @return A pointer to configuration digest + */ + uint8_t* getMstConfigDigest() const { + return getMstpHeader()->mstConfigDigest; + } + + /** + * Sets the pointer to configuration message digest. The field itself always + * 16 bytes long. + * @param[in] value Pointer to digest + * @param[in] len Length of the digest, should be less than 16. If longer + * first 16 bytes are used + */ + void setMstConfigDigest(const uint8_t* value, uint8_t len); + + /** + * Returns CIST internal root path cost + * @return Value of the internal root path cost + */ + uint32_t getCISTIrpc() const; + + /** + * Sets CIST internal root path cost + * @param[in] value Value of the internal root path cost + */ + void setCISTIrpc(uint32_t value); + + /** + * Returns CIST bridge identifier + * @return Value of the bridge identifier + */ + uint64_t getCISTBridgeId() const; + + /** + * Sets CIST bridge identifier + * @param[in] value Value of the bridge identifier + */ + void setCISTBridgeId(uint64_t value); + + /** + * Returns the priority of CIST bridge + * @return Priority of CIST bridge + */ + uint16_t getCISTBridgePriority() const; + + /** + * Sets the priority of CIST bridge + * @param[in] value Priority of CIST bridge + */ + void setCISTBridgePriority(uint16_t value); + + /** + * Returns the system identifier extension of CIST bridge + * @return System extension of CIST bridge + */ + uint16_t getCISTBridgeSystemIDExtension() const; + + /** + * Sets the system identifier extension of CIST bridge + * @param[in] value System extension of CIST bridge + */ + void setCISTBridgeSystemIDExtension(uint16_t value); + + /** + * Returns the system identifier of CIST bridge + * @return System identifier of CIST bridge + */ + pcpp::MacAddress getCISTBridgeSystemID() const { + return IDtoMacAddress(getCISTBridgeId()); + } + + /** + * Sets the system identifier of CIST bridge + * @param[in] value System identifier of CIST bridge + */ + void setCISTBridgeSystemID(const pcpp::MacAddress& value); + + /** + * Returns the remaining hop count + * @return Value of remaining hop count + */ + uint8_t getRemainingHopCount() const { return getMstpHeader()->remainId; } + + /** + * Returns the remaining hop count + * @param[in] value Value of remaining hop count + */ + void setRemainingHopCount(uint8_t value) { + getMstpHeader()->remainId = value; + } + + /** + * Returns the total number of MSTI configuration messages + * @return Number of MSTI configuration messages. Can be between 0 and 64. + */ + uint8_t getNumberOfMSTIConfMessages() const { + return (getVersion3Len() - (sizeof(mstp_conf_bpdu) - + sizeof(rstp_conf_bpdu) - sizeof(uint16_t))) / + sizeof(msti_conf_msg); + } + + /** + * Returns a reference to MSTI configuration messages. An MSTP packet can + * contain between 0 to 64 MSTI messages. The number of messages can be + * obtained by using getNumberOfMSTIConfMessages() + * @return An array pointer to MSTI configuration messages. Returns nullptr if + * there is no MSTI message. + */ + msti_conf_msg* getMstiConfMessages() const; + + // overridden methods + + /// Parses next layer + void parseNextLayer() {} + + /** + * @return Returns the protocol info as readable string + */ + std::string toString() const { return "Multiple Spanning Tree"; } + + /** + * A static method that validates the input data + * @param[in] data The pointer to the beginning of a byte stream of an + * Multiple STP packet + * @param[in] dataLen The length of the byte stream + * @return True if the data is valid and can represent an Spanning Tree packet + */ + static bool isDataValid(const uint8_t* data, size_t dataLen) { + return data && dataLen >= sizeof(mstp_conf_bpdu); + } +}; } // namespace pcpp #endif /* PACKETPP_STP_LAYER */ diff --git a/Packet++/header/TLVData.h b/Packet++/header/TLVData.h index 84072931ca..a6268684fe 100644 --- a/Packet++/header/TLVData.h +++ b/Packet++/header/TLVData.h @@ -1,8 +1,8 @@ #ifndef PACKETPP_TLV_DATA #define PACKETPP_TLV_DATA -#include "Layer.h" #include "IpAddress.h" +#include "Layer.h" #include /// @file @@ -11,423 +11,425 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - /** - * @class TLVRecord - * A wrapper class for a Type-Length-Value (TLV) record. This class does not create or modify TLV records, but rather - * serves as a wrapper and provides useful methods for retrieving data from them. This class has several abstract methods - * that should be implemented in derived classes. These methods are for record length value calculation (the 'L' in TLV) - * which is implemented differently in different protocols - */ - template - class TLVRecord - { - protected: - - /** A struct representing the TLV construct */ - struct TLVRawData - { - /** Record type */ - TRecType recordType; - /** Record length in bytes */ - TRecLen recordLen; - /** Record value (variable size) */ - uint8_t recordValue[]; - }; - - TLVRawData* m_Data; - - public: - - /** - * A c'tor for this class that gets a pointer to the TLV record raw data (byte array) - * @param[in] recordRawData A pointer to the TLV record raw data - */ - TLVRecord(uint8_t* recordRawData) - { - assign(recordRawData); - } - - /** - * A copy c'tor for this class. This copy c'tor doesn't copy the TLV data, but only the pointer to it, - * which means that after calling it both the old and the new instance will point to the same TLV raw data - * @param[in] other The TLVRecord instance to copy from - */ - TLVRecord(const TLVRecord& other) - { - m_Data = other.m_Data; - } - - /** - * A d'tor for this class, currently does nothing - */ - virtual ~TLVRecord() { } - - /** - * Assign a pointer to the TLV record raw data (byte array) - * @param[in] recordRawData A pointer to the TLV record raw data - */ - void assign(uint8_t* recordRawData) - { - m_Data = (TLVRawData*)recordRawData; - } - - /** - * Check if a pointer can be assigned to the TLV record data - * @param[in] recordRawData A pointer to the TLV record raw data - * @param[in] tlvDataLen The size of the TLV record raw data - * @return True if data is valid and can be assigned - */ - static bool canAssign(const uint8_t* recordRawData, size_t tlvDataLen) - { - return recordRawData != nullptr && tlvDataLen >= (sizeof(TLVRawData::recordType) + sizeof(TLVRawData::recordLen)); - } - - /** - * Overload of the assignment operator. This operator doesn't copy the TLV data, but rather copies the pointer to it, - * which means that after calling it both the old and the new instance will point to the same TLV raw data - * @param[in] other The TLVRecord instance to assign - */ - TLVRecord& operator=(const TLVRecord& other) - { - m_Data = other.m_Data; - return *this; - } - - /** - * Overload of the equality operator. Two record are equal if both of them point to the same data, or if they point - * to different data but their total size is equal and the raw data they both contain is similar. - * @param[in] rhs The object to compare to - * @return True if both objects are equal, false otherwise - */ - bool operator==(const TLVRecord& rhs) const - { - if (m_Data == rhs.m_Data) - return true; - - if (getTotalSize() != rhs.getTotalSize()) - return false; - - if (isNull() || ((TLVRecord&)rhs).isNull()) - return false; - - return (memcmp(m_Data, rhs.m_Data, getTotalSize()) == 0); - } - - /** - * Overload of the not equal operator. - * @param[in] rhs The object to compare to - * @return True if objects are not equal, false otherwise - */ - bool operator!=(const TLVRecord& rhs) const - { - return !operator==(rhs); - } - - /** - * @return The type field of the record (the 'T' in __Type__-Length-Value) - */ - TRecType getType() const { - if (m_Data == nullptr) - return 0; - - return m_Data->recordType; - } - - /** - * @return A pointer to the value of the record as byte array (the 'V' in Type-Length- __Value__) - */ - uint8_t* getValue() const { - if (m_Data == nullptr) - return nullptr; - - return m_Data->recordValue; - } - - /** - * @return True if the TLV record raw data is NULL, false otherwise - */ - bool isNull() const { return (m_Data == nullptr); } - - /** - * @return True if the TLV record raw data is not NULL, false otherwise - */ - bool isNotNull() const { return (m_Data != nullptr); } - - /** - * @return A pointer to the TLV record raw data byte stream - */ - uint8_t* getRecordBasePtr() const { return (uint8_t*)m_Data; } - - /** - * Free the memory of the TLV record raw data - */ - void purgeRecordData() - { - if (!isNull()) - { - delete [] m_Data; - m_Data = nullptr; - } - } - - /** - * A templated method to retrieve the record data as a certain type T. For example, if record data is 4B long - * (integer) then this method should be used as getValueAs() and it will return the record data as an integer.
- * Notice this return value is a copy of the data, not a pointer to the actual data - * @param[in] offset The offset in the record data to start reading the value from. Useful for cases when you want - * to read some of the data that doesn't start at offset 0. This is an optional parameter and the default value - * is 0, meaning start reading the value at the beginning of the record data - * @return The record data as type T - */ - template - T getValueAs(size_t offset = 0) const - { - if (getDataSize() - offset < sizeof(T)) - return 0; - - T result; - memcpy(&result, m_Data->recordValue + offset, sizeof(T)); - return result; - } - - /** - * A templated method to copy data of type T into the TLV record data. For example: if record data is 4[Bytes] long use - * this method with \ to set an integer value into the record data: setValue(num) - * @param[in] newValue The value of type T to copy to the record data - * @param[in] valueOffset An optional parameter that specifies where to start setting the record data (default set to 0). For example: - * if record data is 20 bytes long and you only need to set the 4 last bytes as integer then use this method like this: - * setValue(num, 16) - * @return True if value was set successfully or false if the size of T is larger than the record data size - */ - template - bool setValue(T newValue, int valueOffset = 0) - { - if (getDataSize() < sizeof(T)) - return false; - - memcpy(m_Data->recordValue + valueOffset, &newValue, sizeof(T)); - return true; - } - - /** - * @return The total size of the TLV record (in bytes) - */ - virtual size_t getTotalSize() const = 0; - - /** - * @return The size of the record value (meaning the size of the 'V' part in TLV) - */ - virtual size_t getDataSize() const = 0; - }; - - - /** - * @class TLVRecordReader - * A class for reading TLV records data out of a byte stream. This class contains helper methods for retrieving and - * counting TLV records. This is a template class that expects template argument class derived from TLVRecord. - */ - template - class TLVRecordReader - { - private: - mutable size_t m_RecordCount; - - public: - - /** - * A default c'tor for this class - */ - TLVRecordReader() { m_RecordCount = (size_t)-1; } - - /** - * A default copy c'tor for this class - */ - TLVRecordReader(const TLVRecordReader& other) - { - m_RecordCount = other.m_RecordCount; - } - - /** - * A d'tor for this class which currently does nothing - */ - virtual ~TLVRecordReader() { } - - /** - * Overload of the assignment operator for this class - * @param[in] other The TLVRecordReader instance to assign - */ - TLVRecordReader& operator=(const TLVRecordReader& other) - { - m_RecordCount = other.m_RecordCount; - return *this; - } - - /** - * Get the first TLV record out of a byte stream - * @param[in] tlvDataBasePtr A pointer to the TLV data byte stream - * @param[in] tlvDataLen The TLV data byte stream length - * @return An instance of type TLVRecordType that contains the first TLV record. If tlvDataBasePtr is NULL or - * tlvDataLen is zero the returned TLVRecordType instance will be logically NULL, meaning TLVRecordType.isNull() will - * return true - */ - TLVRecordType getFirstTLVRecord(uint8_t* tlvDataBasePtr, size_t tlvDataLen) const - { - TLVRecordType resRec(NULL); // for NRVO optimization - if (!TLVRecordType::canAssign(tlvDataBasePtr, tlvDataLen)) - return resRec; - - resRec.assign(tlvDataBasePtr); - // resRec pointer is out-bounds of the TLV records memory - if (resRec.getRecordBasePtr() + resRec.getTotalSize() > tlvDataBasePtr + tlvDataLen) - resRec.assign(NULL); - - // check if there are records at all and the total size is not zero - if (!resRec.isNull() && (tlvDataLen == 0 || resRec.getTotalSize() == 0)) - resRec.assign(NULL); - - return resRec; - } - - /** - * Get a TLV record that follows a given TLV record in a byte stream - * @param[in] record A given TLV record - * @param[in] tlvDataBasePtr A pointer to the TLV data byte stream - * @param[in] tlvDataLen The TLV data byte stream length - * @return An instance of type TLVRecordType that wraps the record following the record given as input. If the - * input record.isNull() is true or if the next record is out of bounds of the byte stream, a logical NULL instance - * of TLVRecordType will be returned, meaning TLVRecordType.isNull() will return true - */ - TLVRecordType getNextTLVRecord(TLVRecordType& record, const uint8_t* tlvDataBasePtr, size_t tlvDataLen) const - { - TLVRecordType resRec(NULL); // for NRVO optimization - - if (record.isNull()) - return resRec; - - if (!TLVRecordType::canAssign(record.getRecordBasePtr() + record.getTotalSize(), tlvDataBasePtr - record.getRecordBasePtr() + tlvDataLen - record.getTotalSize())) - return resRec; - - resRec.assign(record.getRecordBasePtr() + record.getTotalSize()); - - if (resRec.getTotalSize() == 0) - resRec.assign(NULL); - - // resRec pointer is out-bounds of the TLV records memory - if ((resRec.getRecordBasePtr() - tlvDataBasePtr) < 0) - resRec.assign(NULL); - - // resRec pointer is out-bounds of the TLV records memory - if (!resRec.isNull() && resRec.getRecordBasePtr() + resRec.getTotalSize() > tlvDataBasePtr + tlvDataLen) - resRec.assign(NULL); - - return resRec; - } - - /** - * Search for the first TLV record that corresponds to a given record type (the 'T' in __Type__-Length-Value) - * @param[in] recordType The record type to search for - * @param[in] tlvDataBasePtr A pointer to the TLV data byte stream - * @param[in] tlvDataLen The TLV data byte stream length - * @return An instance of type TLVRecordType that contains the result record. If record was not found a logical - * NULL instance of TLVRecordType will be returned, meaning TLVRecordType.isNull() will return true - */ - TLVRecordType getTLVRecord(uint32_t recordType, uint8_t* tlvDataBasePtr, size_t tlvDataLen) const - { - TLVRecordType curRec = getFirstTLVRecord(tlvDataBasePtr, tlvDataLen); - while (!curRec.isNull()) - { - if (curRec.getType() == recordType) - { - return curRec; - } - - curRec = getNextTLVRecord(curRec, tlvDataBasePtr, tlvDataLen); - } - - curRec.assign(NULL); - return curRec; // for NRVO optimization - } - - /** - * Get the TLV record count in a given TLV data byte stream. For efficiency purposes the count is being cached - * so only the first call to this method will go over all the TLV records, while all consequent calls will return - * the cached number. This implies that if there is a change in the number of records, it's the user's responsibility - * to call changeTLVRecordCount() with the record count change - * @param[in] tlvDataBasePtr A pointer to the TLV data byte stream - * @param[in] tlvDataLen The TLV data byte stream length - * @return The TLV record count - */ - size_t getTLVRecordCount(uint8_t* tlvDataBasePtr, size_t tlvDataLen) const - { - if (m_RecordCount != (size_t)-1) - return m_RecordCount; - - m_RecordCount = 0; - TLVRecordType curRec = getFirstTLVRecord(tlvDataBasePtr, tlvDataLen); - while (!curRec.isNull()) - { - m_RecordCount++; - curRec = getNextTLVRecord(curRec, tlvDataBasePtr, tlvDataLen); - } - - return m_RecordCount; - } - - /** - * As described in getTLVRecordCount(), the TLV record count is being cached for efficiency purposes. So if the - * number of TLV records change, it's the user's responsibility to call this method with the number of TLV records - * being added or removed. If records were added the change should be a positive number, or a negative number - * if records were removed - * @param[in] changedBy Number of records that were added or removed - */ - void changeTLVRecordCount(int changedBy) { if (m_RecordCount != (size_t)-1) m_RecordCount += changedBy; } - }; - - - /** - * @class TLVRecordBuilder - * A base class for building Type-Length-Value (TLV) records. This builder receives the record parameters in its c'tor, - * builds the record raw buffer and provides a method to build a TLVRecord object out of it. Please notice this is - * a base class that lacks the capability of actually building TLVRecord objects and also cannot be instantiated. The - * reason for that is that different protocols build TLV records in different ways, so these missing capabilities will - * be implemented by the derived classes which are specific to each protocol. This class only provides the common - * infrastructure that will be used by them - */ - class TLVRecordBuilder - { - protected: - - TLVRecordBuilder(); - - TLVRecordBuilder(uint32_t recType, const uint8_t* recValue, uint8_t recValueLen); - - TLVRecordBuilder(uint32_t recType, uint8_t recValue); - - TLVRecordBuilder(uint32_t recType, uint16_t recValue); - - TLVRecordBuilder(uint32_t recType, uint32_t recValue); - - TLVRecordBuilder(uint32_t recType, const IPv4Address& recValue); - - TLVRecordBuilder(uint32_t recType, const std::string& recValue, bool valueIsHexString = false); - - TLVRecordBuilder(const TLVRecordBuilder& other); - - TLVRecordBuilder& operator=(const TLVRecordBuilder& other); - - virtual ~TLVRecordBuilder(); - - void init(uint32_t recType, const uint8_t* recValue, size_t recValueLen); - - uint8_t* m_RecValue; - size_t m_RecValueLen; - uint32_t m_RecType; - - private: - - void copyData(const TLVRecordBuilder& other); - }; -} +namespace pcpp { +/** + * @class TLVRecord + * A wrapper class for a Type-Length-Value (TLV) record. This class does not + * create or modify TLV records, but rather serves as a wrapper and provides + * useful methods for retrieving data from them. This class has several abstract + * methods that should be implemented in derived classes. These methods are for + * record length value calculation (the 'L' in TLV) which is implemented + * differently in different protocols + */ +template +class TLVRecord { + protected: + /** A struct representing the TLV construct */ + struct TLVRawData { + /** Record type */ + TRecType recordType; + /** Record length in bytes */ + TRecLen recordLen; + /** Record value (variable size) */ + uint8_t recordValue[]; + }; + + TLVRawData* m_Data; + + public: + /** + * A c'tor for this class that gets a pointer to the TLV record raw data (byte + * array) + * @param[in] recordRawData A pointer to the TLV record raw data + */ + TLVRecord(uint8_t* recordRawData) { assign(recordRawData); } + + /** + * A copy c'tor for this class. This copy c'tor doesn't copy the TLV data, but + * only the pointer to it, which means that after calling it both the old and + * the new instance will point to the same TLV raw data + * @param[in] other The TLVRecord instance to copy from + */ + TLVRecord(const TLVRecord& other) { m_Data = other.m_Data; } + + /** + * A d'tor for this class, currently does nothing + */ + virtual ~TLVRecord() {} + + /** + * Assign a pointer to the TLV record raw data (byte array) + * @param[in] recordRawData A pointer to the TLV record raw data + */ + void assign(uint8_t* recordRawData) { m_Data = (TLVRawData*)recordRawData; } + + /** + * Check if a pointer can be assigned to the TLV record data + * @param[in] recordRawData A pointer to the TLV record raw data + * @param[in] tlvDataLen The size of the TLV record raw data + * @return True if data is valid and can be assigned + */ + static bool canAssign(const uint8_t* recordRawData, size_t tlvDataLen) { + return recordRawData != nullptr && + tlvDataLen >= + (sizeof(TLVRawData::recordType) + sizeof(TLVRawData::recordLen)); + } + + /** + * Overload of the assignment operator. This operator doesn't copy the TLV + * data, but rather copies the pointer to it, which means that after calling + * it both the old and the new instance will point to the same TLV raw data + * @param[in] other The TLVRecord instance to assign + */ + TLVRecord& operator=(const TLVRecord& other) { + m_Data = other.m_Data; + return *this; + } + + /** + * Overload of the equality operator. Two record are equal if both of them + * point to the same data, or if they point to different data but their total + * size is equal and the raw data they both contain is similar. + * @param[in] rhs The object to compare to + * @return True if both objects are equal, false otherwise + */ + bool operator==(const TLVRecord& rhs) const { + if (m_Data == rhs.m_Data) + return true; + + if (getTotalSize() != rhs.getTotalSize()) + return false; + + if (isNull() || ((TLVRecord&)rhs).isNull()) + return false; + + return (memcmp(m_Data, rhs.m_Data, getTotalSize()) == 0); + } + + /** + * Overload of the not equal operator. + * @param[in] rhs The object to compare to + * @return True if objects are not equal, false otherwise + */ + bool operator!=(const TLVRecord& rhs) const { return !operator==(rhs); } + + /** + * @return The type field of the record (the 'T' in __Type__-Length-Value) + */ + TRecType getType() const { + if (m_Data == nullptr) + return 0; + + return m_Data->recordType; + } + + /** + * @return A pointer to the value of the record as byte array (the 'V' in + * Type-Length- __Value__) + */ + uint8_t* getValue() const { + if (m_Data == nullptr) + return nullptr; + + return m_Data->recordValue; + } + + /** + * @return True if the TLV record raw data is NULL, false otherwise + */ + bool isNull() const { return (m_Data == nullptr); } + + /** + * @return True if the TLV record raw data is not NULL, false otherwise + */ + bool isNotNull() const { return (m_Data != nullptr); } + + /** + * @return A pointer to the TLV record raw data byte stream + */ + uint8_t* getRecordBasePtr() const { return (uint8_t*)m_Data; } + + /** + * Free the memory of the TLV record raw data + */ + void purgeRecordData() { + if (!isNull()) { + delete[] m_Data; + m_Data = nullptr; + } + } + + /** + * A templated method to retrieve the record data as a certain type T. For + * example, if record data is 4B long (integer) then this method should be + * used as getValueAs() and it will return the record data as an + * integer.
Notice this return value is a copy of the data, not a pointer + * to the actual data + * @param[in] offset The offset in the record data to start reading the value + * from. Useful for cases when you want to read some of the data that doesn't + * start at offset 0. This is an optional parameter and the default value is + * 0, meaning start reading the value at the beginning of the record data + * @return The record data as type T + */ + template + T getValueAs(size_t offset = 0) const { + if (getDataSize() - offset < sizeof(T)) + return 0; + + T result; + memcpy(&result, m_Data->recordValue + offset, sizeof(T)); + return result; + } + + /** + * A templated method to copy data of type T into the TLV record data. For + * example: if record data is 4[Bytes] long use this method with \ to + * set an integer value into the record data: setValue(num) + * @param[in] newValue The value of type T to copy to the record data + * @param[in] valueOffset An optional parameter that specifies where to start + * setting the record data (default set to 0). For example: if record data is + * 20 bytes long and you only need to set the 4 last bytes as integer then use + * this method like this: setValue(num, 16) + * @return True if value was set successfully or false if the size of T is + * larger than the record data size + */ + template + bool setValue(T newValue, int valueOffset = 0) { + if (getDataSize() < sizeof(T)) + return false; + + memcpy(m_Data->recordValue + valueOffset, &newValue, sizeof(T)); + return true; + } + + /** + * @return The total size of the TLV record (in bytes) + */ + virtual size_t getTotalSize() const = 0; + + /** + * @return The size of the record value (meaning the size of the 'V' part in + * TLV) + */ + virtual size_t getDataSize() const = 0; +}; + +/** + * @class TLVRecordReader + * A class for reading TLV records data out of a byte stream. This class + * contains helper methods for retrieving and counting TLV records. This is a + * template class that expects template argument class derived from TLVRecord. + */ +template +class TLVRecordReader { + private: + mutable size_t m_RecordCount; + + public: + /** + * A default c'tor for this class + */ + TLVRecordReader() { m_RecordCount = (size_t)-1; } + + /** + * A default copy c'tor for this class + */ + TLVRecordReader(const TLVRecordReader& other) { + m_RecordCount = other.m_RecordCount; + } + + /** + * A d'tor for this class which currently does nothing + */ + virtual ~TLVRecordReader() {} + + /** + * Overload of the assignment operator for this class + * @param[in] other The TLVRecordReader instance to assign + */ + TLVRecordReader& operator=(const TLVRecordReader& other) { + m_RecordCount = other.m_RecordCount; + return *this; + } + + /** + * Get the first TLV record out of a byte stream + * @param[in] tlvDataBasePtr A pointer to the TLV data byte stream + * @param[in] tlvDataLen The TLV data byte stream length + * @return An instance of type TLVRecordType that contains the first TLV + * record. If tlvDataBasePtr is NULL or tlvDataLen is zero the returned + * TLVRecordType instance will be logically NULL, meaning + * TLVRecordType.isNull() will return true + */ + TLVRecordType getFirstTLVRecord(uint8_t* tlvDataBasePtr, + size_t tlvDataLen) const { + TLVRecordType resRec(NULL); // for NRVO optimization + if (!TLVRecordType::canAssign(tlvDataBasePtr, tlvDataLen)) + return resRec; + + resRec.assign(tlvDataBasePtr); + // resRec pointer is out-bounds of the TLV records memory + if (resRec.getRecordBasePtr() + resRec.getTotalSize() > + tlvDataBasePtr + tlvDataLen) + resRec.assign(NULL); + + // check if there are records at all and the total size is not zero + if (!resRec.isNull() && (tlvDataLen == 0 || resRec.getTotalSize() == 0)) + resRec.assign(NULL); + + return resRec; + } + + /** + * Get a TLV record that follows a given TLV record in a byte stream + * @param[in] record A given TLV record + * @param[in] tlvDataBasePtr A pointer to the TLV data byte stream + * @param[in] tlvDataLen The TLV data byte stream length + * @return An instance of type TLVRecordType that wraps the record following + * the record given as input. If the input record.isNull() is true or if the + * next record is out of bounds of the byte stream, a logical NULL instance of + * TLVRecordType will be returned, meaning TLVRecordType.isNull() will return + * true + */ + TLVRecordType getNextTLVRecord(TLVRecordType& record, + const uint8_t* tlvDataBasePtr, + size_t tlvDataLen) const { + TLVRecordType resRec(NULL); // for NRVO optimization + + if (record.isNull()) + return resRec; + + if (!TLVRecordType::canAssign(record.getRecordBasePtr() + + record.getTotalSize(), + tlvDataBasePtr - record.getRecordBasePtr() + + tlvDataLen - record.getTotalSize())) + return resRec; + + resRec.assign(record.getRecordBasePtr() + record.getTotalSize()); + + if (resRec.getTotalSize() == 0) + resRec.assign(NULL); + + // resRec pointer is out-bounds of the TLV records memory + if ((resRec.getRecordBasePtr() - tlvDataBasePtr) < 0) + resRec.assign(NULL); + + // resRec pointer is out-bounds of the TLV records memory + if (!resRec.isNull() && resRec.getRecordBasePtr() + resRec.getTotalSize() > + tlvDataBasePtr + tlvDataLen) + resRec.assign(NULL); + + return resRec; + } + + /** + * Search for the first TLV record that corresponds to a given record type + * (the 'T' in __Type__-Length-Value) + * @param[in] recordType The record type to search for + * @param[in] tlvDataBasePtr A pointer to the TLV data byte stream + * @param[in] tlvDataLen The TLV data byte stream length + * @return An instance of type TLVRecordType that contains the result record. + * If record was not found a logical NULL instance of TLVRecordType will be + * returned, meaning TLVRecordType.isNull() will return true + */ + TLVRecordType getTLVRecord(uint32_t recordType, uint8_t* tlvDataBasePtr, + size_t tlvDataLen) const { + TLVRecordType curRec = getFirstTLVRecord(tlvDataBasePtr, tlvDataLen); + while (!curRec.isNull()) { + if (curRec.getType() == recordType) { + return curRec; + } + + curRec = getNextTLVRecord(curRec, tlvDataBasePtr, tlvDataLen); + } + + curRec.assign(NULL); + return curRec; // for NRVO optimization + } + + /** + * Get the TLV record count in a given TLV data byte stream. For efficiency + * purposes the count is being cached so only the first call to this method + * will go over all the TLV records, while all consequent calls will return + * the cached number. This implies that if there is a change in the number of + * records, it's the user's responsibility to call changeTLVRecordCount() with + * the record count change + * @param[in] tlvDataBasePtr A pointer to the TLV data byte stream + * @param[in] tlvDataLen The TLV data byte stream length + * @return The TLV record count + */ + size_t getTLVRecordCount(uint8_t* tlvDataBasePtr, size_t tlvDataLen) const { + if (m_RecordCount != (size_t)-1) + return m_RecordCount; + + m_RecordCount = 0; + TLVRecordType curRec = getFirstTLVRecord(tlvDataBasePtr, tlvDataLen); + while (!curRec.isNull()) { + m_RecordCount++; + curRec = getNextTLVRecord(curRec, tlvDataBasePtr, tlvDataLen); + } + + return m_RecordCount; + } + + /** + * As described in getTLVRecordCount(), the TLV record count is being cached + * for efficiency purposes. So if the number of TLV records change, it's the + * user's responsibility to call this method with the number of TLV records + * being added or removed. If records were added the change should be a + * positive number, or a negative number if records were removed + * @param[in] changedBy Number of records that were added or removed + */ + void changeTLVRecordCount(int changedBy) { + if (m_RecordCount != (size_t)-1) + m_RecordCount += changedBy; + } +}; + +/** + * @class TLVRecordBuilder + * A base class for building Type-Length-Value (TLV) records. This builder + * receives the record parameters in its c'tor, builds the record raw buffer and + * provides a method to build a TLVRecord object out of it. Please notice this + * is a base class that lacks the capability of actually building TLVRecord + * objects and also cannot be instantiated. The reason for that is that + * different protocols build TLV records in different ways, so these missing + * capabilities will be implemented by the derived classes which are specific to + * each protocol. This class only provides the common infrastructure that will + * be used by them + */ +class TLVRecordBuilder { + protected: + TLVRecordBuilder(); + + TLVRecordBuilder(uint32_t recType, const uint8_t* recValue, + uint8_t recValueLen); + + TLVRecordBuilder(uint32_t recType, uint8_t recValue); + + TLVRecordBuilder(uint32_t recType, uint16_t recValue); + + TLVRecordBuilder(uint32_t recType, uint32_t recValue); + + TLVRecordBuilder(uint32_t recType, const IPv4Address& recValue); + + TLVRecordBuilder(uint32_t recType, const std::string& recValue, + bool valueIsHexString = false); + + TLVRecordBuilder(const TLVRecordBuilder& other); + + TLVRecordBuilder& operator=(const TLVRecordBuilder& other); + + virtual ~TLVRecordBuilder(); + + void init(uint32_t recType, const uint8_t* recValue, size_t recValueLen); + + uint8_t* m_RecValue; + size_t m_RecValueLen; + uint32_t m_RecType; + + private: + void copyData(const TLVRecordBuilder& other); +}; +} // namespace pcpp #endif // PACKETPP_TLV_DATA diff --git a/Packet++/header/TcpLayer.h b/Packet++/header/TcpLayer.h index 50b89ca9bc..8b9b87036e 100644 --- a/Packet++/header/TcpLayer.h +++ b/Packet++/header/TcpLayer.h @@ -11,516 +11,523 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - - /** - * @struct tcphdr - * Represents an TCP protocol header - */ -#pragma pack(push,1) - struct tcphdr - { - /** Source TCP port */ - uint16_t portSrc; - /** Destination TCP port */ - uint16_t portDst; - /** Sequence number */ - uint32_t sequenceNumber; - /** Acknowledgment number */ - uint32_t ackNumber; +namespace pcpp { + +/** + * @struct tcphdr + * Represents an TCP protocol header + */ +#pragma pack(push, 1) +struct tcphdr { + /** Source TCP port */ + uint16_t portSrc; + /** Destination TCP port */ + uint16_t portDst; + /** Sequence number */ + uint32_t sequenceNumber; + /** Acknowledgment number */ + uint32_t ackNumber; #if (BYTE_ORDER == LITTLE_ENDIAN) - uint16_t reserved:4, - /** Specifies the size of the TCP header in 32-bit words */ - dataOffset:4, - /** FIN flag */ - finFlag:1, - /** SYN flag */ - synFlag:1, - /** RST flag */ - rstFlag:1, - /** PSH flag */ - pshFlag:1, - /** ACK flag */ - ackFlag:1, - /** URG flag */ - urgFlag:1, - /** ECE flag */ - eceFlag:1, - /** CWR flag */ - cwrFlag:1; + uint16_t reserved : 4, + /** Specifies the size of the TCP header in 32-bit words */ + dataOffset : 4, + /** FIN flag */ + finFlag : 1, + /** SYN flag */ + synFlag : 1, + /** RST flag */ + rstFlag : 1, + /** PSH flag */ + pshFlag : 1, + /** ACK flag */ + ackFlag : 1, + /** URG flag */ + urgFlag : 1, + /** ECE flag */ + eceFlag : 1, + /** CWR flag */ + cwrFlag : 1; #elif (BYTE_ORDER == BIG_ENDIAN) - /** Specifies the size of the TCP header in 32-bit words */ - uint16_t dataOffset:4, - reserved:4, - /** CWR flag */ - cwrFlag:1, - /** ECE flag */ - eceFlag:1, - /** URG flag */ - urgFlag:1, - /** ACK flag */ - ackFlag:1, - /** PSH flag */ - pshFlag:1, - /** RST flag */ - rstFlag:1, - /** SYN flag */ - synFlag:1, - /** FIN flag */ - finFlag:1; + /** Specifies the size of the TCP header in 32-bit words */ + uint16_t dataOffset : 4, reserved : 4, + /** CWR flag */ + cwrFlag : 1, + /** ECE flag */ + eceFlag : 1, + /** URG flag */ + urgFlag : 1, + /** ACK flag */ + ackFlag : 1, + /** PSH flag */ + pshFlag : 1, + /** RST flag */ + rstFlag : 1, + /** SYN flag */ + synFlag : 1, + /** FIN flag */ + finFlag : 1; #else -#error "Endian is not LE nor BE..." +#error "Endian is not LE nor BE..." #endif - /** The size of the receive window, which specifies the number of window size units (by default, bytes) */ - uint16_t windowSize; - /** The 16-bit checksum field is used for error-checking of the header and data */ - uint16_t headerChecksum; - /** If the URG flag (@ref tcphdr#urgFlag) is set, then this 16-bit field is an offset from the sequence number indicating the last urgent data byte */ - uint16_t urgentPointer; - }; + /** The size of the receive window, which specifies the number of window size + * units (by default, bytes) */ + uint16_t windowSize; + /** The 16-bit checksum field is used for error-checking of the header and + * data */ + uint16_t headerChecksum; + /** If the URG flag (@ref tcphdr#urgFlag) is set, then this 16-bit field is an + * offset from the sequence number indicating the last urgent data byte */ + uint16_t urgentPointer; +}; #pragma pack(pop) - - /** - * TCP options types - */ - enum TcpOptionType - { - /** Padding */ - PCPP_TCPOPT_NOP = 1, - /** End of options */ - PCPP_TCPOPT_EOL = 0, - /** Segment size negotiating */ - TCPOPT_MSS = 2, - /** Window scaling */ - PCPP_TCPOPT_WINDOW = 3, - /** SACK Permitted */ - TCPOPT_SACK_PERM = 4, - /** SACK Block */ - PCPP_TCPOPT_SACK = 5, - /** Echo (obsoleted by option ::PCPP_TCPOPT_TIMESTAMP) */ - TCPOPT_ECHO = 6, - /** Echo Reply (obsoleted by option ::PCPP_TCPOPT_TIMESTAMP) */ - TCPOPT_ECHOREPLY = 7, - /** TCP Timestamps */ - PCPP_TCPOPT_TIMESTAMP = 8, - /** CC (obsolete) */ - TCPOPT_CC = 11, - /** CC.NEW (obsolete) */ - TCPOPT_CCNEW = 12, - /** CC.ECHO(obsolete) */ - TCPOPT_CCECHO = 13, - /** MD5 Signature Option */ - TCPOPT_MD5 = 19, - /** Multipath TCP */ - TCPOPT_MPTCP = 0x1e, - /** SCPS Capabilities */ - TCPOPT_SCPS = 20, - /** SCPS SNACK */ - TCPOPT_SNACK = 21, - /** SCPS Record Boundary */ - TCPOPT_RECBOUND = 22, - /** SCPS Corruption Experienced */ - TCPOPT_CORREXP = 23, - /** Quick-Start Response */ - TCPOPT_QS = 27, - /** User Timeout Option (also, other known unauthorized use) */ - TCPOPT_USER_TO = 28, - /** RFC3692-style Experiment 1 (also improperly used for shipping products) */ - TCPOPT_EXP_FD = 0xfd, - /** RFC3692-style Experiment 2 (also improperly used for shipping products) */ - TCPOPT_EXP_FE = 0xfe, - /** Riverbed probe option, non IANA registered option number */ - TCPOPT_RVBD_PROBE = 76, - /** Riverbed transparency option, non IANA registered option number */ - TCPOPT_RVBD_TRPY = 78, - /** Unknown option */ - TCPOPT_Unknown = 255 - }; - - - // TCP option lengths - - /** pcpp::PCPP_TCPOPT_NOP length */ -#define PCPP_TCPOLEN_NOP 1 - /** pcpp::PCPP_TCPOPT_EOL length */ -#define PCPP_TCPOLEN_EOL 1 - /** pcpp::TCPOPT_MSS length */ -#define PCPP_TCPOLEN_MSS 4 - /** pcpp::PCPP_TCPOPT_WINDOW length */ -#define PCPP_TCPOLEN_WINDOW 3 - /** pcpp::TCPOPT_SACK_PERM length */ -#define PCPP_TCPOLEN_SACK_PERM 2 - /** pcpp::PCPP_TCPOPT_SACK length */ -#define PCPP_TCPOLEN_SACK_MIN 2 - /** pcpp::TCPOPT_ECHO length */ -#define PCPP_TCPOLEN_ECHO 6 - /** pcpp::TCPOPT_ECHOREPLY length */ -#define PCPP_TCPOLEN_ECHOREPLY 6 - /** pcpp::PCPP_TCPOPT_TIMESTAMP length */ -#define PCPP_TCPOLEN_TIMESTAMP 10 - /** pcpp::TCPOPT_CC length */ -#define PCPP_TCPOLEN_CC 6 - /** pcpp::TCPOPT_CCNEW length */ -#define PCPP_TCPOLEN_CCNEW 6 - /** pcpp::TCPOPT_CCECHO length */ -#define PCPP_TCPOLEN_CCECHO 6 - /** pcpp::TCPOPT_MD5 length */ -#define PCPP_TCPOLEN_MD5 18 - /** pcpp::TCPOPT_MPTCP length */ -#define PCPP_TCPOLEN_MPTCP_MIN 8 - /** pcpp::TCPOPT_SCPS length */ -#define PCPP_TCPOLEN_SCPS 4 - /** pcpp::TCPOPT_SNACK length */ -#define PCPP_TCPOLEN_SNACK 6 - /** pcpp::TCPOPT_RECBOUND length */ -#define PCPP_TCPOLEN_RECBOUND 2 - /** pcpp::TCPOPT_CORREXP length */ -#define PCPP_TCPOLEN_CORREXP 2 - /** pcpp::TCPOPT_QS length */ -#define PCPP_TCPOLEN_QS 8 - /** pcpp::TCPOPT_USER_TO length */ -#define PCPP_TCPOLEN_USER_TO 4 - /** pcpp::TCPOPT_RVBD_PROBE length */ +/** + * TCP options types + */ +enum TcpOptionType { + /** Padding */ + PCPP_TCPOPT_NOP = 1, + /** End of options */ + PCPP_TCPOPT_EOL = 0, + /** Segment size negotiating */ + TCPOPT_MSS = 2, + /** Window scaling */ + PCPP_TCPOPT_WINDOW = 3, + /** SACK Permitted */ + TCPOPT_SACK_PERM = 4, + /** SACK Block */ + PCPP_TCPOPT_SACK = 5, + /** Echo (obsoleted by option ::PCPP_TCPOPT_TIMESTAMP) */ + TCPOPT_ECHO = 6, + /** Echo Reply (obsoleted by option ::PCPP_TCPOPT_TIMESTAMP) */ + TCPOPT_ECHOREPLY = 7, + /** TCP Timestamps */ + PCPP_TCPOPT_TIMESTAMP = 8, + /** CC (obsolete) */ + TCPOPT_CC = 11, + /** CC.NEW (obsolete) */ + TCPOPT_CCNEW = 12, + /** CC.ECHO(obsolete) */ + TCPOPT_CCECHO = 13, + /** MD5 Signature Option */ + TCPOPT_MD5 = 19, + /** Multipath TCP */ + TCPOPT_MPTCP = 0x1e, + /** SCPS Capabilities */ + TCPOPT_SCPS = 20, + /** SCPS SNACK */ + TCPOPT_SNACK = 21, + /** SCPS Record Boundary */ + TCPOPT_RECBOUND = 22, + /** SCPS Corruption Experienced */ + TCPOPT_CORREXP = 23, + /** Quick-Start Response */ + TCPOPT_QS = 27, + /** User Timeout Option (also, other known unauthorized use) */ + TCPOPT_USER_TO = 28, + /** RFC3692-style Experiment 1 (also improperly used for shipping products) */ + TCPOPT_EXP_FD = 0xfd, + /** RFC3692-style Experiment 2 (also improperly used for shipping products) */ + TCPOPT_EXP_FE = 0xfe, + /** Riverbed probe option, non IANA registered option number */ + TCPOPT_RVBD_PROBE = 76, + /** Riverbed transparency option, non IANA registered option number */ + TCPOPT_RVBD_TRPY = 78, + /** Unknown option */ + TCPOPT_Unknown = 255 +}; + +// TCP option lengths + +/** pcpp::PCPP_TCPOPT_NOP length */ +#define PCPP_TCPOLEN_NOP 1 +/** pcpp::PCPP_TCPOPT_EOL length */ +#define PCPP_TCPOLEN_EOL 1 +/** pcpp::TCPOPT_MSS length */ +#define PCPP_TCPOLEN_MSS 4 +/** pcpp::PCPP_TCPOPT_WINDOW length */ +#define PCPP_TCPOLEN_WINDOW 3 +/** pcpp::TCPOPT_SACK_PERM length */ +#define PCPP_TCPOLEN_SACK_PERM 2 +/** pcpp::PCPP_TCPOPT_SACK length */ +#define PCPP_TCPOLEN_SACK_MIN 2 +/** pcpp::TCPOPT_ECHO length */ +#define PCPP_TCPOLEN_ECHO 6 +/** pcpp::TCPOPT_ECHOREPLY length */ +#define PCPP_TCPOLEN_ECHOREPLY 6 +/** pcpp::PCPP_TCPOPT_TIMESTAMP length */ +#define PCPP_TCPOLEN_TIMESTAMP 10 +/** pcpp::TCPOPT_CC length */ +#define PCPP_TCPOLEN_CC 6 +/** pcpp::TCPOPT_CCNEW length */ +#define PCPP_TCPOLEN_CCNEW 6 +/** pcpp::TCPOPT_CCECHO length */ +#define PCPP_TCPOLEN_CCECHO 6 +/** pcpp::TCPOPT_MD5 length */ +#define PCPP_TCPOLEN_MD5 18 +/** pcpp::TCPOPT_MPTCP length */ +#define PCPP_TCPOLEN_MPTCP_MIN 8 +/** pcpp::TCPOPT_SCPS length */ +#define PCPP_TCPOLEN_SCPS 4 +/** pcpp::TCPOPT_SNACK length */ +#define PCPP_TCPOLEN_SNACK 6 +/** pcpp::TCPOPT_RECBOUND length */ +#define PCPP_TCPOLEN_RECBOUND 2 +/** pcpp::TCPOPT_CORREXP length */ +#define PCPP_TCPOLEN_CORREXP 2 +/** pcpp::TCPOPT_QS length */ +#define PCPP_TCPOLEN_QS 8 +/** pcpp::TCPOPT_USER_TO length */ +#define PCPP_TCPOLEN_USER_TO 4 +/** pcpp::TCPOPT_RVBD_PROBE length */ #define PCPP_TCPOLEN_RVBD_PROBE_MIN 3 - /** pcpp::TCPOPT_RVBD_TRPY length */ +/** pcpp::TCPOPT_RVBD_TRPY length */ #define PCPP_TCPOLEN_RVBD_TRPY_MIN 16 - /** pcpp::TCPOPT_EXP_FD and pcpp::TCPOPT_EXP_FE length */ -#define PCPP_TCPOLEN_EXP_MIN 2 - - - /** - * @class TcpOption - * A wrapper class for TCP options. This class does not create or modify TCP option records, but rather - * serves as a wrapper and provides useful methods for retrieving data from them - */ - class TcpOption : public TLVRecord - { - public: - - /** - * A c'tor for this class that gets a pointer to the option raw data (byte array) - * @param[in] optionRawData A pointer to the TCP option raw data - */ - explicit TcpOption(uint8_t* optionRawData) : TLVRecord(optionRawData) { } - - /** - * A d'tor for this class, currently does nothing - */ - ~TcpOption() { } - - /** - * @return TCP option type casted as pcpp::TcpOptionType enum. If the data is null a value - * of ::TCPOPT_Unknown is returned - */ - TcpOptionType getTcpOptionType() const - { - if (m_Data == nullptr) - return TCPOPT_Unknown; - - return (TcpOptionType)m_Data->recordType; - } - - /** - * Check if a pointer can be assigned to the TLV record data - * @param[in] recordRawData A pointer to the TLV record raw data - * @param[in] tlvDataLen The size of the TLV record raw data - * @return True if data is valid and can be assigned - */ - static bool canAssign(const uint8_t* recordRawData, size_t tlvDataLen) - { - auto data = (TLVRawData*)recordRawData; - if (data == nullptr) - return false; - - if (tlvDataLen < sizeof(TLVRawData::recordType)) - return false; - - if (data->recordType == (uint8_t)PCPP_TCPOPT_NOP || data->recordType == (uint8_t)PCPP_TCPOPT_EOL) - return true; - - return TLVRecord::canAssign(recordRawData, tlvDataLen); - } - - // implement abstract methods - - size_t getTotalSize() const - { - if (m_Data == nullptr) - return 0; - - if (m_Data->recordType == (uint8_t)PCPP_TCPOPT_NOP || m_Data->recordType == (uint8_t)PCPP_TCPOPT_EOL) - return sizeof(uint8_t); - - return (size_t)m_Data->recordLen; - } - - size_t getDataSize() const - { - if (m_Data == nullptr) - return 0; - - if (m_Data->recordType == (uint8_t)PCPP_TCPOPT_NOP || m_Data->recordType == (uint8_t)PCPP_TCPOPT_EOL) - return 0; - - return (size_t)m_Data->recordLen - (2*sizeof(uint8_t)); - } - }; - - - /** - * @class TcpOptionBuilder - * A class for building TCP option records. This builder receives the TCP option parameters in its c'tor, - * builds the TCP option raw buffer and provides a build() method to get a TcpOption object out of it - */ - class TcpOptionBuilder : public TLVRecordBuilder - { - - public: - - /** - * An enum to describe NOP and EOL TCP options. Used in one of this class's c'tors - */ - enum NopEolOptionTypes - { - /** NOP TCP option */ - NOP, - /** EOL TCP option */ - EOL - }; - - /** - * A c'tor for building TCP options which their value is a byte array. The TcpOption object can be later - * retrieved by calling build() - * @param[in] optionType TCP option type - * @param[in] optionValue A buffer containing the option value. This buffer is read-only and isn't modified in any way. - * @param[in] optionValueLen Option value length in bytes - */ - TcpOptionBuilder(TcpOptionType optionType, const uint8_t* optionValue, uint8_t optionValueLen) : - TLVRecordBuilder((uint8_t)optionType, optionValue, optionValueLen) {} - - /** - * A c'tor for building TCP options which have a 1-byte value. The TcpOption object can be later retrieved - * by calling build() - * @param[in] optionType TCP option type - * @param[in] optionValue A 1-byte option value - */ - TcpOptionBuilder(TcpOptionType optionType, uint8_t optionValue) : - TLVRecordBuilder((uint8_t)optionType, optionValue) {} - - /** - * A c'tor for building TCP options which have a 2-byte value. The TcpOption object can be later retrieved - * by calling build() - * @param[in] optionType TCP option type - * @param[in] optionValue A 2-byte option value - */ - TcpOptionBuilder(TcpOptionType optionType, uint16_t optionValue) : - TLVRecordBuilder((uint8_t)optionType, optionValue) {} - - /** - * A c'tor for building TCP options which have a 4-byte value. The TcpOption object can be later retrieved - * by calling build() - * @param[in] optionType TCP option type - * @param[in] optionValue A 4-byte option value - */ - TcpOptionBuilder(TcpOptionType optionType, uint32_t optionValue) : - TLVRecordBuilder((uint8_t)optionType, optionValue) {} - - /** - * A c'tor for building TCP NOP and EOL options. These option types are special in that they contain only 1 byte - * which is the TCP option type (NOP or EOL). The TcpOption object can be later retrieved - * by calling build() - * @param[in] optionType An enum value indicating which option type to build (NOP or EOL) - */ - explicit TcpOptionBuilder(NopEolOptionTypes optionType); - - /** - * Build the TcpOption object out of the parameters defined in the c'tor - * @return The TcpOption object - */ - TcpOption build() const; - }; - - - /** - * @class TcpLayer - * Represents a TCP (Transmission Control Protocol) protocol layer - */ - class TcpLayer : public Layer - { - public: - /** - * A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data (will be casted to @ref tcphdr) - * @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 - */ - TcpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); - - /** - * A constructor that allocates a new TCP header with zero TCP options - */ - TcpLayer(); - - /** - * A constructor that allocates a new TCP header with source port and destination port and zero TCP options - * @param[in] portSrc Source port - * @param[in] portDst Destination port - */ - TcpLayer(uint16_t portSrc, uint16_t portDst); - - ~TcpLayer() {} - - /** - * A copy constructor that copy the entire header from the other TcpLayer (including TCP options) - */ - TcpLayer(const TcpLayer& other); - - /** - * An assignment operator that first delete all data from current layer and then copy the entire header from the other TcpLayer (including TCP options) - */ - TcpLayer& operator=(const TcpLayer& other); - - /** - * Get a pointer to the TCP header. Notice this points directly to the data, so every change will change the actual packet data - * @return A pointer to the @ref tcphdr - */ - tcphdr* getTcpHeader() const { return (tcphdr*)m_Data; } - - /** - * @return TCP source port - */ - uint16_t getSrcPort() const; - - /** - * @return TCP destination port - */ - uint16_t getDstPort() const; - - /** - * Get a TCP option by type - * @param[in] option TCP option type to retrieve - * @return An TcpOption object that contains the first option that matches this type, or logical NULL - * (TcpOption#isNull() == true) if no such option found - */ - TcpOption getTcpOption(TcpOptionType option) const; - - /** - * @return The first TCP option in the packet. If the current layer contains no options the returned value will contain - * a logical NULL (TcpOption#isNull() == true) - */ - TcpOption getFirstTcpOption() const; - - /** - * Get the TCP option that comes after a given option. If the given option was the last one, the - * returned value will contain a logical NULL (TcpOption#isNull() == true) - * @param[in] tcpOption A TCP option object that exists in the current layer - * @return A TcpOption object that contains the TCP option data that comes next, or logical NULL if the given - * TCP option: (1) was the last one; or (2) contains a logical NULL; or (3) doesn't belong to this packet - */ - TcpOption getNextTcpOption(TcpOption& tcpOption) const; - - /** - * @return The number of TCP options in this layer - */ - size_t getTcpOptionCount() const; - - /** - * Add a new TCP option at the end of the layer (after the last TCP option) - * @param[in] optionBuilder A TcpOptionBuilder object that contains the TCP option data to be added - * @return A TcpOption object that contains the newly added TCP option data or logical NULL - * (TcpOption#isNull() == true) if addition failed. In case of a failure a corresponding error message will be - * printed to log - */ - TcpOption addTcpOption(const TcpOptionBuilder& optionBuilder); - - /** - * Add a new TCP option after an existing one - * @param[in] optionBuilder A TcpOptionBuilder object that contains the requested TCP option data to be added - * @param[in] prevOptionType The TCP option which the newly added option should come after. This is an optional parameter which - * gets a default value of ::TCPOPT_Unknown if omitted, which means the new option will be added as the first option in the layer - * @return A TcpOption object containing the newly added TCP option data or logical NULL - * (TcpOption#isNull() == true) if addition failed. In case of a failure a corresponding error message will be - * printed to log - */ - TcpOption addTcpOptionAfter(const TcpOptionBuilder& optionBuilder, TcpOptionType prevOptionType = TCPOPT_Unknown); - - /** - * Remove an existing TCP option from the layer. TCP option is found by type - * @param[in] optionType The TCP option type to remove - * @return True if TCP option was removed or false if type wasn't found or if removal failed (in each case a proper error - * will be written to log) - */ - bool removeTcpOption(TcpOptionType optionType); - - /** - * Remove all TCP options in this layer - * @return True if all TCP options were successfully removed or false if removal failed for some reason - * (a proper error will be written to log) - */ - bool removeAllTcpOptions(); - - - /** - * Calculate the checksum from header and data and possibly write the result to @ref tcphdr#headerChecksum - * @param[in] writeResultToPacket If set to true then checksum result will be written to @ref tcphdr#headerChecksum - * @return The checksum result - */ - uint16_t calculateChecksum(bool writeResultToPacket); - - /** - * The static method makes validation of input data - * @param[in] data The pointer to the beginning of byte stream of TCP packet - * @param[in] dataLen The length of byte stream - * @return True if the data is valid and can represent a TCP packet - */ - static inline bool isDataValid(const uint8_t* data, size_t dataLen); - - // implement abstract methods - - /** - * Currently identifies the following next layers: HttpRequestLayer, HttpResponseLayer. Otherwise sets PayloadLayer - */ - void parseNextLayer(); - - /** - * @return Size of @ref tcphdr + all TCP options - */ - size_t getHeaderLen() const { return getTcpHeader()->dataOffset*4 ;} - - /** - * Calculate @ref tcphdr#headerChecksum field - */ - void computeCalculateFields(); - - std::string toString() const; - - OsiModelLayer getOsiModelLayer() const { return OsiModelTransportLayer; } - - private: - - TLVRecordReader m_OptionReader; - int m_NumOfTrailingBytes; - - void initLayer(); - uint8_t* getOptionsBasePtr() const { return m_Data + sizeof(tcphdr); } - TcpOption addTcpOptionAt(const TcpOptionBuilder& optionBuilder, int offset); - void adjustTcpOptionTrailer(size_t totalOptSize); - void copyLayerData(const TcpLayer& other); - }; - - - // implementation of inline methods - - bool TcpLayer::isDataValid(const uint8_t* data, size_t dataLen) - { - const tcphdr* hdr = reinterpret_cast(data); - return dataLen >= sizeof(tcphdr) - && hdr->dataOffset >= 5 /* the minimum TCP header size */ - && dataLen >= hdr->dataOffset * sizeof(uint32_t); - } +/** pcpp::TCPOPT_EXP_FD and pcpp::TCPOPT_EXP_FE length */ +#define PCPP_TCPOLEN_EXP_MIN 2 + +/** + * @class TcpOption + * A wrapper class for TCP options. This class does not create or modify TCP + * option records, but rather serves as a wrapper and provides useful methods + * for retrieving data from them + */ +class TcpOption : public TLVRecord { + public: + /** + * A c'tor for this class that gets a pointer to the option raw data (byte + * array) + * @param[in] optionRawData A pointer to the TCP option raw data + */ + explicit TcpOption(uint8_t* optionRawData) : TLVRecord(optionRawData) {} + + /** + * A d'tor for this class, currently does nothing + */ + ~TcpOption() {} + + /** + * @return TCP option type casted as pcpp::TcpOptionType enum. If the data is + * null a value of ::TCPOPT_Unknown is returned + */ + TcpOptionType getTcpOptionType() const { + if (m_Data == nullptr) + return TCPOPT_Unknown; + + return (TcpOptionType)m_Data->recordType; + } + + /** + * Check if a pointer can be assigned to the TLV record data + * @param[in] recordRawData A pointer to the TLV record raw data + * @param[in] tlvDataLen The size of the TLV record raw data + * @return True if data is valid and can be assigned + */ + static bool canAssign(const uint8_t* recordRawData, size_t tlvDataLen) { + auto data = (TLVRawData*)recordRawData; + if (data == nullptr) + return false; + + if (tlvDataLen < sizeof(TLVRawData::recordType)) + return false; + + if (data->recordType == (uint8_t)PCPP_TCPOPT_NOP || + data->recordType == (uint8_t)PCPP_TCPOPT_EOL) + return true; + + return TLVRecord::canAssign(recordRawData, tlvDataLen); + } + + // implement abstract methods + + size_t getTotalSize() const { + if (m_Data == nullptr) + return 0; + + if (m_Data->recordType == (uint8_t)PCPP_TCPOPT_NOP || + m_Data->recordType == (uint8_t)PCPP_TCPOPT_EOL) + return sizeof(uint8_t); + + return (size_t)m_Data->recordLen; + } + + size_t getDataSize() const { + if (m_Data == nullptr) + return 0; + + if (m_Data->recordType == (uint8_t)PCPP_TCPOPT_NOP || + m_Data->recordType == (uint8_t)PCPP_TCPOPT_EOL) + return 0; + + return (size_t)m_Data->recordLen - (2 * sizeof(uint8_t)); + } +}; + +/** + * @class TcpOptionBuilder + * A class for building TCP option records. This builder receives the TCP option + * parameters in its c'tor, builds the TCP option raw buffer and provides a + * build() method to get a TcpOption object out of it + */ +class TcpOptionBuilder : public TLVRecordBuilder { + + public: + /** + * An enum to describe NOP and EOL TCP options. Used in one of this class's + * c'tors + */ + enum NopEolOptionTypes { + /** NOP TCP option */ + NOP, + /** EOL TCP option */ + EOL + }; + + /** + * A c'tor for building TCP options which their value is a byte array. The + * TcpOption object can be later retrieved by calling build() + * @param[in] optionType TCP option type + * @param[in] optionValue A buffer containing the option value. This buffer is + * read-only and isn't modified in any way. + * @param[in] optionValueLen Option value length in bytes + */ + TcpOptionBuilder(TcpOptionType optionType, const uint8_t* optionValue, + uint8_t optionValueLen) + : TLVRecordBuilder((uint8_t)optionType, optionValue, optionValueLen) {} + + /** + * A c'tor for building TCP options which have a 1-byte value. The TcpOption + * object can be later retrieved by calling build() + * @param[in] optionType TCP option type + * @param[in] optionValue A 1-byte option value + */ + TcpOptionBuilder(TcpOptionType optionType, uint8_t optionValue) + : TLVRecordBuilder((uint8_t)optionType, optionValue) {} + + /** + * A c'tor for building TCP options which have a 2-byte value. The TcpOption + * object can be later retrieved by calling build() + * @param[in] optionType TCP option type + * @param[in] optionValue A 2-byte option value + */ + TcpOptionBuilder(TcpOptionType optionType, uint16_t optionValue) + : TLVRecordBuilder((uint8_t)optionType, optionValue) {} + + /** + * A c'tor for building TCP options which have a 4-byte value. The TcpOption + * object can be later retrieved by calling build() + * @param[in] optionType TCP option type + * @param[in] optionValue A 4-byte option value + */ + TcpOptionBuilder(TcpOptionType optionType, uint32_t optionValue) + : TLVRecordBuilder((uint8_t)optionType, optionValue) {} + + /** + * A c'tor for building TCP NOP and EOL options. These option types are + * special in that they contain only 1 byte which is the TCP option type (NOP + * or EOL). The TcpOption object can be later retrieved by calling build() + * @param[in] optionType An enum value indicating which option type to build + * (NOP or EOL) + */ + explicit TcpOptionBuilder(NopEolOptionTypes optionType); + + /** + * Build the TcpOption object out of the parameters defined in the c'tor + * @return The TcpOption object + */ + TcpOption build() const; +}; + +/** + * @class TcpLayer + * Represents a TCP (Transmission Control Protocol) protocol layer + */ +class TcpLayer : public Layer { + public: + /** + * A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data (will be casted to @ref tcphdr) + * @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 + */ + TcpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); + + /** + * A constructor that allocates a new TCP header with zero TCP options + */ + TcpLayer(); + + /** + * A constructor that allocates a new TCP header with source port and + * destination port and zero TCP options + * @param[in] portSrc Source port + * @param[in] portDst Destination port + */ + TcpLayer(uint16_t portSrc, uint16_t portDst); + + ~TcpLayer() {} + + /** + * A copy constructor that copy the entire header from the other TcpLayer + * (including TCP options) + */ + TcpLayer(const TcpLayer& other); + + /** + * An assignment operator that first delete all data from current layer and + * then copy the entire header from the other TcpLayer (including TCP options) + */ + TcpLayer& operator=(const TcpLayer& other); + + /** + * Get a pointer to the TCP header. Notice this points directly to the data, + * so every change will change the actual packet data + * @return A pointer to the @ref tcphdr + */ + tcphdr* getTcpHeader() const { return (tcphdr*)m_Data; } + + /** + * @return TCP source port + */ + uint16_t getSrcPort() const; + + /** + * @return TCP destination port + */ + uint16_t getDstPort() const; + + /** + * Get a TCP option by type + * @param[in] option TCP option type to retrieve + * @return An TcpOption object that contains the first option that matches + * this type, or logical NULL (TcpOption#isNull() == true) if no such option + * found + */ + TcpOption getTcpOption(TcpOptionType option) const; + + /** + * @return The first TCP option in the packet. If the current layer contains + * no options the returned value will contain a logical NULL + * (TcpOption#isNull() == true) + */ + TcpOption getFirstTcpOption() const; + + /** + * Get the TCP option that comes after a given option. If the given option was + * the last one, the returned value will contain a logical NULL + * (TcpOption#isNull() == true) + * @param[in] tcpOption A TCP option object that exists in the current layer + * @return A TcpOption object that contains the TCP option data that comes + * next, or logical NULL if the given TCP option: (1) was the last one; or (2) + * contains a logical NULL; or (3) doesn't belong to this packet + */ + TcpOption getNextTcpOption(TcpOption& tcpOption) const; + + /** + * @return The number of TCP options in this layer + */ + size_t getTcpOptionCount() const; + + /** + * Add a new TCP option at the end of the layer (after the last TCP option) + * @param[in] optionBuilder A TcpOptionBuilder object that contains the TCP + * option data to be added + * @return A TcpOption object that contains the newly added TCP option data or + * logical NULL (TcpOption#isNull() == true) if addition failed. In case of a + * failure a corresponding error message will be printed to log + */ + TcpOption addTcpOption(const TcpOptionBuilder& optionBuilder); + + /** + * Add a new TCP option after an existing one + * @param[in] optionBuilder A TcpOptionBuilder object that contains the + * requested TCP option data to be added + * @param[in] prevOptionType The TCP option which the newly added option + * should come after. This is an optional parameter which gets a default value + * of ::TCPOPT_Unknown if omitted, which means the new option will be added as + * the first option in the layer + * @return A TcpOption object containing the newly added TCP option data or + * logical NULL (TcpOption#isNull() == true) if addition failed. In case of a + * failure a corresponding error message will be printed to log + */ + TcpOption addTcpOptionAfter(const TcpOptionBuilder& optionBuilder, + TcpOptionType prevOptionType = TCPOPT_Unknown); + + /** + * Remove an existing TCP option from the layer. TCP option is found by type + * @param[in] optionType The TCP option type to remove + * @return True if TCP option was removed or false if type wasn't found or if + * removal failed (in each case a proper error will be written to log) + */ + bool removeTcpOption(TcpOptionType optionType); + + /** + * Remove all TCP options in this layer + * @return True if all TCP options were successfully removed or false if + * removal failed for some reason (a proper error will be written to log) + */ + bool removeAllTcpOptions(); + + /** + * Calculate the checksum from header and data and possibly write the result + * to @ref tcphdr#headerChecksum + * @param[in] writeResultToPacket If set to true then checksum result will be + * written to @ref tcphdr#headerChecksum + * @return The checksum result + */ + uint16_t calculateChecksum(bool writeResultToPacket); + + /** + * The static method makes validation of input data + * @param[in] data The pointer to the beginning of byte stream of TCP packet + * @param[in] dataLen The length of byte stream + * @return True if the data is valid and can represent a TCP packet + */ + static inline bool isDataValid(const uint8_t* data, size_t dataLen); + + // implement abstract methods + + /** + * Currently identifies the following next layers: HttpRequestLayer, + * HttpResponseLayer. Otherwise sets PayloadLayer + */ + void parseNextLayer(); + + /** + * @return Size of @ref tcphdr + all TCP options + */ + size_t getHeaderLen() const { return getTcpHeader()->dataOffset * 4; } + + /** + * Calculate @ref tcphdr#headerChecksum field + */ + void computeCalculateFields(); + + std::string toString() const; + + OsiModelLayer getOsiModelLayer() const { return OsiModelTransportLayer; } + + private: + TLVRecordReader m_OptionReader; + int m_NumOfTrailingBytes; + + void initLayer(); + uint8_t* getOptionsBasePtr() const { return m_Data + sizeof(tcphdr); } + TcpOption addTcpOptionAt(const TcpOptionBuilder& optionBuilder, int offset); + void adjustTcpOptionTrailer(size_t totalOptSize); + void copyLayerData(const TcpLayer& other); +}; + +// implementation of inline methods + +bool TcpLayer::isDataValid(const uint8_t* data, size_t dataLen) { + const tcphdr* hdr = reinterpret_cast(data); + return dataLen >= sizeof(tcphdr) && + hdr->dataOffset >= 5 /* the minimum TCP header size */ + && dataLen >= hdr->dataOffset * sizeof(uint32_t); +} } // namespace pcpp diff --git a/Packet++/header/TcpReassembly.h b/Packet++/header/TcpReassembly.h index e0fa96a0de..ae696b177e 100644 --- a/Packet++/header/TcpReassembly.h +++ b/Packet++/header/TcpReassembly.h @@ -1,67 +1,109 @@ #ifndef PACKETPP_TCP_REASSEMBLY #define PACKETPP_TCP_REASSEMBLY -#include "Packet.h" #include "IpAddress.h" +#include "Packet.h" #include "PointerVector.h" -#include #include +#include #include - /** * @file - * This is an implementation of TCP reassembly logic, which means reassembly of TCP messages spanning multiple TCP segments (or packets).
- * This logic can be useful in analyzing messages for a large number of protocols implemented on top of TCP including HTTP, SSL/TLS, FTP and many many more. + * This is an implementation of TCP reassembly logic, which means reassembly of + * TCP messages spanning multiple TCP segments (or packets).
This logic can + * be useful in analyzing messages for a large number of protocols implemented + * on top of TCP including HTTP, SSL/TLS, FTP and many many more. * * __General Features:__ * - Manage multiple TCP connections under one pcpp#TcpReassembly instance * - Support TCP retransmission * - Support out-of-order packets * - Support missing TCP data - * - TCP connections can end "naturally" (by FIN/RST packets) or manually by the user + * - TCP connections can end "naturally" (by FIN/RST packets) or manually by the + * user * - Support callbacks for new TCP data, connection start and connection end * * __Logic Description:__ * - The user creates an instance of the pcpp#TcpReassembly class * - Then the user starts feeding it with TCP packets - * - The pcpp#TcpReassembly instance manages all TCP connections from the packets it's being fed. For each connection it manages its 2 sides (A->B and B->A) + * - The pcpp#TcpReassembly instance manages all TCP connections from the + * packets it's being fed. For each connection it manages its 2 sides (A->B and + * B->A) * - When a packet arrives, it is first classified to a certain TCP connection * - Then it is classified to a certain side of the TCP connection - * - Then the pcpp#TcpReassembly logic tries to understand if the data in this packet is the expected data (sequence-wise) and if it's new (e.g isn't a retransmission) - * - If the packet data matches these criteria a callback is being invoked. This callback is supplied by the user in the creation of the pcpp#TcpReassembly instance. This callback contains - * the new data (of course), but also information about the connection (5-tuple, 4-byte hash key describing the connection, etc.) and also a pointer to a "user cookie", meaning a pointer to - * a structure provided by the user during the creation of the pcpp#TcpReassembly instance + * - Then the pcpp#TcpReassembly logic tries to understand if the data in this + * packet is the expected data (sequence-wise) and if it's new (e.g isn't a + * retransmission) + * - If the packet data matches these criteria a callback is being invoked. This + * callback is supplied by the user in the creation of the pcpp#TcpReassembly + * instance. This callback contains the new data (of course), but also + * information about the connection (5-tuple, 4-byte hash key describing the + * connection, etc.) and also a pointer to a "user cookie", meaning a pointer to + * a structure provided by the user during the creation of the + * pcpp#TcpReassembly instance * - If the data in this packet isn't new, it's being ignored - * - If the data in this packet isn't expected (meaning this packet came out-of-order), then the data is being queued internally and will be sent to the user when its turn arrives - * (meaning, after the data before arrives) - * - If the missing data doesn't arrive until a new message from the other side of the connection arrives or until the connection ends - this will be considered as missing data and the - * queued data will be sent to the user, but the string "[X bytes missing]" will be added to the message sent in the callback - * - pcpp#TcpReassembly supports 2 more callbacks - one is invoked when a new TCP connection is first seen and the other when it's ended (either by a FIN/RST packet or manually by the user). - * Both of these callbacks contain data about the connection (5-tuple, 4-byte hash key describing the connection, etc.) and also a pointer to a "user cookie", meaning a pointer to a - * structure provided by the user during the creation of the pcpp#TcpReassembly instance. The end connection callback also provides the reason for closing it ("naturally" or manually) + * - If the data in this packet isn't expected (meaning this packet came + * out-of-order), then the data is being queued internally and will be sent to + * the user when its turn arrives (meaning, after the data before arrives) + * - If the missing data doesn't arrive until a new message from the other side + * of the connection arrives or until the connection ends - this will be + * considered as missing data and the queued data will be sent to the user, but + * the string "[X bytes missing]" will be added to the message sent in the + * callback + * - pcpp#TcpReassembly supports 2 more callbacks - one is invoked when a new + * TCP connection is first seen and the other when it's ended (either by a + * FIN/RST packet or manually by the user). Both of these callbacks contain data + * about the connection (5-tuple, 4-byte hash key describing the connection, + * etc.) and also a pointer to a "user cookie", meaning a pointer to a structure + * provided by the user during the creation of the pcpp#TcpReassembly instance. + * The end connection callback also provides the reason for closing it + * ("naturally" or manually) * * __Basic Usage and APIs:__ - * - pcpp#TcpReassembly c'tor - Create an instance, provide the callbacks and the user cookie to the instance - * - pcpp#TcpReassembly#reassemblePacket() - Feed pcpp#TcpReassembly instance with packets - * - pcpp#TcpReassembly#closeConnection() - Manually close a connection by a flow key - * - pcpp#TcpReassembly#closeAllConnections() - Manually close all currently opened connections - * - pcpp#TcpReassembly#OnTcpMessageReady callback - Invoked when new data arrives on a certain connection. Contains the new data as well as connection data (5-tuple, flow key) - * - pcpp#TcpReassembly#OnTcpConnectionStart callback - Invoked when a new connection is identified - * - pcpp#TcpReassembly#OnTcpConnectionEnd callback - Invoked when a connection ends (either by FIN/RST or manually by the user) + * - pcpp#TcpReassembly c'tor - Create an instance, provide the callbacks and + * the user cookie to the instance + * - pcpp#TcpReassembly#reassemblePacket() - Feed pcpp#TcpReassembly instance + * with packets + * - pcpp#TcpReassembly#closeConnection() - Manually close a connection by a + * flow key + * - pcpp#TcpReassembly#closeAllConnections() - Manually close all currently + * opened connections + * - pcpp#TcpReassembly#OnTcpMessageReady callback - Invoked when new data + * arrives on a certain connection. Contains the new data as well as connection + * data (5-tuple, flow key) + * - pcpp#TcpReassembly#OnTcpConnectionStart callback - Invoked when a new + * connection is identified + * - pcpp#TcpReassembly#OnTcpConnectionEnd callback - Invoked when a connection + * ends (either by FIN/RST or manually by the user) * * __Additional information:__ - * When the connection is closed the information is not being deleted from memory immediately. There is a delay between these moments. Existence of this delay is caused by two reasons: - * - pcpp#TcpReassembly#reassemblePacket() should detect the packets that arrive after the FIN packet has been received - * - the user can use the information about connections managed by pcpp#TcpReassembly instance. Following methods are used for this purpose: pcpp#TcpReassembly#getConnectionInformation and pcpp#TcpReassembly#isConnectionOpen. - * Cleaning of memory can be performed automatically (the default behavior) by pcpp#TcpReassembly#reassemblePacket() or manually by calling pcpp#TcpReassembly#purgeClosedConnections in the user code. - * Automatic cleaning is performed once per second. + * When the connection is closed the information is not being deleted from + * memory immediately. There is a delay between these moments. Existence of this + * delay is caused by two reasons: + * - pcpp#TcpReassembly#reassemblePacket() should detect the packets that arrive + * after the FIN packet has been received + * - the user can use the information about connections managed by + * pcpp#TcpReassembly instance. Following methods are used for this purpose: + * pcpp#TcpReassembly#getConnectionInformation and + * pcpp#TcpReassembly#isConnectionOpen. Cleaning of memory can be performed + * automatically (the default behavior) by pcpp#TcpReassembly#reassemblePacket() + * or manually by calling pcpp#TcpReassembly#purgeClosedConnections in the user + * code. Automatic cleaning is performed once per second. * - * The struct pcpp#TcpReassemblyConfiguration allows to setup the parameters of cleanup. Following parameters are supported: - * - pcpp#TcpReassemblyConfiguration#doNotRemoveConnInfo - if this member is set to false the automatic cleanup mode is applied - * - pcpp#TcpReassemblyConfiguration#closedConnectionDelay - the value of delay expressed in seconds. The minimum value is 1 - * - pcpp#TcpReassemblyConfiguration#maxNumToClean - to avoid performance overhead when the cleanup is being performed, this parameter is used. It defines the maximum number of items to be removed per one call of pcpp#TcpReassembly#purgeClosedConnections - * - pcpp#TcpReassemblyConfiguration#maxOutOfOrderFragments - the maximum number of unmatched fragments to keep per flow before missed fragments are considered lost. A value of 0 means unlimited + * The struct pcpp#TcpReassemblyConfiguration allows to setup the parameters of + * cleanup. Following parameters are supported: + * - pcpp#TcpReassemblyConfiguration#doNotRemoveConnInfo - if this member is set + * to false the automatic cleanup mode is applied + * - pcpp#TcpReassemblyConfiguration#closedConnectionDelay - the value of delay + * expressed in seconds. The minimum value is 1 + * - pcpp#TcpReassemblyConfiguration#maxNumToClean - to avoid performance + * overhead when the cleanup is being performed, this parameter is used. It + * defines the maximum number of items to be removed per one call of + * pcpp#TcpReassembly#purgeClosedConnections + * - pcpp#TcpReassemblyConfiguration#maxOutOfOrderFragments - the maximum number + * of unmatched fragments to keep per flow before missed fragments are + * considered lost. A value of 0 means unlimited * */ @@ -69,392 +111,469 @@ * @namespace pcpp * @brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ +namespace pcpp { /** * @struct ConnectionData * Represents basic TCP/UDP + IP connection data */ -struct ConnectionData -{ - /** Source IP address */ - IPAddress srcIP; - /** Destination IP address */ - IPAddress dstIP; - /** Source TCP/UDP port */ - uint16_t srcPort; - /** Destination TCP/UDP port */ - uint16_t dstPort; - /** A 4-byte hash key representing the connection */ - uint32_t flowKey; - /** Start TimeStamp of the connection */ - timeval startTime; - /** End TimeStamp of the connection */ - timeval endTime; - - /** - * A c'tor for this struct that basically zeros all members - */ - ConnectionData() : srcPort(0), dstPort(0), flowKey(0), startTime(), endTime() {} - - /** - * Set startTime of Connection - * @param[in] startTimeValue integer value - */ - void setStartTime(const timeval &startTimeValue) { startTime = startTimeValue; } - - /** - * Set endTime of Connection - * @param[in] endTimeValue integer value - */ - void setEndTime(const timeval &endTimeValue) { endTime = endTimeValue; } +struct ConnectionData { + /** Source IP address */ + IPAddress srcIP; + /** Destination IP address */ + IPAddress dstIP; + /** Source TCP/UDP port */ + uint16_t srcPort; + /** Destination TCP/UDP port */ + uint16_t dstPort; + /** A 4-byte hash key representing the connection */ + uint32_t flowKey; + /** Start TimeStamp of the connection */ + timeval startTime; + /** End TimeStamp of the connection */ + timeval endTime; + + /** + * A c'tor for this struct that basically zeros all members + */ + ConnectionData() + : srcPort(0), dstPort(0), flowKey(0), startTime(), endTime() {} + + /** + * Set startTime of Connection + * @param[in] startTimeValue integer value + */ + void setStartTime(const timeval& startTimeValue) { + startTime = startTimeValue; + } + + /** + * Set endTime of Connection + * @param[in] endTimeValue integer value + */ + void setEndTime(const timeval& endTimeValue) { endTime = endTimeValue; } }; - class TcpReassembly; - /** * @class TcpStreamData - * When following a TCP connection each packet may contain a piece of the data transferred between the client and the server. This class represents these pieces: each instance of it - * contains a piece of data, usually extracted from a single packet, as well as information about the connection + * When following a TCP connection each packet may contain a piece of the data + * transferred between the client and the server. This class represents these + * pieces: each instance of it contains a piece of data, usually extracted from + * a single packet, as well as information about the connection */ -class TcpStreamData -{ -public: - /** - * A c'tor for this class that get data from outside and set the internal members - * @param[in] tcpData A pointer to buffer containing the TCP data piece - * @param[in] tcpDataLength The length of the buffer - * @param[in] missingBytes The number of missing bytes due to packet loss. - * @param[in] connData TCP connection information for this TCP data - * @param[in] timestamp when this packet was received - */ - TcpStreamData(const uint8_t* tcpData, size_t tcpDataLength, size_t missingBytes, const ConnectionData& connData, timeval timestamp) - : m_Data(tcpData), m_DataLen(tcpDataLength), m_MissingBytes(missingBytes), m_Connection(connData), m_Timestamp(timestamp) - { - } - - /** - * A getter for the data buffer - * @return A pointer to the buffer - */ - const uint8_t* getData() const { return m_Data; } - - /** - * A getter for buffer length - * @return Buffer length - */ - size_t getDataLength() const { return m_DataLen; } - - /** - * A getter for missing byte count due to packet loss. - * @return Missing byte count - */ - size_t getMissingByteCount() const { return m_MissingBytes; } - - /** - * Determine if bytes are missing. getMissingByteCount can be called to determine the number of missing bytes. - * @return true if bytes are missing. - */ - bool isBytesMissing() const { return getMissingByteCount() > 0; } - - /** - * A getter for the connection data - * @return The const reference to connection data - */ - const ConnectionData& getConnectionData() const { return m_Connection; } - - /** - * A getter for the timestamp of this packet - * @return The const timeval object with timestamp of this packet - */ - timeval getTimeStamp() const { return m_Timestamp; } - -private: - const uint8_t* m_Data; - size_t m_DataLen; - size_t m_MissingBytes; - const ConnectionData& m_Connection; - timeval m_Timestamp; +class TcpStreamData { + public: + /** + * A c'tor for this class that get data from outside and set the internal + * members + * @param[in] tcpData A pointer to buffer containing the TCP data piece + * @param[in] tcpDataLength The length of the buffer + * @param[in] missingBytes The number of missing bytes due to packet loss. + * @param[in] connData TCP connection information for this TCP data + * @param[in] timestamp when this packet was received + */ + TcpStreamData(const uint8_t* tcpData, size_t tcpDataLength, + size_t missingBytes, const ConnectionData& connData, + timeval timestamp) + : m_Data(tcpData), m_DataLen(tcpDataLength), m_MissingBytes(missingBytes), + m_Connection(connData), m_Timestamp(timestamp) {} + + /** + * A getter for the data buffer + * @return A pointer to the buffer + */ + const uint8_t* getData() const { return m_Data; } + + /** + * A getter for buffer length + * @return Buffer length + */ + size_t getDataLength() const { return m_DataLen; } + + /** + * A getter for missing byte count due to packet loss. + * @return Missing byte count + */ + size_t getMissingByteCount() const { return m_MissingBytes; } + + /** + * Determine if bytes are missing. getMissingByteCount can be called to + * determine the number of missing bytes. + * @return true if bytes are missing. + */ + bool isBytesMissing() const { return getMissingByteCount() > 0; } + + /** + * A getter for the connection data + * @return The const reference to connection data + */ + const ConnectionData& getConnectionData() const { return m_Connection; } + + /** + * A getter for the timestamp of this packet + * @return The const timeval object with timestamp of this packet + */ + timeval getTimeStamp() const { return m_Timestamp; } + + private: + const uint8_t* m_Data; + size_t m_DataLen; + size_t m_MissingBytes; + const ConnectionData& m_Connection; + timeval m_Timestamp; }; - /** * @struct TcpReassemblyConfiguration * A structure for configuring the TcpReassembly class */ -struct TcpReassemblyConfiguration -{ - /** The flag indicating whether to remove the connection data after a connection is closed */ - bool removeConnInfo; - - /** How long the closed connections will not be cleaned up. The value is expressed in seconds. If the value is set to 0 then TcpReassembly should use the default value. - * This parameter is only relevant if removeConnInfo is equal to true. - */ - uint32_t closedConnectionDelay; - - /** The maximum number of items to be cleaned up per one call of purgeClosedConnections. If the value is set to 0 then TcpReassembly should use the default value. - * This parameter is only relevant if removeConnInfo is equal to true. - */ - uint32_t maxNumToClean; - - /** The maximum number of fragments with a non-matching sequence-number to store per connection flow before packets are assumed permanently missed. - If the value is 0, TcpReassembly should keep out of order fragments indefinitely, or until a message from the paired side is seen. - */ - uint32_t maxOutOfOrderFragments; - - /** To enable to clear buffer once packet contains data from a different side than the side seen before - */ - bool enableBaseBufferClearCondition; - - /** - * A c'tor for this struct - * @param[in] removeConnInfo The flag indicating whether to remove the connection data after a connection is closed. The default is true - * @param[in] closedConnectionDelay How long the closed connections will not be cleaned up. The value is expressed in seconds. If it's set to 0 the default value will be used. The default is 5. - * @param[in] maxNumToClean The maximum number of items to be cleaned up per one call of purgeClosedConnections. If it's set to 0 the default value will be used. The default is 30. - * @param[in] maxOutOfOrderFragments The maximum number of unmatched fragments to keep per flow before missed fragments are considered lost. The default is unlimited. - * @param[in] enableBaseBufferClearCondition To enable to clear buffer once packet contains data from a different side than the side seen before - */ - explicit TcpReassemblyConfiguration(bool removeConnInfo = true, uint32_t closedConnectionDelay = 5, uint32_t maxNumToClean = 30, uint32_t maxOutOfOrderFragments = 0, - bool enableBaseBufferClearCondition = true) : removeConnInfo(removeConnInfo), closedConnectionDelay(closedConnectionDelay), maxNumToClean(maxNumToClean), maxOutOfOrderFragments(maxOutOfOrderFragments), enableBaseBufferClearCondition(enableBaseBufferClearCondition) - { - } +struct TcpReassemblyConfiguration { + /** The flag indicating whether to remove the connection data after a + * connection is closed */ + bool removeConnInfo; + + /** How long the closed connections will not be cleaned up. The value is + * expressed in seconds. If the value is set to 0 then TcpReassembly should + * use the default value. This parameter is only relevant if removeConnInfo is + * equal to true. + */ + uint32_t closedConnectionDelay; + + /** The maximum number of items to be cleaned up per one call of + * purgeClosedConnections. If the value is set to 0 then TcpReassembly should + * use the default value. This parameter is only relevant if removeConnInfo is + * equal to true. + */ + uint32_t maxNumToClean; + + /** The maximum number of fragments with a non-matching sequence-number to + store per connection flow before packets are assumed permanently missed. If + the value is 0, TcpReassembly should keep out of order fragments + indefinitely, or until a message from the paired side is seen. + */ + uint32_t maxOutOfOrderFragments; + + /** To enable to clear buffer once packet contains data from a different side + * than the side seen before + */ + bool enableBaseBufferClearCondition; + + /** + * A c'tor for this struct + * @param[in] removeConnInfo The flag indicating whether to remove the + * connection data after a connection is closed. The default is true + * @param[in] closedConnectionDelay How long the closed connections will not + * be cleaned up. The value is expressed in seconds. If it's set to 0 the + * default value will be used. The default is 5. + * @param[in] maxNumToClean The maximum number of items to be cleaned up per + * one call of purgeClosedConnections. If it's set to 0 the default value will + * be used. The default is 30. + * @param[in] maxOutOfOrderFragments The maximum number of unmatched fragments + * to keep per flow before missed fragments are considered lost. The default + * is unlimited. + * @param[in] enableBaseBufferClearCondition To enable to clear buffer once + * packet contains data from a different side than the side seen before + */ + explicit TcpReassemblyConfiguration( + bool removeConnInfo = true, uint32_t closedConnectionDelay = 5, + uint32_t maxNumToClean = 30, uint32_t maxOutOfOrderFragments = 0, + bool enableBaseBufferClearCondition = true) + : removeConnInfo(removeConnInfo), + closedConnectionDelay(closedConnectionDelay), + maxNumToClean(maxNumToClean), + maxOutOfOrderFragments(maxOutOfOrderFragments), + enableBaseBufferClearCondition(enableBaseBufferClearCondition) {} }; - /** * @class TcpReassembly - * A class containing the TCP reassembly logic. Please refer to the documentation at the top of TcpReassembly.h for understanding how to use this class + * A class containing the TCP reassembly logic. Please refer to the + * documentation at the top of TcpReassembly.h for understanding how to use this + * class */ -class TcpReassembly -{ -public: - - /** - * An enum for connection end reasons - */ - enum ConnectionEndReason - { - /** Connection ended because of FIN or RST packet */ - TcpReassemblyConnectionClosedByFIN_RST, - /** Connection ended manually by the user */ - TcpReassemblyConnectionClosedManually - }; - - /** - * An enum for providing reassembly status for each processed packet - */ - enum ReassemblyStatus - { - /** - * The processed packet contains valid TCP payload, and its payload is processed by `OnMessageReadyCallback` callback function. - * The packet may be: - * 1. An in-order TCP packet, meaning `packet_sequence == sequence_expected`. - * Note if there's any buffered out-of-order packet waiting for this packet, their associated callbacks are called in this `reassemblePacket` call. - * 2. An out-of-order TCP packet which satisfy `packet_sequence < sequence_expected && packet_sequence + packet_payload_length > sequence_expected`. - * Note only the new data (the `[sequence_expected, packet_sequence + packet_payload_length]` part ) is processed by `OnMessageReadyCallback` callback function. - */ - TcpMessageHandled, - /** - * The processed packet is an out-of-order TCP packet, meaning `packet_sequence > sequence_expected`. It's buffered so no `OnMessageReadyCallback` callback function is called. - * The callback function for this packet maybe called LATER, under different circumstances: - * 1. When an in-order packet which is right before this packet arrives(case 1 and case 2 described in `TcpMessageHandled` section above). - * 2. When a FIN or RST packet arrives, which will clear the buffered out-of-order packets of this side. - * If this packet contains "new data", meaning `(packet_sequence <= sequence_expected) && (packet_sequence + packet_payload_length > sequence_expected)`, the new data is processed by `OnMessageReadyCallback` callback. - */ - OutOfOrderTcpMessageBuffered, - /** - * The processed packet is a FIN or RST packet with no payload. - * Buffered out-of-order packets will be cleared. - * If they contain "new data", the new data is processed by `OnMessageReadyCallback` callback. - */ - FIN_RSTWithNoData, - /** - * The processed packet is not a SYN/SYNACK/FIN/RST packet and has no payload. - * Normally it's just a bare ACK packet. - * It's ignored and no callback function is called. - */ - Ignore_PacketWithNoData, - /** - * The processed packet comes from a closed flow(an in-order FIN or RST is seen). - * It's ignored and no callback function is called. - */ - Ignore_PacketOfClosedFlow, - /** - * The processed packet is a restransmission packet with no new data, meaning the `packet_sequence + packet_payload_length < sequence_expected`. - * It's ignored and no callback function is called. - */ - Ignore_Retransimission, - /** - * The processed packet is not an IP packet. - * It's ignored and no callback function is called. - */ - NonIpPacket, - /** - * The processed packet is not a TCP packet. - * It's ignored and no callback function is called. - */ - NonTcpPacket, - /** - * The processed packet does not belong to any known TCP connection. - * It's ignored and no callback function is called. - * Normally this will be happen. - */ - Error_PacketDoesNotMatchFlow, - }; - - /** - * The type for storing the connection information - */ - typedef std::map ConnectionInfoList; - - /** - * @typedef OnTcpMessageReady - * A callback invoked when new data arrives on a connection - * @param[in] side The side this data belongs to (MachineA->MachineB or vice versa). The value is 0 or 1 where 0 is the first side seen in the connection and 1 is the second side seen - * @param[in] tcpData The TCP data itself + connection information - * @param[in] userCookie A pointer to the cookie provided by the user in TcpReassembly c'tor (or NULL if no cookie provided) - */ - typedef void (*OnTcpMessageReady)(int8_t side, const TcpStreamData& tcpData, void* userCookie); - - /** - * @typedef OnTcpConnectionStart - * A callback invoked when a new TCP connection is identified (whether it begins with a SYN packet or not) - * @param[in] connectionData Connection information - * @param[in] userCookie A pointer to the cookie provided by the user in TcpReassembly c'tor (or NULL if no cookie provided) - */ - typedef void (*OnTcpConnectionStart)(const ConnectionData& connectionData, void* userCookie); - - /** - * @typedef OnTcpConnectionEnd - * A callback invoked when a TCP connection is terminated, either by a FIN or RST packet or manually by the user - * @param[in] connectionData Connection information - * @param[in] reason The reason for connection termination: FIN/RST packet or manually by the user - * @param[in] userCookie A pointer to the cookie provided by the user in TcpReassembly c'tor (or NULL if no cookie provided) - */ - typedef void (*OnTcpConnectionEnd)(const ConnectionData& connectionData, ConnectionEndReason reason, void* userCookie); - - /** - * A c'tor for this class - * @param[in] onMessageReadyCallback The callback to be invoked when new data arrives - * @param[in] userCookie A pointer to an object provided by the user. This pointer will be returned when invoking the various callbacks. This parameter is optional, default cookie is NULL - * @param[in] onConnectionStartCallback The callback to be invoked when a new connection is identified. This parameter is optional - * @param[in] onConnectionEndCallback The callback to be invoked when a new connection is terminated (either by a FIN/RST packet or manually by the user). This parameter is optional - * @param[in] config Optional parameter for defining special configuration parameters. If not set the default parameters will be set - */ - explicit TcpReassembly(OnTcpMessageReady onMessageReadyCallback, void* userCookie = NULL, OnTcpConnectionStart onConnectionStartCallback = NULL, OnTcpConnectionEnd onConnectionEndCallback = NULL, const TcpReassemblyConfiguration &config = TcpReassemblyConfiguration()); - - /** - * The most important method of this class which gets a packet from the user and processes it. If this packet opens a new connection, ends a connection or contains new data on an - * existing connection, the relevant callback will be called (TcpReassembly#OnTcpMessageReady, TcpReassembly#OnTcpConnectionStart, TcpReassembly#OnTcpConnectionEnd) - * @param[in] tcpData A reference to the packet to process - * @return A enum of `TcpReassembly::ReassemblyStatus`, indicating status of TCP reassembly - */ - ReassemblyStatus reassemblePacket(Packet& tcpData); - - /** - * The most important method of this class which gets a raw packet from the user and processes it. If this packet opens a new connection, ends a connection or contains new data on an - * existing connection, the relevant callback will be invoked (TcpReassembly#OnTcpMessageReady, TcpReassembly#OnTcpConnectionStart, TcpReassembly#OnTcpConnectionEnd) - * @param[in] tcpRawData A reference to the raw packet to process - * @return A enum of `TcpReassembly::ReassemblyStatus`, indicating status of TCP reassembly - */ - ReassemblyStatus reassemblePacket(RawPacket* tcpRawData); - - /** - * Close a connection manually. If the connection doesn't exist or already closed an error log is printed. This method will cause the TcpReassembly#OnTcpConnectionEnd to be invoked with - * a reason of TcpReassembly#TcpReassemblyConnectionClosedManually - * @param[in] flowKey A 4-byte hash key representing the connection. Can be taken from a ConnectionData instance - */ - void closeConnection(uint32_t flowKey); - - /** - * Close all open connections manually. This method will cause the TcpReassembly#OnTcpConnectionEnd to be invoked for each connection with a reason of - * TcpReassembly#TcpReassemblyConnectionClosedManually - */ - void closeAllConnections(); - - /** - * Get a map of all connections managed by this TcpReassembly instance (both connections that are open and those that are already closed) - * @return A map of all connections managed. Notice this map is constant and cannot be changed by the user - */ - const ConnectionInfoList& getConnectionInformation() const { return m_ConnectionInfo; } - - /** - * Check if a certain connection managed by this TcpReassembly instance is currently opened or closed - * @param[in] connection The connection to check - * @return A positive number (> 0) if connection is opened, zero (0) if connection is closed, and a negative number (< 0) if this connection isn't managed by this TcpReassembly instance - */ - int isConnectionOpen(const ConnectionData& connection) const; - - /** - * Clean up the closed connections from the memory - * @param[in] maxNumToClean The maximum number of items to be cleaned up per one call. This parameter, when its value is not zero, overrides the value that was set by the constructor. - * @return The number of cleared items - */ - uint32_t purgeClosedConnections(uint32_t maxNumToClean = 0); - -private: - struct TcpFragment - { - uint32_t sequence; - size_t dataLength; - uint8_t* data; - timeval timestamp; - - TcpFragment() : sequence(0), dataLength(0), data(NULL) {} - ~TcpFragment() { delete [] data; } - }; - - struct TcpOneSideData - { - IPAddress srcIP; - uint16_t srcPort; - uint32_t sequence; - PointerVector tcpFragmentList; - bool gotFinOrRst; - - TcpOneSideData() : srcPort(0), sequence(0), gotFinOrRst(false) {} - }; - - struct TcpReassemblyData - { - bool closed; - int8_t numOfSides; - int8_t prevSide; - TcpOneSideData twoSides[2]; - ConnectionData connData; - - TcpReassemblyData() : closed(false), numOfSides(0), prevSide(-1) {} - }; - - typedef std::map ConnectionList; - typedef std::map > CleanupList; - - OnTcpMessageReady m_OnMessageReadyCallback; - OnTcpConnectionStart m_OnConnStart; - OnTcpConnectionEnd m_OnConnEnd; - void* m_UserCookie; - ConnectionList m_ConnectionList; - ConnectionInfoList m_ConnectionInfo; - CleanupList m_CleanupList; - bool m_RemoveConnInfo; - uint32_t m_ClosedConnectionDelay; - uint32_t m_MaxNumToClean; - size_t m_MaxOutOfOrderFragments; - time_t m_PurgeTimepoint; - bool m_EnableBaseBufferClearCondition; - - void checkOutOfOrderFragments(TcpReassemblyData* tcpReassemblyData, int8_t sideIndex, bool cleanWholeFragList); - - void handleFinOrRst(TcpReassemblyData* tcpReassemblyData, int8_t sideIndex, uint32_t flowKey, bool isRst); - - void closeConnectionInternal(uint32_t flowKey, ConnectionEndReason reason); - - void insertIntoCleanupList(uint32_t flowKey); +class TcpReassembly { + public: + /** + * An enum for connection end reasons + */ + enum ConnectionEndReason { + /** Connection ended because of FIN or RST packet */ + TcpReassemblyConnectionClosedByFIN_RST, + /** Connection ended manually by the user */ + TcpReassemblyConnectionClosedManually + }; + + /** + * An enum for providing reassembly status for each processed packet + */ + enum ReassemblyStatus { + /** + * The processed packet contains valid TCP payload, and its payload is + * processed by `OnMessageReadyCallback` callback function. The packet may + * be: + * 1. An in-order TCP packet, meaning `packet_sequence == + * sequence_expected`. Note if there's any buffered out-of-order packet + * waiting for this packet, their associated callbacks are called in this + * `reassemblePacket` call. + * 2. An out-of-order TCP packet which satisfy `packet_sequence < + * sequence_expected && packet_sequence + packet_payload_length > + * sequence_expected`. Note only the new data (the `[sequence_expected, + * packet_sequence + packet_payload_length]` part ) is processed by + * `OnMessageReadyCallback` callback function. + */ + TcpMessageHandled, + /** + * The processed packet is an out-of-order TCP packet, meaning + * `packet_sequence > sequence_expected`. It's buffered so no + * `OnMessageReadyCallback` callback function is called. The callback + * function for this packet maybe called LATER, under different + * circumstances: + * 1. When an in-order packet which is right before this packet arrives(case + * 1 and case 2 described in `TcpMessageHandled` section above). + * 2. When a FIN or RST packet arrives, which will clear the buffered + * out-of-order packets of this side. If this packet contains "new data", + * meaning `(packet_sequence <= sequence_expected) && (packet_sequence + + * packet_payload_length > sequence_expected)`, the new data is processed by + * `OnMessageReadyCallback` callback. + */ + OutOfOrderTcpMessageBuffered, + /** + * The processed packet is a FIN or RST packet with no payload. + * Buffered out-of-order packets will be cleared. + * If they contain "new data", the new data is processed by + * `OnMessageReadyCallback` callback. + */ + FIN_RSTWithNoData, + /** + * The processed packet is not a SYN/SYNACK/FIN/RST packet and has no + * payload. Normally it's just a bare ACK packet. It's ignored and no + * callback function is called. + */ + Ignore_PacketWithNoData, + /** + * The processed packet comes from a closed flow(an in-order FIN or RST is + * seen). It's ignored and no callback function is called. + */ + Ignore_PacketOfClosedFlow, + /** + * The processed packet is a restransmission packet with no new data, + * meaning the `packet_sequence + packet_payload_length < + * sequence_expected`. It's ignored and no callback function is called. + */ + Ignore_Retransimission, + /** + * The processed packet is not an IP packet. + * It's ignored and no callback function is called. + */ + NonIpPacket, + /** + * The processed packet is not a TCP packet. + * It's ignored and no callback function is called. + */ + NonTcpPacket, + /** + * The processed packet does not belong to any known TCP connection. + * It's ignored and no callback function is called. + * Normally this will be happen. + */ + Error_PacketDoesNotMatchFlow, + }; + + /** + * The type for storing the connection information + */ + typedef std::map ConnectionInfoList; + + /** + * @typedef OnTcpMessageReady + * A callback invoked when new data arrives on a connection + * @param[in] side The side this data belongs to (MachineA->MachineB or vice + * versa). The value is 0 or 1 where 0 is the first side seen in the + * connection and 1 is the second side seen + * @param[in] tcpData The TCP data itself + connection information + * @param[in] userCookie A pointer to the cookie provided by the user in + * TcpReassembly c'tor (or NULL if no cookie provided) + */ + typedef void (*OnTcpMessageReady)(int8_t side, const TcpStreamData& tcpData, + void* userCookie); + + /** + * @typedef OnTcpConnectionStart + * A callback invoked when a new TCP connection is identified (whether it + * begins with a SYN packet or not) + * @param[in] connectionData Connection information + * @param[in] userCookie A pointer to the cookie provided by the user in + * TcpReassembly c'tor (or NULL if no cookie provided) + */ + typedef void (*OnTcpConnectionStart)(const ConnectionData& connectionData, + void* userCookie); + + /** + * @typedef OnTcpConnectionEnd + * A callback invoked when a TCP connection is terminated, either by a FIN or + * RST packet or manually by the user + * @param[in] connectionData Connection information + * @param[in] reason The reason for connection termination: FIN/RST packet or + * manually by the user + * @param[in] userCookie A pointer to the cookie provided by the user in + * TcpReassembly c'tor (or NULL if no cookie provided) + */ + typedef void (*OnTcpConnectionEnd)(const ConnectionData& connectionData, + ConnectionEndReason reason, + void* userCookie); + + /** + * A c'tor for this class + * @param[in] onMessageReadyCallback The callback to be invoked when new data + * arrives + * @param[in] userCookie A pointer to an object provided by the user. This + * pointer will be returned when invoking the various callbacks. This + * parameter is optional, default cookie is NULL + * @param[in] onConnectionStartCallback The callback to be invoked when a new + * connection is identified. This parameter is optional + * @param[in] onConnectionEndCallback The callback to be invoked when a new + * connection is terminated (either by a FIN/RST packet or manually by the + * user). This parameter is optional + * @param[in] config Optional parameter for defining special configuration + * parameters. If not set the default parameters will be set + */ + explicit TcpReassembly( + OnTcpMessageReady onMessageReadyCallback, void* userCookie = NULL, + OnTcpConnectionStart onConnectionStartCallback = NULL, + OnTcpConnectionEnd onConnectionEndCallback = NULL, + const TcpReassemblyConfiguration& config = TcpReassemblyConfiguration()); + + /** + * The most important method of this class which gets a packet from the user + * and processes it. If this packet opens a new connection, ends a connection + * or contains new data on an existing connection, the relevant callback will + * be called (TcpReassembly#OnTcpMessageReady, + * TcpReassembly#OnTcpConnectionStart, TcpReassembly#OnTcpConnectionEnd) + * @param[in] tcpData A reference to the packet to process + * @return A enum of `TcpReassembly::ReassemblyStatus`, indicating status of + * TCP reassembly + */ + ReassemblyStatus reassemblePacket(Packet& tcpData); + + /** + * The most important method of this class which gets a raw packet from the + * user and processes it. If this packet opens a new connection, ends a + * connection or contains new data on an existing connection, the relevant + * callback will be invoked (TcpReassembly#OnTcpMessageReady, + * TcpReassembly#OnTcpConnectionStart, TcpReassembly#OnTcpConnectionEnd) + * @param[in] tcpRawData A reference to the raw packet to process + * @return A enum of `TcpReassembly::ReassemblyStatus`, indicating status of + * TCP reassembly + */ + ReassemblyStatus reassemblePacket(RawPacket* tcpRawData); + + /** + * Close a connection manually. If the connection doesn't exist or already + * closed an error log is printed. This method will cause the + * TcpReassembly#OnTcpConnectionEnd to be invoked with a reason of + * TcpReassembly#TcpReassemblyConnectionClosedManually + * @param[in] flowKey A 4-byte hash key representing the connection. Can be + * taken from a ConnectionData instance + */ + void closeConnection(uint32_t flowKey); + + /** + * Close all open connections manually. This method will cause the + * TcpReassembly#OnTcpConnectionEnd to be invoked for each connection with a + * reason of TcpReassembly#TcpReassemblyConnectionClosedManually + */ + void closeAllConnections(); + + /** + * Get a map of all connections managed by this TcpReassembly instance (both + * connections that are open and those that are already closed) + * @return A map of all connections managed. Notice this map is constant and + * cannot be changed by the user + */ + const ConnectionInfoList& getConnectionInformation() const { + return m_ConnectionInfo; + } + + /** + * Check if a certain connection managed by this TcpReassembly instance is + * currently opened or closed + * @param[in] connection The connection to check + * @return A positive number (> 0) if connection is opened, zero (0) if + * connection is closed, and a negative number (< 0) if this connection isn't + * managed by this TcpReassembly instance + */ + int isConnectionOpen(const ConnectionData& connection) const; + + /** + * Clean up the closed connections from the memory + * @param[in] maxNumToClean The maximum number of items to be cleaned up per + * one call. This parameter, when its value is not zero, overrides the value + * that was set by the constructor. + * @return The number of cleared items + */ + uint32_t purgeClosedConnections(uint32_t maxNumToClean = 0); + + private: + struct TcpFragment { + uint32_t sequence; + size_t dataLength; + uint8_t* data; + timeval timestamp; + + TcpFragment() : sequence(0), dataLength(0), data(NULL) {} + ~TcpFragment() { delete[] data; } + }; + + struct TcpOneSideData { + IPAddress srcIP; + uint16_t srcPort; + uint32_t sequence; + PointerVector tcpFragmentList; + bool gotFinOrRst; + + TcpOneSideData() : srcPort(0), sequence(0), gotFinOrRst(false) {} + }; + + struct TcpReassemblyData { + bool closed; + int8_t numOfSides; + int8_t prevSide; + TcpOneSideData twoSides[2]; + ConnectionData connData; + + TcpReassemblyData() : closed(false), numOfSides(0), prevSide(-1) {} + }; + + typedef std::map ConnectionList; + typedef std::map> CleanupList; + + OnTcpMessageReady m_OnMessageReadyCallback; + OnTcpConnectionStart m_OnConnStart; + OnTcpConnectionEnd m_OnConnEnd; + void* m_UserCookie; + ConnectionList m_ConnectionList; + ConnectionInfoList m_ConnectionInfo; + CleanupList m_CleanupList; + bool m_RemoveConnInfo; + uint32_t m_ClosedConnectionDelay; + uint32_t m_MaxNumToClean; + size_t m_MaxOutOfOrderFragments; + time_t m_PurgeTimepoint; + bool m_EnableBaseBufferClearCondition; + + void checkOutOfOrderFragments(TcpReassemblyData* tcpReassemblyData, + int8_t sideIndex, bool cleanWholeFragList); + + void handleFinOrRst(TcpReassemblyData* tcpReassemblyData, int8_t sideIndex, + uint32_t flowKey, bool isRst); + + void closeConnectionInternal(uint32_t flowKey, ConnectionEndReason reason); + + void insertIntoCleanupList(uint32_t flowKey); }; -} +} // namespace pcpp #endif /* PACKETPP_TCP_REASSEMBLY */ diff --git a/Packet++/header/TelnetLayer.h b/Packet++/header/TelnetLayer.h index 197126a4e1..f685d68f57 100644 --- a/Packet++/header/TelnetLayer.h +++ b/Packet++/header/TelnetLayer.h @@ -9,341 +9,371 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ +namespace pcpp { /** * Class for representing the Telnet Layer */ -class TelnetLayer : public Layer -{ +class TelnetLayer : public Layer { private: - // Position iterator for next command - size_t lastPositionOffset; - - // Checks if position is a data field - bool isDataField(uint8_t *pos) const; - // Checks if position is a command field - bool isCommandField(uint8_t *pos) const; - // Returns distance to next IAC - size_t distanceToNextIAC(uint8_t *startPos, size_t maxLength); - // Returns length of provided field - size_t getFieldLen(uint8_t *startPos, size_t maxLength); - // Get position of next data field - uint8_t *getNextDataField(uint8_t *pos, size_t len); - // Get position of next command field - uint8_t *getNextCommandField(uint8_t *pos, size_t len); - // Get options of provided field - int16_t getSubCommand(uint8_t *pos, size_t len); - // Get data of provided field - uint8_t *getCommandData(uint8_t *pos, size_t &slen); + // Position iterator for next command + size_t lastPositionOffset; + + // Checks if position is a data field + bool isDataField(uint8_t* pos) const; + // Checks if position is a command field + bool isCommandField(uint8_t* pos) const; + // Returns distance to next IAC + size_t distanceToNextIAC(uint8_t* startPos, size_t maxLength); + // Returns length of provided field + size_t getFieldLen(uint8_t* startPos, size_t maxLength); + // Get position of next data field + uint8_t* getNextDataField(uint8_t* pos, size_t len); + // Get position of next command field + uint8_t* getNextCommandField(uint8_t* pos, size_t len); + // Get options of provided field + int16_t getSubCommand(uint8_t* pos, size_t len); + // Get data of provided field + uint8_t* getCommandData(uint8_t* pos, size_t& slen); public: - /** - * Telnet Command Indicator - */ - enum class TelnetCommand : int - { - /// Indicator to parser reached end of packet - TelnetCommandEndOfPacket = -1, - - /// End of file - EndOfFile = 236, - /// Suspend current process - Suspend, - /// Abort Process - Abort, - /// End of Record - EndOfRecordCommand, - /// Marks the end of a Telnet option subnegotiation, used with the SB code to specify more specific option - /// parameters. - SubnegotiationEnd, - /// Null command; does nothing. - NoOperation, - /// Used to mark the end of a sequence of data that the recipient should scan for urgent Telnet commands. - DataMark, - /// Represents the pressing of the “break” or “attention” key on the terminal. - Break, - /// Tells the recipient to interrupt, abort, suspend or terminate the process currently in use. - InterruptProcess, - /// Instructs the remote host to continue running the current process, but discard all remaining output from it. - /// This may be needed if a program starts to send unexpectedly large amounts of data to the user. - AbortOutput, - /// May be used to check that the remote host is still “alive”. When this character is sent the remote host - /// returns some type of output to indicate that it is still functioning. - AreYouThere, - /// Instructs the recipient to delete the last undeleted character from the data stream. Used to “undo” the - /// sending of a character. - EraseCharacter, - /// Tells the recipient to delete all characters from the data stream back to (but not including) the last end - /// of line (CR+LF) sequence. - EraseLine, - /// Used in Telnet half-duplex mode to signal the other device that it may transmit. - GoAhead, - /// Marks the beginning of a Telnet option subnegotiation, used when an option requires the client and server to - /// exchange parameters. - Subnegotiation, - /// Indicates that the device sending this code is willing to perform or continue performing a particular - /// option. - WillPerform, - /// Indicates that the device sending this code is either not willing to perform a particular option, or is now - /// refusing to continue to perform it. - WontPerform, - /// Requests that the other device perform a particular option or confirms the expectation that the other device - /// will perform that option. - DoPerform, - /// Specifies that the other party not perform an option, or confirms a device’s expectation that the other - /// party not perform an option. - DontPerform, - /// Precedes command values 240 through 254 as described above. A pair of IAC bytes in a row represents the data - /// value 255. - InterpretAsCommand - }; - - /** - * Telnet Options - */ - enum class TelnetOption : int - { - /// Internal return for no option detected - TelnetOptionNoOption = -1, - - /// Binary Transmission RFC856 https://www.iana.org/go/rfc856 - TransmitBinary = 0, - /// Echo RFC857 https://www.iana.org/go/rfc857 - Echo, - /// Reconnection - Reconnection, - /// Suppress Go Ahead RFC858 https://www.iana.org/go/rfc858 - SuppressGoAhead, - /// Negotiate approximate message size - ApproxMsgSizeNegotiation, - /// Status RFC859 https://www.iana.org/go/rfc859 - Status, - /// Timing Mark RFC860 https://www.iana.org/go/rfc860 - TimingMark, - /// RCTE, Remote Controlled Transmission and Echo RFC726 https://www.iana.org/go/rfc726 - RemoteControlledTransAndEcho, - /// Output Line Width - OutputLineWidth, - /// Output Page Size - OutputPageSize, - /// NAOCRD, Negotiate About Output Carriage-Return Disposition RFC652 https://www.iana.org/go/rfc652 - OutputCarriageReturnDisposition, - /// NAOHTS, Negotiate About Output Horizontal Tabstops RFC653 https://www.iana.org/go/rfc653 - OutputHorizontalTabStops, - /// NAOHTD, Negotiate About Output Horizontal Tab Disposition RFC654 https://www.iana.org/go/rfc654 - OutputHorizontalTabDisposition, - /// NAOFFD, Negotiate About Output Formfeed Disposition RFC655 https://www.iana.org/go/rfc655 - OutputFormfeedDisposition, - /// NAOVTS, Negotiate About Vertical Tabstops RFC656 https://www.iana.org/go/rfc656 - OutputVerticalTabStops, - /// NAOVTD, Negotiate About Output Vertcial Tab Disposition RFC657 https://www.iana.org/go/rfc657 - OutputVerticalTabDisposition, - /// NAOLFD, Negotiate About Output Linefeed Disposition RFC658 https://www.iana.org/go/rfc658 - OutputLinefeedDisposition, - /// Extended ASCII RFC698 https://www.iana.org/go/rfc698 - ExtendedASCII, - /// Logout RFC727 https://www.iana.org/go/rfc727 - Logout, - /// BM, Byte Macro RFC735 https://www.iana.org/go/rfc735 - ByteMacro, - /// Data Entry Terminal RFC1043 - RFC732 https://www.iana.org/go/rfc1043 https://www.iana.org/go/rfc732 - DataEntryTerminal, - /// SUPDUP RFC736 - RFC734 https://www.iana.org/go/rfc736 https://www.iana.org/go/rfc734 - SUPDUP, - /// SUPDUP Output RFC749 https://www.iana.org/go/rfc749 - SUPDUPOutput, - /// Send Location RFC779 https://www.iana.org/go/rfc779 - SendLocation, - /// Terminal Type RFC1091 https://www.iana.org/go/rfc1091 - TerminalType, - /// End of record RFC885 https://www.iana.org/go/rfc885 - EndOfRecordOption, - /// TUID, TACACS User Identification RFC927 https://www.iana.org/go/rfc927 - TACACSUserIdentification, - /// OUTMRK, Output Marking RFC933 https://www.iana.org/go/rfc933 - OutputMarking, - /// TTYLOC, Terminal Location Number RFC946 https://www.iana.org/go/rfc946 - TerminalLocationNumber, - /// Telnet 3270 Regime RFC1041 https://www.iana.org/go/rfc1041 - Telnet3270Regime, - /// X.3 PAD RFC1053 https://www.iana.org/go/rfc1053 - X3Pad, - /// NAWS, Negotiate About Window Size RFC1073 https://www.iana.org/go/rfc1073 - NegotiateAboutWindowSize, - /// Terminal Speed RFC1079 https://www.iana.org/go/rfc1079 - TerminalSpeed, - /// Remote Flow Control RFC1372 https://www.iana.org/go/rfc1372 - RemoteFlowControl, - /// Line Mode RFC1184 https://www.iana.org/go/rfc1184 - Linemode, - /// X Display Location RFC1096 https://www.iana.org/go/rfc1096 - XDisplayLocation, - /// Environment Option RFC1408 https://www.iana.org/go/rfc1408 - EnvironmentOption, - /// Authentication Option RFC2941 https://www.iana.org/go/rfc2941 - AuthenticationOption, - /// Encryption Option RFC2946 https://www.iana.org/go/rfc2946 - EncryptionOption, - /// New Environment Option RFC1572 https://www.iana.org/go/rfc1572 - NewEnvironmentOption, - /// TN3270E RFC2355 https://www.iana.org/go/rfc2355 - TN3270E, - /// X Server Authentication - XAuth, - /// Charset RFC2066 https://www.iana.org/go/rfc2066 - Charset, - /// RSP, Telnet Remote Serial Port - TelnetRemoteSerialPort, - /// Com Port Control Option RFC2217 https://www.iana.org/go/rfc2217 - ComPortControlOption, - /// Telnet Suppress Local Echo - TelnetSuppressLocalEcho, - /// Telnet Start TLS - TelnetStartTLS, - /// Kermit RFC2840 https://www.iana.org/go/rfc2840 - Kermit, - /// Send URL - SendURL, - /// Forward X Server - ForwardX, - - /// Telnet Option Pragma Logon - TelOptPragmaLogon = 138, - /// Telnet Option SSPI Logon - TelOptSSPILogon, - /// Telnet Option Pragma Heartbeat - TelOptPragmaHeartbeat, - - /// Extended option list - ExtendedOptions = 255 - }; - - /** - * 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 - */ - TelnetLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) : Layer(data, dataLen, prevLayer, packet) - { - m_Protocol = Telnet; - lastPositionOffset = SIZE_MAX; - }; - - /** - * Get the Telnet data as readable string - * @param[in] removeEscapeCharacters Whether non-alphanumerical characters should be removed or not - * @return Full payload as readable string, empty if Telnet packet contains control commands/options. - */ - std::string getDataAsString(bool removeEscapeCharacters = true); - - /** - * Get the total number of detected Telnet commands - * @return size_t The number of Telnet commands - */ - size_t getTotalNumberOfCommands(); - - /** - * Returns the number of occurrences of provided command - * @param[in] command Telnet command to count - * @return size_t Number of occurrences of command - */ - size_t getNumberOfCommands(TelnetCommand command); - - /** - * Returns the first command of packet - * @return TelnetCommand First detected command value, TelnetCommandEndOfPacket if there is no command field - */ - TelnetCommand getFirstCommand(); - - /** - * Returns the next command of packet. Uses an internal iterator. The iterator resets when reached end of packet. - * @return TelnetCommand Detected command value, TelnetCommandEndOfPacket if reached the end of packet. - */ - TelnetCommand getNextCommand(); - - /** - * Returns the option of current command. Uses an internal iterator. Iterator can be moved with getNextCommand - * @return TelnetOption Option of current command - */ - TelnetOption getOption(); - - /** - * Returns the option of provided command. It will return option of first occurrence of the command - * @param[in] command Telnet command to search - * @return TelnetOption Option of the command. Returns TelnetOptionNoOption if the provided command not found. - */ - TelnetOption getOption(TelnetCommand command); - - /** - * Returns the data of current command. Uses an internal iterator. Iterator can be moved with getNextCommand - * @param[out] length Length of the data of current command - * @return uint8_t* Pointer to the data of current command. NULL if there is no data for this command. - */ - uint8_t *getOptionData(size_t &length); - - /** - * Returns the data of provided command. It will return data of first occurrence of the command - * @param[in] command Telnet command to search - * @param[out] length Length of the data of current command - * @return uint8_t* Pointer to the data of current command. NULL if there is no data for this command or if can't - * find the command. - */ - uint8_t *getOptionData(TelnetCommand command, size_t &length); - - /** - * Convert the Telnet Command to readable string - * @param[in] val Value of the command - * @return The Telnet Command as readable string - */ - static std::string getTelnetCommandAsString(TelnetCommand val); - - /** - * Convert the Telnet option to readable string - * @param[in] val Value of the option - * @return The Telnet Option as readable string - */ - static std::string getTelnetOptionAsString(TelnetOption val); - - /** - * A static method that checks whether the port is considered as Telnet - * @param[in] port The port number to be checked - */ - static bool isTelnetPort(uint16_t port) { return port == 23; } - - /** - * A static method that takes a byte array and detects whether it is a Telnet message - * @param[in] data A byte array - * @param[in] dataSize The byte array size (in bytes) - * @return True if the data is identified as Telnet message - */ - static bool isDataValid(const uint8_t *data, size_t dataSize) { return data && dataSize; } - - // overridden methods - - /// Parses the next layer. Telnet is the always last so does nothing for this layer - void parseNextLayer() {} - - /** - * @return Get the size of the layer - */ - size_t getHeaderLen() const { return m_DataLen; } - - /// Does nothing for this layer - void computeCalculateFields() {} - - /** - * @return The OSI layer level of Telnet (Application Layer). - */ - OsiModelLayer getOsiModelLayer() const { return OsiModelApplicationLayer; } - - /** - * @return Returns the protocol info as readable string - */ - std::string toString() const; + /** + * Telnet Command Indicator + */ + enum class TelnetCommand : int { + /// Indicator to parser reached end of packet + TelnetCommandEndOfPacket = -1, + + /// End of file + EndOfFile = 236, + /// Suspend current process + Suspend, + /// Abort Process + Abort, + /// End of Record + EndOfRecordCommand, + /// Marks the end of a Telnet option subnegotiation, used with the SB code + /// to specify more specific option parameters. + SubnegotiationEnd, + /// Null command; does nothing. + NoOperation, + /// Used to mark the end of a sequence of data that the recipient should + /// scan for urgent Telnet commands. + DataMark, + /// Represents the pressing of the “break” or “attention” key on the + /// terminal. + Break, + /// Tells the recipient to interrupt, abort, suspend or terminate the + /// process currently in use. + InterruptProcess, + /// Instructs the remote host to continue running the current process, but + /// discard all remaining output from it. This may be needed if a program + /// starts to send unexpectedly large amounts of data to the user. + AbortOutput, + /// May be used to check that the remote host is still “alive”. When this + /// character is sent the remote host returns some type of output to + /// indicate that it is still functioning. + AreYouThere, + /// Instructs the recipient to delete the last undeleted character from the + /// data stream. Used to “undo” the sending of a character. + EraseCharacter, + /// Tells the recipient to delete all characters from the data stream back + /// to (but not including) the last end of line (CR+LF) sequence. + EraseLine, + /// Used in Telnet half-duplex mode to signal the other device that it may + /// transmit. + GoAhead, + /// Marks the beginning of a Telnet option subnegotiation, used when an + /// option requires the client and server to exchange parameters. + Subnegotiation, + /// Indicates that the device sending this code is willing to perform or + /// continue performing a particular option. + WillPerform, + /// Indicates that the device sending this code is either not willing to + /// perform a particular option, or is now refusing to continue to perform + /// it. + WontPerform, + /// Requests that the other device perform a particular option or confirms + /// the expectation that the other device will perform that option. + DoPerform, + /// Specifies that the other party not perform an option, or confirms a + /// device’s expectation that the other party not perform an option. + DontPerform, + /// Precedes command values 240 through 254 as described above. A pair of + /// IAC bytes in a row represents the data value 255. + InterpretAsCommand + }; + + /** + * Telnet Options + */ + enum class TelnetOption : int { + /// Internal return for no option detected + TelnetOptionNoOption = -1, + + /// Binary Transmission RFC856 https://www.iana.org/go/rfc856 + TransmitBinary = 0, + /// Echo RFC857 https://www.iana.org/go/rfc857 + Echo, + /// Reconnection + Reconnection, + /// Suppress Go Ahead RFC858 https://www.iana.org/go/rfc858 + SuppressGoAhead, + /// Negotiate approximate message size + ApproxMsgSizeNegotiation, + /// Status RFC859 https://www.iana.org/go/rfc859 + Status, + /// Timing Mark RFC860 https://www.iana.org/go/rfc860 + TimingMark, + /// RCTE, Remote Controlled Transmission and Echo RFC726 + /// https://www.iana.org/go/rfc726 + RemoteControlledTransAndEcho, + /// Output Line Width + OutputLineWidth, + /// Output Page Size + OutputPageSize, + /// NAOCRD, Negotiate About Output Carriage-Return Disposition RFC652 + /// https://www.iana.org/go/rfc652 + OutputCarriageReturnDisposition, + /// NAOHTS, Negotiate About Output Horizontal Tabstops RFC653 + /// https://www.iana.org/go/rfc653 + OutputHorizontalTabStops, + /// NAOHTD, Negotiate About Output Horizontal Tab Disposition RFC654 + /// https://www.iana.org/go/rfc654 + OutputHorizontalTabDisposition, + /// NAOFFD, Negotiate About Output Formfeed Disposition RFC655 + /// https://www.iana.org/go/rfc655 + OutputFormfeedDisposition, + /// NAOVTS, Negotiate About Vertical Tabstops RFC656 + /// https://www.iana.org/go/rfc656 + OutputVerticalTabStops, + /// NAOVTD, Negotiate About Output Vertcial Tab Disposition RFC657 + /// https://www.iana.org/go/rfc657 + OutputVerticalTabDisposition, + /// NAOLFD, Negotiate About Output Linefeed Disposition RFC658 + /// https://www.iana.org/go/rfc658 + OutputLinefeedDisposition, + /// Extended ASCII RFC698 https://www.iana.org/go/rfc698 + ExtendedASCII, + /// Logout RFC727 https://www.iana.org/go/rfc727 + Logout, + /// BM, Byte Macro RFC735 https://www.iana.org/go/rfc735 + ByteMacro, + /// Data Entry Terminal RFC1043 - RFC732 https://www.iana.org/go/rfc1043 + /// https://www.iana.org/go/rfc732 + DataEntryTerminal, + /// SUPDUP RFC736 - RFC734 https://www.iana.org/go/rfc736 + /// https://www.iana.org/go/rfc734 + SUPDUP, + /// SUPDUP Output RFC749 https://www.iana.org/go/rfc749 + SUPDUPOutput, + /// Send Location RFC779 https://www.iana.org/go/rfc779 + SendLocation, + /// Terminal Type RFC1091 https://www.iana.org/go/rfc1091 + TerminalType, + /// End of record RFC885 https://www.iana.org/go/rfc885 + EndOfRecordOption, + /// TUID, TACACS User Identification RFC927 https://www.iana.org/go/rfc927 + TACACSUserIdentification, + /// OUTMRK, Output Marking RFC933 https://www.iana.org/go/rfc933 + OutputMarking, + /// TTYLOC, Terminal Location Number RFC946 https://www.iana.org/go/rfc946 + TerminalLocationNumber, + /// Telnet 3270 Regime RFC1041 https://www.iana.org/go/rfc1041 + Telnet3270Regime, + /// X.3 PAD RFC1053 https://www.iana.org/go/rfc1053 + X3Pad, + /// NAWS, Negotiate About Window Size RFC1073 + /// https://www.iana.org/go/rfc1073 + NegotiateAboutWindowSize, + /// Terminal Speed RFC1079 https://www.iana.org/go/rfc1079 + TerminalSpeed, + /// Remote Flow Control RFC1372 https://www.iana.org/go/rfc1372 + RemoteFlowControl, + /// Line Mode RFC1184 https://www.iana.org/go/rfc1184 + Linemode, + /// X Display Location RFC1096 https://www.iana.org/go/rfc1096 + XDisplayLocation, + /// Environment Option RFC1408 https://www.iana.org/go/rfc1408 + EnvironmentOption, + /// Authentication Option RFC2941 https://www.iana.org/go/rfc2941 + AuthenticationOption, + /// Encryption Option RFC2946 https://www.iana.org/go/rfc2946 + EncryptionOption, + /// New Environment Option RFC1572 https://www.iana.org/go/rfc1572 + NewEnvironmentOption, + /// TN3270E RFC2355 https://www.iana.org/go/rfc2355 + TN3270E, + /// X Server Authentication + XAuth, + /// Charset RFC2066 https://www.iana.org/go/rfc2066 + Charset, + /// RSP, Telnet Remote Serial Port + TelnetRemoteSerialPort, + /// Com Port Control Option RFC2217 https://www.iana.org/go/rfc2217 + ComPortControlOption, + /// Telnet Suppress Local Echo + TelnetSuppressLocalEcho, + /// Telnet Start TLS + TelnetStartTLS, + /// Kermit RFC2840 https://www.iana.org/go/rfc2840 + Kermit, + /// Send URL + SendURL, + /// Forward X Server + ForwardX, + + /// Telnet Option Pragma Logon + TelOptPragmaLogon = 138, + /// Telnet Option SSPI Logon + TelOptSSPILogon, + /// Telnet Option Pragma Heartbeat + TelOptPragmaHeartbeat, + + /// Extended option list + ExtendedOptions = 255 + }; + + /** + * 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 + */ + TelnetLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : Layer(data, dataLen, prevLayer, packet) { + m_Protocol = Telnet; + lastPositionOffset = SIZE_MAX; + }; + + /** + * Get the Telnet data as readable string + * @param[in] removeEscapeCharacters Whether non-alphanumerical characters + * should be removed or not + * @return Full payload as readable string, empty if Telnet packet contains + * control commands/options. + */ + std::string getDataAsString(bool removeEscapeCharacters = true); + + /** + * Get the total number of detected Telnet commands + * @return size_t The number of Telnet commands + */ + size_t getTotalNumberOfCommands(); + + /** + * Returns the number of occurrences of provided command + * @param[in] command Telnet command to count + * @return size_t Number of occurrences of command + */ + size_t getNumberOfCommands(TelnetCommand command); + + /** + * Returns the first command of packet + * @return TelnetCommand First detected command value, + * TelnetCommandEndOfPacket if there is no command field + */ + TelnetCommand getFirstCommand(); + + /** + * Returns the next command of packet. Uses an internal iterator. The iterator + * resets when reached end of packet. + * @return TelnetCommand Detected command value, TelnetCommandEndOfPacket if + * reached the end of packet. + */ + TelnetCommand getNextCommand(); + + /** + * Returns the option of current command. Uses an internal iterator. Iterator + * can be moved with getNextCommand + * @return TelnetOption Option of current command + */ + TelnetOption getOption(); + + /** + * Returns the option of provided command. It will return option of first + * occurrence of the command + * @param[in] command Telnet command to search + * @return TelnetOption Option of the command. Returns TelnetOptionNoOption if + * the provided command not found. + */ + TelnetOption getOption(TelnetCommand command); + + /** + * Returns the data of current command. Uses an internal iterator. Iterator + * can be moved with getNextCommand + * @param[out] length Length of the data of current command + * @return uint8_t* Pointer to the data of current command. NULL if there is + * no data for this command. + */ + uint8_t* getOptionData(size_t& length); + + /** + * Returns the data of provided command. It will return data of first + * occurrence of the command + * @param[in] command Telnet command to search + * @param[out] length Length of the data of current command + * @return uint8_t* Pointer to the data of current command. NULL if there is + * no data for this command or if can't find the command. + */ + uint8_t* getOptionData(TelnetCommand command, size_t& length); + + /** + * Convert the Telnet Command to readable string + * @param[in] val Value of the command + * @return The Telnet Command as readable string + */ + static std::string getTelnetCommandAsString(TelnetCommand val); + + /** + * Convert the Telnet option to readable string + * @param[in] val Value of the option + * @return The Telnet Option as readable string + */ + static std::string getTelnetOptionAsString(TelnetOption val); + + /** + * A static method that checks whether the port is considered as Telnet + * @param[in] port The port number to be checked + */ + static bool isTelnetPort(uint16_t port) { return port == 23; } + + /** + * A static method that takes a byte array and detects whether it is a Telnet + * message + * @param[in] data A byte array + * @param[in] dataSize The byte array size (in bytes) + * @return True if the data is identified as Telnet message + */ + static bool isDataValid(const uint8_t* data, size_t dataSize) { + return data && dataSize; + } + + // overridden methods + + /// Parses the next layer. Telnet is the always last so does nothing for this + /// layer + void parseNextLayer() {} + + /** + * @return Get the size of the layer + */ + size_t getHeaderLen() const { return m_DataLen; } + + /// Does nothing for this layer + void computeCalculateFields() {} + + /** + * @return The OSI layer level of Telnet (Application Layer). + */ + OsiModelLayer getOsiModelLayer() const { return OsiModelApplicationLayer; } + + /** + * @return Returns the protocol info as readable string + */ + std::string toString() const; }; } // namespace pcpp diff --git a/Packet++/header/TextBasedProtocol.h b/Packet++/header/TextBasedProtocol.h index 2cb6fa9fa2..ee9cc51c56 100644 --- a/Packet++/header/TextBasedProtocol.h +++ b/Packet++/header/TextBasedProtocol.h @@ -1,263 +1,308 @@ #ifndef PACKETPP_TEXT_BASED_PROTOCOL_LAYER #define PACKETPP_TEXT_BASED_PROTOCOL_LAYER -#include #include "Layer.h" +#include /// @file -namespace pcpp -{ +namespace pcpp { /** End of header */ #define PCPP_END_OF_TEXT_BASED_PROTOCOL_HEADER "" class TextBasedProtocolMessage; - // -------- Class HeaderField ----------------- - /** * @class HeaderField - * A wrapper class for each text-based-protocol header field, e.g "Host", "Cookie", "Content-Length", "Via", "Call-ID", etc. - * Each field contains a name (e.g "Host") and a value (e.g "www.wikipedia.org"). The user can get and set both of them through dedicated methods. - * The separator between header fields is either CRLF ("\r\n\") or LF ("\n") in more rare cases, which means every HeaderField instance is - * responsible for wrapping and parsing a header field from the previous CRLF (not inclusive) until the next CRLF/LF (inclusive) - * A special case is with the end of a header, meaning 2 consecutive CRLFs ("\r\n\r\n") or consecutive LFs ("\n\n"). PcapPlusPlus treats the first - * CRLF/LF as part of the last field in the header, and the second CRLF is an HeaderField instance of its own which name and values are an empty string ("") - * or pcpp::PCPP_END_OF_TEXT_BASED_PROTOCOL_HEADER + * A wrapper class for each text-based-protocol header field, e.g "Host", + * "Cookie", "Content-Length", "Via", "Call-ID", etc. Each field contains a name + * (e.g "Host") and a value (e.g "www.wikipedia.org"). The user can get and set + * both of them through dedicated methods. The separator between header fields + * is either CRLF ("\r\n\") or LF ("\n") in more rare cases, which means every + * HeaderField instance is responsible for wrapping and parsing a header field + * from the previous CRLF (not inclusive) until the next CRLF/LF (inclusive) A + * special case is with the end of a header, meaning 2 consecutive CRLFs + * ("\r\n\r\n") or consecutive LFs ("\n\n"). PcapPlusPlus treats the first + * CRLF/LF as part of the last field in the header, and the second CRLF is an + * HeaderField instance of its own which name and values are an empty string + * ("") or pcpp::PCPP_END_OF_TEXT_BASED_PROTOCOL_HEADER */ -class HeaderField -{ - friend class TextBasedProtocolMessage; -public: - - ~HeaderField(); - - /** - * A copy constructor that creates a new instance out of an existing HeaderField instance. The copied instance will not have shared - * resources with the original instance, meaning all members and properties are copied - * @param[in] other The original instance to copy from - */ - HeaderField(const HeaderField& other); - - /** - * Assignment operator for this class. This method copies the data from the other instance and will not share any resources with it. - * Also, if the instance already contains data it will be deleted or zeroed - * @param[in] other The instance to assign from - * @return A reference to the assignee - */ - HeaderField& operator=(const HeaderField& other); - - /** - * @return The field length in bytes, meaning count of all characters from the previous CRLF (not inclusive) until the next CRLF (inclusive) - * For example: the field "Host: www.wikipedia.org\r\n" will have the length of 25 - */ - size_t getFieldSize() const { return m_FieldSize; } - - /** - * @return The field name as string. Notice the return data is copied data, so changing it won't change the packet data - */ - std::string getFieldName() const; - - /** - * @return The field value as string. Notice the return data is copied data, so changing it won't change the packet data - */ - std::string getFieldValue() const; - - /** - * A setter for field value - * @param[in] newValue The new value to set to the field. Old value will be deleted - * @return True if setting the value was completed successfully, false otherwise - */ - bool setFieldValue(const std::string& newValue); - - /** - * Get an indication whether the field is a field that ends the header (meaning contain only CRLF - see class explanation) - * @return True if this is a end-of-header field, false otherwise - */ - bool isEndOfHeader() const { return m_IsEndOfHeaderField; } - -private: - HeaderField(const std::string& name, const std::string& value, char nameValueSeparator, bool spacesAllowedBetweenNameAndValue); - HeaderField(TextBasedProtocolMessage* TextBasedProtocolMessage, int offsetInMessage, char nameValueSeparator, bool spacesAllowedBetweenNameAndValue); - - char* getData() const; - void setNextField(HeaderField* nextField); - HeaderField *getNextField() const; - void initNewField(const std::string& name, const std::string& value); - void attachToTextBasedProtocolMessage(TextBasedProtocolMessage* message, int fieldOffsetInMessage); - - uint8_t* m_NewFieldData; - TextBasedProtocolMessage* m_TextBasedProtocolMessage; - int m_NameOffsetInMessage; - size_t m_FieldNameSize; - int m_ValueOffsetInMessage; - size_t m_FieldValueSize; - size_t m_FieldSize; - HeaderField* m_NextField; - bool m_IsEndOfHeaderField; - char m_NameValueSeparator; - bool m_SpacesAllowedBetweenNameAndValue; +class HeaderField { + friend class TextBasedProtocolMessage; + + public: + ~HeaderField(); + + /** + * A copy constructor that creates a new instance out of an existing + * HeaderField instance. The copied instance will not have shared resources + * with the original instance, meaning all members and properties are copied + * @param[in] other The original instance to copy from + */ + HeaderField(const HeaderField& other); + + /** + * Assignment operator for this class. This method copies the data from the + * other instance and will not share any resources with it. Also, if the + * instance already contains data it will be deleted or zeroed + * @param[in] other The instance to assign from + * @return A reference to the assignee + */ + HeaderField& operator=(const HeaderField& other); + + /** + * @return The field length in bytes, meaning count of all characters from the + * previous CRLF (not inclusive) until the next CRLF (inclusive) For example: + * the field "Host: www.wikipedia.org\r\n" will have the length of 25 + */ + size_t getFieldSize() const { return m_FieldSize; } + + /** + * @return The field name as string. Notice the return data is copied data, so + * changing it won't change the packet data + */ + std::string getFieldName() const; + + /** + * @return The field value as string. Notice the return data is copied data, + * so changing it won't change the packet data + */ + std::string getFieldValue() const; + + /** + * A setter for field value + * @param[in] newValue The new value to set to the field. Old value will be + * deleted + * @return True if setting the value was completed successfully, false + * otherwise + */ + bool setFieldValue(const std::string& newValue); + + /** + * Get an indication whether the field is a field that ends the header + * (meaning contain only CRLF - see class explanation) + * @return True if this is a end-of-header field, false otherwise + */ + bool isEndOfHeader() const { return m_IsEndOfHeaderField; } + + private: + HeaderField(const std::string& name, const std::string& value, + char nameValueSeparator, bool spacesAllowedBetweenNameAndValue); + HeaderField(TextBasedProtocolMessage* TextBasedProtocolMessage, + int offsetInMessage, char nameValueSeparator, + bool spacesAllowedBetweenNameAndValue); + + char* getData() const; + void setNextField(HeaderField* nextField); + HeaderField* getNextField() const; + void initNewField(const std::string& name, const std::string& value); + void attachToTextBasedProtocolMessage(TextBasedProtocolMessage* message, + int fieldOffsetInMessage); + + uint8_t* m_NewFieldData; + TextBasedProtocolMessage* m_TextBasedProtocolMessage; + int m_NameOffsetInMessage; + size_t m_FieldNameSize; + int m_ValueOffsetInMessage; + size_t m_FieldValueSize; + size_t m_FieldSize; + HeaderField* m_NextField; + bool m_IsEndOfHeaderField; + char m_NameValueSeparator; + bool m_SpacesAllowedBetweenNameAndValue; }; - - - // -------- Class TextBasedProtocolMessage ----------------- /** * @class TextBasedProtocolMessage - * An abstract base class that wraps text-based-protocol header layers (both requests and responses). It is the base class for all those layers. - * This class is not meant to be instantiated, hence the protected c'tor + * An abstract base class that wraps text-based-protocol header layers (both + * requests and responses). It is the base class for all those layers. This + * class is not meant to be instantiated, hence the protected c'tor */ -class TextBasedProtocolMessage : public Layer -{ - friend class HeaderField; -public: - ~TextBasedProtocolMessage(); - - /** - * Get a pointer to a header field by name. The search is case insensitive, meaning if a field with name "Host" exists and the - * fieldName parameter is "host" (all letter are lower case), this method will return a pointer to "Host" field - * @param[in] fieldName The field name - * @param[in] index Optional parameter. If the field name appears more than once, this parameter will indicate which field to get. - * The default value is 0 (get the first appearance of the field name as appears on the packet) - * @return A pointer to an HeaderField instance, or NULL if field doesn't exist - */ - HeaderField* getFieldByName(std::string fieldName, int index = 0) const; - - /** - * @return A pointer to the first header field exists in this message, or NULL if no such field exists - */ - HeaderField* getFirstField() const { return m_FieldList; } - - /** - * Get the field which appears after a certain field - * @param[in] prevField A pointer to the field - * @return The field after prevField or NULL if prevField is the last field. If prevField is NULL, this method will return NULL - */ - HeaderField* getNextField(HeaderField* prevField) const { if (prevField != NULL) return prevField->getNextField(); else return NULL; } - - /** - * @return The number of header fields currently in the layer (not including CRLF at the end of the header) - */ - int getFieldCount() const; - - /** - * Add a new header field to this message. This field will be added last (before the end-of-header field) - * @param[in] fieldName The field name - * @param[in] fieldValue The field value - * @return A pointer to the newly created header field, or NULL if the field could not be created - */ - virtual HeaderField* addField(const std::string& fieldName, const std::string& fieldValue); - - /** - * Add a new header field to this message. This field will be added last (before the end-of-header field) - * @param[in] newField The header field to add - * @return A pointer to the newly created header field, or NULL if the field could not be created - */ - virtual HeaderField* addField(const HeaderField& newField); - - /** - * Add the special end-of-header field (see the explanation in HeaderField) - * @return A pointer to the newly created header field, or NULL if the field could not be created - */ - HeaderField* addEndOfHeader(); - - /** - * Insert a new field after an existing field - * @param[in] prevField A pointer to the existing field. If it's NULL the new field will be added as first field - * @param[in] fieldName The field name - * @param[in] fieldValue The field value - * @return A pointer to the newly created header field, or NULL if the field could not be created - */ - virtual HeaderField* insertField(HeaderField* prevField, const std::string& fieldName, const std::string& fieldValue); - - /** - * Insert a new field after an existing field - * @param[in] prevFieldName A name of an existing field. If the field doesn't exist NULL will be returned. - * If field name is empty ('') the new field will be added as first field - * @param[in] fieldName The field name - * @param[in] fieldValue The field value - * @return A pointer to the newly created header field, or NULL if the field could not be created - */ - virtual HeaderField* insertField(std::string prevFieldName, const std::string& fieldName, const std::string& fieldValue); - - /** - * Insert a new field after an existing field - * @param[in] prevField A pointer to the existing field - * @param[in] newField The header field to add - * @return A pointer to the newly created header field, or NULL if the field could not be created - */ - virtual HeaderField* insertField(HeaderField* prevField, const HeaderField& newField); - - /** - * Remove a field from the message - * @param[in] fieldToRemove A pointer to the field that should be removed - * @return True if the field was removed successfully, or false otherwise (for example: if fieldToRemove is NULL, if it doesn't exist - * in the message, or if the removal failed) - */ - bool removeField(HeaderField* fieldToRemove); - - /** - * Remove a field from the message - * @param[in] fieldName The name of the field that should be removed - * @param[in] index Optional parameter. If the field name appears more than once, this parameter will indicate which field to remove. - * The default value is 0 (remove the first appearance of the field name as appears on the packet) - * @return True if the field was removed successfully, or false otherwise (for example: if fieldName doesn't exist in the message, or if the removal failed) - */ - bool removeField(std::string fieldName, int index = 0); - - /** - * Indicate whether the header is complete (ending with end-of-header "\r\n\r\n" or "\n\n") or spread over more packets - * @return True if the header is complete or false if not - */ - bool isHeaderComplete() const; - - // implement Layer's abstract methods - - /** - * Currently set only PayloadLayer for the rest of the data - */ - virtual void parseNextLayer(); - - /** - * @return The message length - */ - size_t getHeaderLen() const; - - /** - * Does nothing for this class - */ - virtual void computeCalculateFields(); - -protected: - TextBasedProtocolMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); - TextBasedProtocolMessage() : m_FieldList(NULL), m_LastField(NULL), m_FieldsOffset(0) {} - - // copy c'tor - TextBasedProtocolMessage(const TextBasedProtocolMessage& other); - TextBasedProtocolMessage& operator=(const TextBasedProtocolMessage& other); - - void copyDataFrom(const TextBasedProtocolMessage& other); - - void parseFields(); - void shiftFieldsOffset(HeaderField* fromField, int numOfBytesToShift); - - // abstract methods - virtual char getHeaderFieldNameValueSeparator() const = 0; - virtual bool spacesAllowedBetweenHeaderFieldNameAndValue() const = 0; - - HeaderField* m_FieldList; - HeaderField* m_LastField; - int m_FieldsOffset; - std::multimap m_FieldNameToFieldMap; +class TextBasedProtocolMessage : public Layer { + friend class HeaderField; + + public: + ~TextBasedProtocolMessage(); + + /** + * Get a pointer to a header field by name. The search is case insensitive, + * meaning if a field with name "Host" exists and the fieldName parameter is + * "host" (all letter are lower case), this method will return a pointer to + * "Host" field + * @param[in] fieldName The field name + * @param[in] index Optional parameter. If the field name appears more than + * once, this parameter will indicate which field to get. The default value is + * 0 (get the first appearance of the field name as appears on the packet) + * @return A pointer to an HeaderField instance, or NULL if field doesn't + * exist + */ + HeaderField* getFieldByName(std::string fieldName, int index = 0) const; + + /** + * @return A pointer to the first header field exists in this message, or NULL + * if no such field exists + */ + HeaderField* getFirstField() const { return m_FieldList; } + + /** + * Get the field which appears after a certain field + * @param[in] prevField A pointer to the field + * @return The field after prevField or NULL if prevField is the last field. + * If prevField is NULL, this method will return NULL + */ + HeaderField* getNextField(HeaderField* prevField) const { + if (prevField != NULL) + return prevField->getNextField(); + else + return NULL; + } + + /** + * @return The number of header fields currently in the layer (not including + * CRLF at the end of the header) + */ + int getFieldCount() const; + + /** + * Add a new header field to this message. This field will be added last + * (before the end-of-header field) + * @param[in] fieldName The field name + * @param[in] fieldValue The field value + * @return A pointer to the newly created header field, or NULL if the field + * could not be created + */ + virtual HeaderField* addField(const std::string& fieldName, + const std::string& fieldValue); + + /** + * Add a new header field to this message. This field will be added last + * (before the end-of-header field) + * @param[in] newField The header field to add + * @return A pointer to the newly created header field, or NULL if the field + * could not be created + */ + virtual HeaderField* addField(const HeaderField& newField); + + /** + * Add the special end-of-header field (see the explanation in HeaderField) + * @return A pointer to the newly created header field, or NULL if the field + * could not be created + */ + HeaderField* addEndOfHeader(); + + /** + * Insert a new field after an existing field + * @param[in] prevField A pointer to the existing field. If it's NULL the new + * field will be added as first field + * @param[in] fieldName The field name + * @param[in] fieldValue The field value + * @return A pointer to the newly created header field, or NULL if the field + * could not be created + */ + virtual HeaderField* insertField(HeaderField* prevField, + const std::string& fieldName, + const std::string& fieldValue); + + /** + * Insert a new field after an existing field + * @param[in] prevFieldName A name of an existing field. If the field doesn't + * exist NULL will be returned. If field name is empty ('') the new field will + * be added as first field + * @param[in] fieldName The field name + * @param[in] fieldValue The field value + * @return A pointer to the newly created header field, or NULL if the field + * could not be created + */ + virtual HeaderField* insertField(std::string prevFieldName, + const std::string& fieldName, + const std::string& fieldValue); + + /** + * Insert a new field after an existing field + * @param[in] prevField A pointer to the existing field + * @param[in] newField The header field to add + * @return A pointer to the newly created header field, or NULL if the field + * could not be created + */ + virtual HeaderField* insertField(HeaderField* prevField, + const HeaderField& newField); + + /** + * Remove a field from the message + * @param[in] fieldToRemove A pointer to the field that should be removed + * @return True if the field was removed successfully, or false otherwise (for + * example: if fieldToRemove is NULL, if it doesn't exist in the message, or + * if the removal failed) + */ + bool removeField(HeaderField* fieldToRemove); + + /** + * Remove a field from the message + * @param[in] fieldName The name of the field that should be removed + * @param[in] index Optional parameter. If the field name appears more than + * once, this parameter will indicate which field to remove. The default value + * is 0 (remove the first appearance of the field name as appears on the + * packet) + * @return True if the field was removed successfully, or false otherwise (for + * example: if fieldName doesn't exist in the message, or if the removal + * failed) + */ + bool removeField(std::string fieldName, int index = 0); + + /** + * Indicate whether the header is complete (ending with end-of-header + * "\r\n\r\n" or "\n\n") or spread over more packets + * @return True if the header is complete or false if not + */ + bool isHeaderComplete() const; + + // implement Layer's abstract methods + + /** + * Currently set only PayloadLayer for the rest of the data + */ + virtual void parseNextLayer(); + + /** + * @return The message length + */ + size_t getHeaderLen() const; + + /** + * Does nothing for this class + */ + virtual void computeCalculateFields(); + + protected: + TextBasedProtocolMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet); + TextBasedProtocolMessage() + : m_FieldList(NULL), m_LastField(NULL), m_FieldsOffset(0) {} + + // copy c'tor + TextBasedProtocolMessage(const TextBasedProtocolMessage& other); + TextBasedProtocolMessage& operator=(const TextBasedProtocolMessage& other); + + void copyDataFrom(const TextBasedProtocolMessage& other); + + void parseFields(); + void shiftFieldsOffset(HeaderField* fromField, int numOfBytesToShift); + + // abstract methods + virtual char getHeaderFieldNameValueSeparator() const = 0; + virtual bool spacesAllowedBetweenHeaderFieldNameAndValue() const = 0; + + HeaderField* m_FieldList; + HeaderField* m_LastField; + int m_FieldsOffset; + std::multimap m_FieldNameToFieldMap; }; - -} - +} // namespace pcpp #endif // PACKETPP_TEXT_BASED_PROTOCOL_LAYER diff --git a/Packet++/header/TpktLayer.h b/Packet++/header/TpktLayer.h index 051611f002..46bc6fa2ce 100644 --- a/Packet++/header/TpktLayer.h +++ b/Packet++/header/TpktLayer.h @@ -10,125 +10,132 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ +namespace pcpp { - /** - * @struct tpkthdr - * Represents a TPKT protocol header - */ +/** + * @struct tpkthdr + * Represents a TPKT protocol header + */ #pragma pack(push, 1) - struct tpkthdr - { - /** message version */ - uint8_t version; - /** message reserved */ - uint8_t reserved; - /** message length */ - uint16_t length; - }; +struct tpkthdr { + /** message version */ + uint8_t version; + /** message reserved */ + uint8_t reserved; + /** message length */ + uint16_t length; +}; #pragma pack(pop) - /** - * @class TpktLayer - * Represents a TPKT (Transport Service on top of the TCP) protocol layer - */ - class TpktLayer : public Layer - { - public: - /** - * A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data (will be casted to @ref tpkthdr) - * @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 - */ - TpktLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) - : Layer(data, dataLen, prevLayer, packet) - { - m_Protocol = TPKT; - } - - /** - * A constructor that allocates a new TPKT header - * @param[in] version Protocol version number - * @param[in] length Packet length - */ - TpktLayer(uint8_t version, uint16_t length); - - virtual ~TpktLayer() {} - - /** - * @return TPKT reserved - */ - uint8_t getReserved() const; - - /** - * @return TPKT version - */ - uint8_t getVersion() const; - - /** - * @return TPKT length - */ - uint16_t getLength() const; - - /** - * Set the value of the version - * @param[in] version The value of the version - */ - void setVersion(uint8_t version) const; - - /** - * Set the value of the length - * @param[in] length The value of the length - */ - void setLength(uint16_t length) const; - - /** - * @return Size of @ref tpkthdr - */ - size_t getHeaderLen() const override { return sizeof(tpkthdr); } - - /** - * Does nothing for this layer - */ - void computeCalculateFields() override {} - - /** - * Currently parses the rest of the packet as a COTP protocol or generic payload (PayloadLayer) - */ - void parseNextLayer() override; - - /** - * A static method that checks whether a source or dest port match those associated with the TPKT protocol - * @param[in] portSrc Source port number to check - * @param[in] portDst Dest port number to check - * @return True if the source or dest port match those associated with the TPKT protocol - */ - static bool isTpktPort(uint16_t portSrc, uint16_t portDst) { return portSrc == 102 || portDst == 102; } - - /** - * A static method that takes a byte array and detects whether it is a TPKT message - * @param[in] data A byte array - * @param[in] dataSize The byte array size (in bytes) - * @return True if the data size is greater or equal than the size of tpkthdr - */ - static bool isDataValid(const uint8_t *data, size_t dataSize) { return data && dataSize >= sizeof(tpkthdr); } - - std::string toString() const override; - - OsiModelLayer getOsiModelLayer() const override { return OsiModelTransportLayer; } - - private: - /** - * Get a pointer to the TPKT header. Data can be retrieved through the - * other methods of this layer. Notice the return value points directly to the data, so every change will change - * the actual packet data - * @return A pointer to the @ref tpkthdr - */ - tpkthdr *getTpktHeader() const { return (tpkthdr *)m_Data; } - }; +/** + * @class TpktLayer + * Represents a TPKT (Transport Service on top of the TCP) protocol layer + */ +class TpktLayer : public Layer { + public: + /** + * A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data (will be casted to @ref tpkthdr) + * @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 + */ + TpktLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : Layer(data, dataLen, prevLayer, packet) { + m_Protocol = TPKT; + } + + /** + * A constructor that allocates a new TPKT header + * @param[in] version Protocol version number + * @param[in] length Packet length + */ + TpktLayer(uint8_t version, uint16_t length); + + virtual ~TpktLayer() {} + + /** + * @return TPKT reserved + */ + uint8_t getReserved() const; + + /** + * @return TPKT version + */ + uint8_t getVersion() const; + + /** + * @return TPKT length + */ + uint16_t getLength() const; + + /** + * Set the value of the version + * @param[in] version The value of the version + */ + void setVersion(uint8_t version) const; + + /** + * Set the value of the length + * @param[in] length The value of the length + */ + void setLength(uint16_t length) const; + + /** + * @return Size of @ref tpkthdr + */ + size_t getHeaderLen() const override { return sizeof(tpkthdr); } + + /** + * Does nothing for this layer + */ + void computeCalculateFields() override {} + + /** + * Currently parses the rest of the packet as a COTP protocol or generic + * payload (PayloadLayer) + */ + void parseNextLayer() override; + + /** + * A static method that checks whether a source or dest port match those + * associated with the TPKT protocol + * @param[in] portSrc Source port number to check + * @param[in] portDst Dest port number to check + * @return True if the source or dest port match those associated with the + * TPKT protocol + */ + static bool isTpktPort(uint16_t portSrc, uint16_t portDst) { + return portSrc == 102 || portDst == 102; + } + + /** + * A static method that takes a byte array and detects whether it is a TPKT + * message + * @param[in] data A byte array + * @param[in] dataSize The byte array size (in bytes) + * @return True if the data size is greater or equal than the size of tpkthdr + */ + static bool isDataValid(const uint8_t* data, size_t dataSize) { + return data && dataSize >= sizeof(tpkthdr); + } + + std::string toString() const override; + + OsiModelLayer getOsiModelLayer() const override { + return OsiModelTransportLayer; + } + + private: + /** + * Get a pointer to the TPKT header. Data can be retrieved through the + * other methods of this layer. Notice the return value points directly to the + * data, so every change will change the actual packet data + * @return A pointer to the @ref tpkthdr + */ + tpkthdr* getTpktHeader() const { return (tpkthdr*)m_Data; } +}; } // namespace pcpp #endif // PACKETPP_TPKT_LAYER diff --git a/Packet++/header/UdpLayer.h b/Packet++/header/UdpLayer.h index 09a42bcd56..f2b8a39826 100644 --- a/Packet++/header/UdpLayer.h +++ b/Packet++/header/UdpLayer.h @@ -9,96 +9,101 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - - /** - * @struct udphdr - * Represents an UDP protocol header - */ -#pragma pack(push,1) - struct udphdr - { - /** Source port */ - uint16_t portSrc; - /** Destination port */ - uint16_t portDst; - /** Length of header and payload in bytes */ - uint16_t length; - /** Error-checking of the header and data */ - uint16_t headerChecksum; - }; -#pragma pack(pop) +namespace pcpp { +/** + * @struct udphdr + * Represents an UDP protocol header + */ +#pragma pack(push, 1) +struct udphdr { + /** Source port */ + uint16_t portSrc; + /** Destination port */ + uint16_t portDst; + /** Length of header and payload in bytes */ + uint16_t length; + /** Error-checking of the header and data */ + uint16_t headerChecksum; +}; +#pragma pack(pop) - /** - * @class UdpLayer - * Represents an UDP (User Datagram Protocol) protocol layer - */ - class UdpLayer : public Layer - { - public: - /** - * A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data (will be casted to @ref udphdr) - * @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 - */ - UdpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) { m_Protocol = UDP; } - - /** - * A constructor that allocates a new UDP header with source and destination ports - * @param[in] portSrc Source UDP port address - * @param[in] portDst Destination UDP port - */ - UdpLayer(uint16_t portSrc, uint16_t portDst); - - /** - * Get a pointer to the UDP header. Notice this points directly to the data, so every change will change the actual packet data - * @return A pointer to the @ref udphdr - */ - udphdr* getUdpHeader() const { return (udphdr*)m_Data; } - - /** - * @return UDP source port - */ - uint16_t getSrcPort() const; - - /** - * @return UDP destination port - */ - uint16_t getDstPort() const; - - /** - * Calculate the checksum from header and data and possibly write the result to @ref udphdr#headerChecksum - * @param[in] writeResultToPacket If set to true then checksum result will be written to @ref udphdr#headerChecksum - * @return The checksum result - */ - uint16_t calculateChecksum(bool writeResultToPacket); - - // implement abstract methods - - /** - * Currently identifies the following next layers: DnsLayer, DhcpLayer, VxlanLayer, SipRequestLayer, SipResponseLayer, - * RadiusLayer. Otherwise sets PayloadLayer - */ - void parseNextLayer(); - - /** - * @return Size of @ref udphdr - */ - size_t getHeaderLen() const { return sizeof(udphdr); } - - /** - * Calculate @ref udphdr#headerChecksum field - */ - void computeCalculateFields(); - - std::string toString() const; - - OsiModelLayer getOsiModelLayer() const { return OsiModelTransportLayer; } - }; +/** + * @class UdpLayer + * Represents an UDP (User Datagram Protocol) protocol layer + */ +class UdpLayer : public Layer { + public: + /** + * A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data (will be casted to @ref udphdr) + * @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 + */ + UdpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : Layer(data, dataLen, prevLayer, packet) { + m_Protocol = UDP; + } + + /** + * A constructor that allocates a new UDP header with source and destination + * ports + * @param[in] portSrc Source UDP port address + * @param[in] portDst Destination UDP port + */ + UdpLayer(uint16_t portSrc, uint16_t portDst); + + /** + * Get a pointer to the UDP header. Notice this points directly to the data, + * so every change will change the actual packet data + * @return A pointer to the @ref udphdr + */ + udphdr* getUdpHeader() const { return (udphdr*)m_Data; } + + /** + * @return UDP source port + */ + uint16_t getSrcPort() const; + + /** + * @return UDP destination port + */ + uint16_t getDstPort() const; + + /** + * Calculate the checksum from header and data and possibly write the result + * to @ref udphdr#headerChecksum + * @param[in] writeResultToPacket If set to true then checksum result will be + * written to @ref udphdr#headerChecksum + * @return The checksum result + */ + uint16_t calculateChecksum(bool writeResultToPacket); + + // implement abstract methods + + /** + * Currently identifies the following next layers: DnsLayer, DhcpLayer, + * VxlanLayer, SipRequestLayer, SipResponseLayer, RadiusLayer. Otherwise sets + * PayloadLayer + */ + void parseNextLayer(); + + /** + * @return Size of @ref udphdr + */ + size_t getHeaderLen() const { return sizeof(udphdr); } + + /** + * Calculate @ref udphdr#headerChecksum field + */ + void computeCalculateFields(); + + std::string toString() const; + + OsiModelLayer getOsiModelLayer() const { return OsiModelTransportLayer; } +}; } // namespace pcpp diff --git a/Packet++/header/VlanLayer.h b/Packet++/header/VlanLayer.h index 231a15f762..91223ab20d 100644 --- a/Packet++/header/VlanLayer.h +++ b/Packet++/header/VlanLayer.h @@ -1,8 +1,8 @@ #ifndef PACKETPP_VLAN_LAYER #define PACKETPP_VLAN_LAYER -#include "Layer.h" #include "EthLayer.h" +#include "Layer.h" /// @file @@ -10,127 +10,133 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ +namespace pcpp { - /** - * @struct vlan_header - * Represents a VLAN header - */ +/** + * @struct vlan_header + * Represents a VLAN header + */ #pragma pack(push, 1) - struct vlan_header - { - /** - @verbatim - 0 1 2 - 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |Prio |C| VLAN ID | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - @endverbatim - */ - uint16_t vlan; - /** Ethernet type for next layer */ - uint16_t etherType; - }; +struct vlan_header { + /** + @verbatim + 0 1 2 + 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |Prio |C| VLAN ID | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + @endverbatim + */ + uint16_t vlan; + /** Ethernet type for next layer */ + uint16_t etherType; +}; #pragma pack(pop) - - /** - * @class VlanLayer - * Represents a VLAN tunnel layer - */ - class VlanLayer : public Layer - { - public: - /** 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 - */ - VlanLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) { m_Protocol = VLAN; } - - /** - * A constructor that allocates a new VLAN header - * @param[in] vlanID VLAN ID - * @param[in] cfi CFI value - * @param[in] priority Priority value - * @param[in] etherType Protocol EtherType of the next layer. It's an optional parameter, a value of 0 will be set if not provided - */ - VlanLayer(const uint16_t vlanID, bool cfi, uint8_t priority, uint16_t etherType = 0); - - ~VlanLayer() {} - - /** - * Get a pointer to the VLAN header. Notice this points directly to the data, so every change will change the actual packet data - * @return A pointer to the vlan_header - */ - vlan_header* getVlanHeader() const { return (vlan_header*)m_Data; } - - /** - * Get the VLAN ID value. This method differs from vlan_header#vlanID because vlan_header#vlanID is 12 bits long in a 16 bit field. - * This methods extracts only the 12 bit relevant for the VLAN ID - * @return VLAN ID value - * @todo Verify it works in big endian machines as well - */ - uint16_t getVlanID() const; - - /** - * @return The CFI bit value - * @todo Verify it works in big endian machines as well - */ - uint8_t getCFI() const; - - /** - * @return The priority value - * @todo Verify it works in big endian machines as well - */ - uint8_t getPriority() const; - - /** - * Set VLAN ID. This method differs from setting vlan_header#vlanID because vlan_header#vlanID is 12 bits long in a 16 bit field. - * This methods sets only the 12 bit relevant for the VLAN ID - * @param[in] id The VLAN ID to set - * @todo Verify it works in big endian machines as well - */ - void setVlanID(uint16_t id); - - /** - * Set CFI bit - * @param[in] cfi The CFI bit to set - * @todo Verify it works in big endian machines as well - */ - void setCFI(bool cfi); - - /** - * Set priority value - * @param[in] priority The priority value to set - * @todo Verify it works in big endian machines as well - */ - void setPriority(uint8_t priority); - - // implement abstract methods - - /** - * Currently identifies the following next layers: IPv4Layer, IPv6Layer, ArpLayer, VlanLayer, MplsLayer. Otherwise sets PayloadLayer - */ - void parseNextLayer(); - - /** - * @return Size of vlan_header - */ - size_t getHeaderLen() const { return sizeof(vlan_header); } - - /** - * Calculate the EtherType for known protocols: IPv4, IPv6, ARP, VLAN - */ - void computeCalculateFields(); - - std::string toString() const; - - OsiModelLayer getOsiModelLayer() const { return OsiModelDataLinkLayer; } - }; +/** + * @class VlanLayer + * Represents a VLAN tunnel layer + */ +class VlanLayer : public Layer { + public: + /** 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 + */ + VlanLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : Layer(data, dataLen, prevLayer, packet) { + m_Protocol = VLAN; + } + + /** + * A constructor that allocates a new VLAN header + * @param[in] vlanID VLAN ID + * @param[in] cfi CFI value + * @param[in] priority Priority value + * @param[in] etherType Protocol EtherType of the next layer. It's an optional + * parameter, a value of 0 will be set if not provided + */ + VlanLayer(const uint16_t vlanID, bool cfi, uint8_t priority, + uint16_t etherType = 0); + + ~VlanLayer() {} + + /** + * Get a pointer to the VLAN header. Notice this points directly to the data, + * so every change will change the actual packet data + * @return A pointer to the vlan_header + */ + vlan_header* getVlanHeader() const { return (vlan_header*)m_Data; } + + /** + * Get the VLAN ID value. This method differs from vlan_header#vlanID because + * vlan_header#vlanID is 12 bits long in a 16 bit field. This methods extracts + * only the 12 bit relevant for the VLAN ID + * @return VLAN ID value + * @todo Verify it works in big endian machines as well + */ + uint16_t getVlanID() const; + + /** + * @return The CFI bit value + * @todo Verify it works in big endian machines as well + */ + uint8_t getCFI() const; + + /** + * @return The priority value + * @todo Verify it works in big endian machines as well + */ + uint8_t getPriority() const; + + /** + * Set VLAN ID. This method differs from setting vlan_header#vlanID because + * vlan_header#vlanID is 12 bits long in a 16 bit field. This methods sets + * only the 12 bit relevant for the VLAN ID + * @param[in] id The VLAN ID to set + * @todo Verify it works in big endian machines as well + */ + void setVlanID(uint16_t id); + + /** + * Set CFI bit + * @param[in] cfi The CFI bit to set + * @todo Verify it works in big endian machines as well + */ + void setCFI(bool cfi); + + /** + * Set priority value + * @param[in] priority The priority value to set + * @todo Verify it works in big endian machines as well + */ + void setPriority(uint8_t priority); + + // implement abstract methods + + /** + * Currently identifies the following next layers: IPv4Layer, IPv6Layer, + * ArpLayer, VlanLayer, MplsLayer. Otherwise sets PayloadLayer + */ + void parseNextLayer(); + + /** + * @return Size of vlan_header + */ + size_t getHeaderLen() const { return sizeof(vlan_header); } + + /** + * Calculate the EtherType for known protocols: IPv4, IPv6, ARP, VLAN + */ + void computeCalculateFields(); + + std::string toString() const; + + OsiModelLayer getOsiModelLayer() const { return OsiModelDataLinkLayer; } +}; } // namespace pcpp diff --git a/Packet++/header/VrrpLayer.h b/Packet++/header/VrrpLayer.h old mode 100755 new mode 100644 index 17f109ab09..be135b550d --- a/Packet++/header/VrrpLayer.h +++ b/Packet++/header/VrrpLayer.h @@ -1,8 +1,8 @@ #ifndef PACKETPP_VRRP_LAYER #define PACKETPP_VRRP_LAYER -#include "Layer.h" #include "IpAddress.h" +#include "Layer.h" #include /// @file @@ -11,451 +11,469 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - /** - For more info see: - https://datatracker.ietf.org/doc/html/rfc2338 - https://datatracker.ietf.org/doc/html/rfc3768 - https://datatracker.ietf.org/doc/html/rfc5798 - */ - - /* VRRPv2 Packet Format - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |Version| Type | Virtual Rtr ID| Priority | Count IP Addrs| - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Auth Type | Adver Int | Checksum | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | IP Address (1) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | . | - | . | - | . | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | IP Address (n) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Authentication Data (1) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Authentication Data (2) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - - /* VRRPv3 Packet Format - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | IPv4 Fields or IPv6 Fields | - ... ... - | | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |Version| Type | Virtual Rtr ID| Priority |Count IPvX Addr| - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |(rsvd) | Max Adver Int | Checksum | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | | - + + - | IPvX Address(es) | - + + - + + - + + - + + - | | - + + - | | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - - /** - * @struct vrrp_header - * VRRP generic header - */ - struct vrrp_header - { +namespace pcpp { +/** +For more info see: + https://datatracker.ietf.org/doc/html/rfc2338 + https://datatracker.ietf.org/doc/html/rfc3768 + https://datatracker.ietf.org/doc/html/rfc5798 +*/ + +/* VRRPv2 Packet Format + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |Version| Type | Virtual Rtr ID| Priority | Count IP Addrs| + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Auth Type | Adver Int | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | IP Address (1) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | . | + | . | + | . | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | IP Address (n) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Authentication Data (1) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Authentication Data (2) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +/* VRRPv3 Packet Format + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | IPv4 Fields or IPv6 Fields | + ... ... + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |Version| Type | Virtual Rtr ID| Priority |Count IPvX Addr| + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |(rsvd) | Max Adver Int | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + + + + | IPvX Address(es) | + + + + + + + + + + + + + | | + + + + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +/** + * @struct vrrp_header + * VRRP generic header + */ +struct vrrp_header { #if (BYTE_ORDER == LITTLE_ENDIAN) - /** Type */ - uint8_t type: 4, + /** Type */ + uint8_t type : 4, - /** Version bits */ - version: 4; + /** Version bits */ + version : 4; #else - /** Version bits */ - uint8_t version:4, + /** Version bits */ + uint8_t version : 4, - /** Type */ - type: 4; + /** Type */ + type : 4; #endif - /** The Virtual Router Identifier (VRID) field identifies the virtual router this packet is reporting status for*/ - uint8_t vrId; - - /** This specifies the sending VRRP router's priority for the virtual router */ - uint8_t priority; - - /** Specifies how many IPvX addresses are present in this Packet */ - uint8_t ipAddrCount; - - /** This specifies authentication type(v2) or (Max) Advertisement interval (in seconds(v2) or centi-seconds(v3)). */ - uint16_t authTypeAdvInt; - - /** This specifies checksum field that is used to detect data corruption in the VRRP message. - * VRRPv2 uses normal checksum algorithm, while VRRPv3 uses "pseudo-header" checksum algorithm. */ - uint16_t checksum; - - /** This specifies one or more IPvX addresses that are associated with the virtual router. */ - uint8_t *ipAddresses[]; - }; - - /** - * @class VrrpLayer - * A base class for all VRRP (Virtual Router Redundancy Protocol) protocol classes. This is an abstract class and cannot be instantiated, - * only its child classes can be instantiated. The inherited classes represent the different versions of the protocol: - * VRRPv2 and VRRPv3 - */ - class VrrpLayer : public Layer - { - private: - bool addIPAddressesAt(const std::vector &ipAddresses, int offset); - - uint8_t getIPAddressLen() const; - - bool isIPAddressValid(IPAddress &ipAddress) const; - - uint8_t* getFirstIPAddressPtr() const; - - uint8_t* getNextIPAddressPtr(uint8_t* ipAddressPtr) const; - - IPAddress getIPAddressFromData(uint8_t *data) const; - - void copyIPAddressToData(uint8_t *data, const IPAddress &ipAddress) const; - - IPAddress::AddressType m_AddressType; - - protected: - VrrpLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet, ProtocolType vrrpVer, - IPAddress::AddressType addressType) - : Layer(data, dataLen, prevLayer, packet), m_AddressType(addressType) - { - m_Protocol = vrrpVer; - } - - explicit VrrpLayer(ProtocolType subProtocol, uint8_t virtualRouterId, uint8_t priority); - - vrrp_header *getVrrpHeader() const { return (vrrp_header *) m_Data; } - - void setAddressType(IPAddress::AddressType addressType); - - public: - /** - * VRRP message types - */ - enum VrrpType - { - /** Unknown VRRP message */ - VrrpType_Unknown = 0, - - /** VRRP advertisement message */ - VrrpType_Advertisement = 1 - }; - - /** - * An enum describing VRRP special priority values - */ - enum VrrpPriority - { - /** Default priority for a backup VRRP router (value of 100) */ - Default, - /** Current Master has stopped participating in VRRP (value of 0) */ - Stop, - /** This VRRP router owns the virtual router's IP address(es) (value of 255) */ - Owner, - /** Other priority */ - Other - }; - - virtual ~VrrpLayer() {} - - /** - * @return The VRRP IP Address type - */ - IPAddress::AddressType getAddressType() const; - - /** - * A static method that validates the input data - * @param[in] data VRRP raw data (byte stream) - * @param[in] dataLen The length of the byte stream - * @return One of the values ::VRRPv2, ::VRRPv3 according to detected VRRP version or ::UnknownProtocol if couldn't detect - * VRRP version - */ - static ProtocolType getVersionFromData(uint8_t *data, size_t dataLen); - - /** - * @return VRRP version of this message - */ - uint8_t getVersion() const; - - /** - * @return VRRP type set in vrrp_header#type as VrrpLayer::VrrpType enum. - */ - VrrpType getType() const; - - /** - * @return The virtual router id (vrId) in this message - */ - uint8_t getVirtualRouterID() const; - - /** - * Set the virtual router ID - * @param virtualRouterID new ID to set - */ - void setVirtualRouterID(uint8_t virtualRouterID); - - /** - * @return The priority in this message - */ - uint8_t getPriority() const; - - /** - * @return An enum describing VRRP priority - */ - VrrpPriority getPriorityAsEnum() const; - - /** - * Set the priority - * @param priority new priority to set - */ - void setPriority(uint8_t priority); - - /** - * @return VRRP checksum of this message - */ - uint16_t getChecksum() const; - - /** - * Fill the checksum from header and data and write the result to @ref vrrp_header#checksum - */ - void calculateAndSetChecksum(); - - /** - * Calculate the checksum from header and data and write the result to @ref vrrp_header#checksum - * @return The checksum result - */ - virtual uint16_t calculateChecksum() const = 0; - - /** - * @return True if VRRP checksum is correct - */ - bool isChecksumCorrect() const; - - /** - * @return The count of VRRP virtual IP addresses in this message - */ - uint8_t getIPAddressesCount() const; - - /** - * @return A list of the virtual IP addresses in this message - */ - std::vector getIPAddresses() const; - - /** - * Add a list of virtual IP addresses at a the end of the virtual IP address list. The vrrp_header#ipAddressCount field will be - * incremented accordingly - * @param[in] ipAddresses A vector containing all the virtual IP address - * @return true if added successfully, false otherwise - */ - bool addIPAddresses(const std::vector &ipAddresses); - - /** - * Add a virtual IP address at a the end of the virtual IP address list. The vrrp_header#ipAddressCount field will be - * incremented accordingly - * @param[in] ipAddress Virtual IP address to add - * @return true if add successfully, false otherwise - */ - bool addIPAddress(const IPAddress &ipAddress); - - /** - * Remove a virtual IP address at a certain index. The vrrp_header#ipAddressCount field will be decremented accordingly - * @param[in] index The index of the virtual IP address to be removed - * @return True if virtual IP address was removed successfully or false otherwise. If false is returned an appropriate error message - * will be printed to log - */ - bool removeIPAddressAtIndex(int index); - - /** - * Remove all virtual IP addresses in the message. The vrrp_header#ipAddressCount field will be set to 0 - * @return True if virtual IP addresses were cleared successfully or false otherwise. If false is returned an appropriate error message - * will be printed to log - */ - bool removeAllIPAddresses(); - - // implement abstract methods - - /** - * Does nothing for this layer (VRRP layer is always last) - */ - void parseNextLayer() override {} - - /** - * Calculate the VRRP checksum - */ - void computeCalculateFields() override; - - /** - * @return The message size in bytes which include the size of the basic header + the size of the IP address(es) - */ - size_t getHeaderLen() const override { return m_DataLen; } - - std::string toString() const override; - - OsiModelLayer getOsiModelLayer() const override { return OsiModelNetworkLayer; } - }; - - /** - * @class VrrpV2Layer - * Represents VRRPv2 (Virtual Router Redundancy Protocol ver 2) layer. This class represents all the different messages of VRRPv2 - */ - class VrrpV2Layer : public VrrpLayer - { - private: - struct vrrpv2_auth_adv - { - uint8_t authType; - uint8_t advInt; - }; - - public: - /** - * VRRP v2 authentication types - */ - enum class VrrpAuthType : uint8_t - { - /** No Authentication */ - NoAuthentication = 0, - /** Simple Text Password */ - SimpleTextPassword = 1, - /** IP Authentication Header */ - IPAuthenticationHeader = 2, - /** Cisco VRRP MD5 Authentication */ - MD5 = 3, - /** Other/Unknown Authentication Type */ - Other = 4 - }; - - /** 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 - */ - VrrpV2Layer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) - : VrrpLayer(data, dataLen, prevLayer, packet, VRRPv2, IPAddress::IPv4AddressType) {} - - /** - * A constructor that allocates a new VRRP v2 layer - * @param virtualRouterId Virtual router ID - * @param priority Priority - * @param advInt Advertisement interval - * @param authType Authentication type (default value is 0) - */ - explicit VrrpV2Layer(uint8_t virtualRouterId, uint8_t priority, uint8_t advInt, uint8_t authType = 0); - - /** - * A destructor for this layer (does nothing) - */ - ~VrrpV2Layer() {} - - /** - * @return The VRRP advertisement interval in this message - */ - uint8_t getAdvInt() const; - - /** - * Set advertisement interval value in this message - * @param advInt value to set - */ - void setAdvInt(uint8_t advInt); - - /** - * @return The authentication type in this message - */ - uint8_t getAuthType() const; - - /** - * @return The VRRP authentication type as enum - */ - VrrpAuthType getAuthTypeAsEnum() const; - - /** - * Set VRRP authentication type - * @param authType value to set - */ - void setAuthType(uint8_t authType); - - // implement abstract methods - - /** - * Calculate the checksum from header and data and write the result to @ref vrrp_header#checksum - * @return The checksum result - */ - uint16_t calculateChecksum() const override; - }; - - /** - * @class VrrpV3Layer - * Represents VRRPv3 (Virtual Router Redundancy Protocol ver 3) layer. This class represents all the different messages of VRRP - */ - class VrrpV3Layer : public VrrpLayer - { - private: - struct vrrpv3_rsvd_adv - { - uint16_t maxAdvInt; - }; - - public: - /** 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 - * @param[in] addressType The IP address type to set for this layer - */ - VrrpV3Layer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet, IPAddress::AddressType addressType) - : VrrpLayer(data, dataLen, prevLayer, packet, VRRPv3, addressType) {} - - /** - * A constructor that allocates a new VRRPv3 - * @param addressType The IP address type to set for this layer - * @param virtualRouterId Virtual router ID - * @param priority Priority - * @param maxAdvInt Max advertisement interval - */ - explicit VrrpV3Layer(IPAddress::AddressType addressType, uint8_t virtualRouterId, uint8_t priority, uint16_t maxAdvInt); - - /** - * A destructor for this layer (does nothing) - */ - ~VrrpV3Layer() {} - - /** - * @return The maximum advertisement interval in this message - */ - uint16_t getMaxAdvInt() const; - - /** - * Set the maximum advertisement interval value - * @param maxAdvInt Value to set - */ - void setMaxAdvInt(uint16_t maxAdvInt); - - // implement abstract methods - - /** - * Calculate the checksum from header and data and write the result to @ref vrrp_header#checksum - * @return The checksum result - */ - uint16_t calculateChecksum() const override; - }; -} + /** The Virtual Router Identifier (VRID) field identifies the virtual router + * this packet is reporting status for*/ + uint8_t vrId; + + /** This specifies the sending VRRP router's priority for the virtual router + */ + uint8_t priority; + + /** Specifies how many IPvX addresses are present in this Packet */ + uint8_t ipAddrCount; + + /** This specifies authentication type(v2) or (Max) Advertisement interval (in + * seconds(v2) or centi-seconds(v3)). */ + uint16_t authTypeAdvInt; + + /** This specifies checksum field that is used to detect data corruption in + * the VRRP message. + * VRRPv2 uses normal checksum algorithm, while VRRPv3 uses "pseudo-header" + * checksum algorithm. */ + uint16_t checksum; + + /** This specifies one or more IPvX addresses that are associated with the + * virtual router. */ + uint8_t* ipAddresses[]; +}; + +/** + * @class VrrpLayer + * A base class for all VRRP (Virtual Router Redundancy Protocol) protocol + * classes. This is an abstract class and cannot be instantiated, only its child + * classes can be instantiated. The inherited classes represent the different + * versions of the protocol: VRRPv2 and VRRPv3 + */ +class VrrpLayer : public Layer { + private: + bool addIPAddressesAt(const std::vector& ipAddresses, int offset); + + uint8_t getIPAddressLen() const; + + bool isIPAddressValid(IPAddress& ipAddress) const; + + uint8_t* getFirstIPAddressPtr() const; + + uint8_t* getNextIPAddressPtr(uint8_t* ipAddressPtr) const; + + IPAddress getIPAddressFromData(uint8_t* data) const; + + void copyIPAddressToData(uint8_t* data, const IPAddress& ipAddress) const; + + IPAddress::AddressType m_AddressType; + + protected: + VrrpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet, + ProtocolType vrrpVer, IPAddress::AddressType addressType) + : Layer(data, dataLen, prevLayer, packet), m_AddressType(addressType) { + m_Protocol = vrrpVer; + } + + explicit VrrpLayer(ProtocolType subProtocol, uint8_t virtualRouterId, + uint8_t priority); + + vrrp_header* getVrrpHeader() const { return (vrrp_header*)m_Data; } + + void setAddressType(IPAddress::AddressType addressType); + + public: + /** + * VRRP message types + */ + enum VrrpType { + /** Unknown VRRP message */ + VrrpType_Unknown = 0, + + /** VRRP advertisement message */ + VrrpType_Advertisement = 1 + }; + + /** + * An enum describing VRRP special priority values + */ + enum VrrpPriority { + /** Default priority for a backup VRRP router (value of 100) */ + Default, + /** Current Master has stopped participating in VRRP (value of 0) */ + Stop, + /** This VRRP router owns the virtual router's IP address(es) (value of 255) + */ + Owner, + /** Other priority */ + Other + }; + + virtual ~VrrpLayer() {} + + /** + * @return The VRRP IP Address type + */ + IPAddress::AddressType getAddressType() const; + + /** + * A static method that validates the input data + * @param[in] data VRRP raw data (byte stream) + * @param[in] dataLen The length of the byte stream + * @return One of the values ::VRRPv2, ::VRRPv3 according to detected VRRP + * version or ::UnknownProtocol if couldn't detect VRRP version + */ + static ProtocolType getVersionFromData(uint8_t* data, size_t dataLen); + + /** + * @return VRRP version of this message + */ + uint8_t getVersion() const; + + /** + * @return VRRP type set in vrrp_header#type as VrrpLayer::VrrpType enum. + */ + VrrpType getType() const; + + /** + * @return The virtual router id (vrId) in this message + */ + uint8_t getVirtualRouterID() const; + + /** + * Set the virtual router ID + * @param virtualRouterID new ID to set + */ + void setVirtualRouterID(uint8_t virtualRouterID); + + /** + * @return The priority in this message + */ + uint8_t getPriority() const; + + /** + * @return An enum describing VRRP priority + */ + VrrpPriority getPriorityAsEnum() const; + + /** + * Set the priority + * @param priority new priority to set + */ + void setPriority(uint8_t priority); + + /** + * @return VRRP checksum of this message + */ + uint16_t getChecksum() const; + + /** + * Fill the checksum from header and data and write the result to @ref + * vrrp_header#checksum + */ + void calculateAndSetChecksum(); + + /** + * Calculate the checksum from header and data and write the result to @ref + * vrrp_header#checksum + * @return The checksum result + */ + virtual uint16_t calculateChecksum() const = 0; + + /** + * @return True if VRRP checksum is correct + */ + bool isChecksumCorrect() const; + + /** + * @return The count of VRRP virtual IP addresses in this message + */ + uint8_t getIPAddressesCount() const; + + /** + * @return A list of the virtual IP addresses in this message + */ + std::vector getIPAddresses() const; + + /** + * Add a list of virtual IP addresses at a the end of the virtual IP address + * list. The vrrp_header#ipAddressCount field will be incremented accordingly + * @param[in] ipAddresses A vector containing all the virtual IP address + * @return true if added successfully, false otherwise + */ + bool addIPAddresses(const std::vector& ipAddresses); + + /** + * Add a virtual IP address at a the end of the virtual IP address list. The + * vrrp_header#ipAddressCount field will be incremented accordingly + * @param[in] ipAddress Virtual IP address to add + * @return true if add successfully, false otherwise + */ + bool addIPAddress(const IPAddress& ipAddress); + + /** + * Remove a virtual IP address at a certain index. The + * vrrp_header#ipAddressCount field will be decremented accordingly + * @param[in] index The index of the virtual IP address to be removed + * @return True if virtual IP address was removed successfully or false + * otherwise. If false is returned an appropriate error message will be + * printed to log + */ + bool removeIPAddressAtIndex(int index); + + /** + * Remove all virtual IP addresses in the message. The + * vrrp_header#ipAddressCount field will be set to 0 + * @return True if virtual IP addresses were cleared successfully or false + * otherwise. If false is returned an appropriate error message will be + * printed to log + */ + bool removeAllIPAddresses(); + + // implement abstract methods + + /** + * Does nothing for this layer (VRRP layer is always last) + */ + void parseNextLayer() override {} + + /** + * Calculate the VRRP checksum + */ + void computeCalculateFields() override; + + /** + * @return The message size in bytes which include the size of the basic + * header + the size of the IP address(es) + */ + size_t getHeaderLen() const override { return m_DataLen; } + + std::string toString() const override; + + OsiModelLayer getOsiModelLayer() const override { + return OsiModelNetworkLayer; + } +}; + +/** + * @class VrrpV2Layer + * Represents VRRPv2 (Virtual Router Redundancy Protocol ver 2) layer. This + * class represents all the different messages of VRRPv2 + */ +class VrrpV2Layer : public VrrpLayer { + private: + struct vrrpv2_auth_adv { + uint8_t authType; + uint8_t advInt; + }; + + public: + /** + * VRRP v2 authentication types + */ + enum class VrrpAuthType : uint8_t { + /** No Authentication */ + NoAuthentication = 0, + /** Simple Text Password */ + SimpleTextPassword = 1, + /** IP Authentication Header */ + IPAuthenticationHeader = 2, + /** Cisco VRRP MD5 Authentication */ + MD5 = 3, + /** Other/Unknown Authentication Type */ + Other = 4 + }; + + /** 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 + */ + VrrpV2Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : VrrpLayer(data, dataLen, prevLayer, packet, VRRPv2, + IPAddress::IPv4AddressType) {} + + /** + * A constructor that allocates a new VRRP v2 layer + * @param virtualRouterId Virtual router ID + * @param priority Priority + * @param advInt Advertisement interval + * @param authType Authentication type (default value is 0) + */ + explicit VrrpV2Layer(uint8_t virtualRouterId, uint8_t priority, + uint8_t advInt, uint8_t authType = 0); + + /** + * A destructor for this layer (does nothing) + */ + ~VrrpV2Layer() {} + + /** + * @return The VRRP advertisement interval in this message + */ + uint8_t getAdvInt() const; + + /** + * Set advertisement interval value in this message + * @param advInt value to set + */ + void setAdvInt(uint8_t advInt); + + /** + * @return The authentication type in this message + */ + uint8_t getAuthType() const; + + /** + * @return The VRRP authentication type as enum + */ + VrrpAuthType getAuthTypeAsEnum() const; + + /** + * Set VRRP authentication type + * @param authType value to set + */ + void setAuthType(uint8_t authType); + + // implement abstract methods + + /** + * Calculate the checksum from header and data and write the result to @ref + * vrrp_header#checksum + * @return The checksum result + */ + uint16_t calculateChecksum() const override; +}; + +/** + * @class VrrpV3Layer + * Represents VRRPv3 (Virtual Router Redundancy Protocol ver 3) layer. This + * class represents all the different messages of VRRP + */ +class VrrpV3Layer : public VrrpLayer { + private: + struct vrrpv3_rsvd_adv { + uint16_t maxAdvInt; + }; + + public: + /** 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 + * @param[in] addressType The IP address type to set for this layer + */ + VrrpV3Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet, + IPAddress::AddressType addressType) + : VrrpLayer(data, dataLen, prevLayer, packet, VRRPv3, addressType) {} + + /** + * A constructor that allocates a new VRRPv3 + * @param addressType The IP address type to set for this layer + * @param virtualRouterId Virtual router ID + * @param priority Priority + * @param maxAdvInt Max advertisement interval + */ + explicit VrrpV3Layer(IPAddress::AddressType addressType, + uint8_t virtualRouterId, uint8_t priority, + uint16_t maxAdvInt); + + /** + * A destructor for this layer (does nothing) + */ + ~VrrpV3Layer() {} + + /** + * @return The maximum advertisement interval in this message + */ + uint16_t getMaxAdvInt() const; + + /** + * Set the maximum advertisement interval value + * @param maxAdvInt Value to set + */ + void setMaxAdvInt(uint16_t maxAdvInt); + + // implement abstract methods + + /** + * Calculate the checksum from header and data and write the result to @ref + * vrrp_header#checksum + * @return The checksum result + */ + uint16_t calculateChecksum() const override; +}; +} // namespace pcpp #endif // PACKETPP_VRRP_LAYER diff --git a/Packet++/header/VxlanLayer.h b/Packet++/header/VxlanLayer.h index b2b1436aab..9dfbf37cac 100644 --- a/Packet++/header/VxlanLayer.h +++ b/Packet++/header/VxlanLayer.h @@ -5,141 +5,148 @@ /// @file -namespace pcpp -{ +namespace pcpp { - /** - * @struct vxlan_header - * Represents a VXLAN protocol header - */ +/** + * @struct vxlan_header + * Represents a VXLAN protocol header + */ #pragma pack(push, 1) - struct vxlan_header - { - #if(BYTE_ORDER == LITTLE_ENDIAN) - /** Reserved bits */ - uint16_t reserved6_8:3; - /** VNI present flag */ - uint16_t vniPresentFlag:1; - /** Reserved bits */ - uint16_t reserved2_4:3; - /** GBP flag */ - uint16_t gbpFlag:1; - /** Reserved bits */ - uint16_t reserved14_16:3; - /** Policy applied flag */ - uint16_t policyAppliedFlag:1; - /** Reserved bits */ - uint16_t reserved11_12:2; - /** Don't learn flag */ - uint16_t dontLearnFlag:1; - /** Reserved bits */ - uint16_t reserved9:1; - #else - /** Reserved bits */ - uint16_t reserved9:1; - /** Don't learn flag */ - uint16_t dontLearnFlag:1; - /** Reserved bits */ - uint16_t reserved11_12:2; - /** Policy applied flag */ - uint16_t policyAppliedFlag:1; - /** Reserved bits */ - uint16_t reserved14_16:3; - /** GBP flag */ - uint16_t gbpFlag:1; - /** Reserved bits */ - uint16_t reserved2_4:3; - /** VNI present flag */ - uint16_t vniPresentFlag:1; - /** Reserved bits */ - uint16_t reserved6_8:3; - #endif - - /** Group Policy ID */ - uint16_t groupPolicyID; - - /** VXLAN Network ID (VNI) */ - uint32_t vni:24; - /** Reserved bits */ - uint32_t pad:8; - }; +struct vxlan_header { +#if (BYTE_ORDER == LITTLE_ENDIAN) + /** Reserved bits */ + uint16_t reserved6_8 : 3; + /** VNI present flag */ + uint16_t vniPresentFlag : 1; + /** Reserved bits */ + uint16_t reserved2_4 : 3; + /** GBP flag */ + uint16_t gbpFlag : 1; + /** Reserved bits */ + uint16_t reserved14_16 : 3; + /** Policy applied flag */ + uint16_t policyAppliedFlag : 1; + /** Reserved bits */ + uint16_t reserved11_12 : 2; + /** Don't learn flag */ + uint16_t dontLearnFlag : 1; + /** Reserved bits */ + uint16_t reserved9 : 1; +#else + /** Reserved bits */ + uint16_t reserved9 : 1; + /** Don't learn flag */ + uint16_t dontLearnFlag : 1; + /** Reserved bits */ + uint16_t reserved11_12 : 2; + /** Policy applied flag */ + uint16_t policyAppliedFlag : 1; + /** Reserved bits */ + uint16_t reserved14_16 : 3; + /** GBP flag */ + uint16_t gbpFlag : 1; + /** Reserved bits */ + uint16_t reserved2_4 : 3; + /** VNI present flag */ + uint16_t vniPresentFlag : 1; + /** Reserved bits */ + uint16_t reserved6_8 : 3; +#endif + + /** Group Policy ID */ + uint16_t groupPolicyID; + + /** VXLAN Network ID (VNI) */ + uint32_t vni : 24; + /** Reserved bits */ + uint32_t pad : 8; +}; #pragma pack(pop) - - /** - * @class VxlanLayer - * Represents a VXLAN (Virtual eXtensible Local Area Network) protocol layer - */ - class VxlanLayer : public Layer - { - public: - /** 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 - */ - VxlanLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) { m_Protocol = VXLAN; } - - /** - * A constructor that creates a new VXLAN header and allocates the data. Note: the VNI present flag is set automatically - * @param[in] vni VNI (VXLAN Network ID) to set. Optional parameter (default is 0) - * @param[in] groupPolicyID Group Policy ID to set. Optional parameter (default is 0) - * @param[in] setGbpFlag Set GBP flag. Optional parameter (default is false) - * @param[in] setPolicyAppliedFlag Set Policy Applied flag. Optional parameter (default is false) - * @param[in] setDontLearnFlag Set Don't Learn flag. Optional parameter (default is false) - */ - explicit VxlanLayer(uint32_t vni = 0, uint16_t groupPolicyID = 0, bool setGbpFlag = false, bool setPolicyAppliedFlag = false, bool setDontLearnFlag = false); - - ~VxlanLayer() {} - - /** - * Get a pointer to the VXLAN header. Notice this points directly to the data, so every change will change the actual packet data - * @return A pointer to the vxlan_header - */ - vxlan_header* getVxlanHeader() const { return (vxlan_header*)m_Data; } - - /** - * @return The VXLAN Network ID (VNI) value - */ - uint32_t getVNI() const; - - /** - * Set VXLAN Network ID (VNI) value - * @param[in] vni VNI value to set - */ - void setVNI(uint32_t vni); - - /** - * A static method that checks whether the port is considered as VxLAN - * @param[in] port The port number to be checked - */ - static bool isVxlanPort(uint16_t port) { return port == 4789; } - - - // implement abstract methods - - /** - * Next layer for VXLAN is always Ethernet - */ - void parseNextLayer(); - - /** - * @return Size of vxlan_header - */ - size_t getHeaderLen() const { return sizeof(vxlan_header); } - - /** - * Does nothing for this layer - */ - void computeCalculateFields() {} - - std::string toString() const; - - OsiModelLayer getOsiModelLayer() const { return OsiModelDataLinkLayer; } - - }; - -} +/** + * @class VxlanLayer + * Represents a VXLAN (Virtual eXtensible Local Area Network) protocol layer + */ +class VxlanLayer : public Layer { + public: + /** 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 + */ + VxlanLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : Layer(data, dataLen, prevLayer, packet) { + m_Protocol = VXLAN; + } + + /** + * A constructor that creates a new VXLAN header and allocates the data. Note: + * the VNI present flag is set automatically + * @param[in] vni VNI (VXLAN Network ID) to set. Optional parameter (default + * is 0) + * @param[in] groupPolicyID Group Policy ID to set. Optional parameter + * (default is 0) + * @param[in] setGbpFlag Set GBP flag. Optional parameter (default is false) + * @param[in] setPolicyAppliedFlag Set Policy Applied flag. Optional parameter + * (default is false) + * @param[in] setDontLearnFlag Set Don't Learn flag. Optional parameter + * (default is false) + */ + explicit VxlanLayer(uint32_t vni = 0, uint16_t groupPolicyID = 0, + bool setGbpFlag = false, + bool setPolicyAppliedFlag = false, + bool setDontLearnFlag = false); + + ~VxlanLayer() {} + + /** + * Get a pointer to the VXLAN header. Notice this points directly to the data, + * so every change will change the actual packet data + * @return A pointer to the vxlan_header + */ + vxlan_header* getVxlanHeader() const { return (vxlan_header*)m_Data; } + + /** + * @return The VXLAN Network ID (VNI) value + */ + uint32_t getVNI() const; + + /** + * Set VXLAN Network ID (VNI) value + * @param[in] vni VNI value to set + */ + void setVNI(uint32_t vni); + + /** + * A static method that checks whether the port is considered as VxLAN + * @param[in] port The port number to be checked + */ + static bool isVxlanPort(uint16_t port) { return port == 4789; } + + // implement abstract methods + + /** + * Next layer for VXLAN is always Ethernet + */ + void parseNextLayer(); + + /** + * @return Size of vxlan_header + */ + size_t getHeaderLen() const { return sizeof(vxlan_header); } + + /** + * Does nothing for this layer + */ + void computeCalculateFields() {} + + std::string toString() const; + + OsiModelLayer getOsiModelLayer() const { return OsiModelDataLinkLayer; } +}; + +} // namespace pcpp #endif // PACKETPP_VXLAN_LAYER diff --git a/Packet++/header/WakeOnLanLayer.h b/Packet++/header/WakeOnLanLayer.h index a3f54bb05a..e9e3b26da0 100644 --- a/Packet++/header/WakeOnLanLayer.h +++ b/Packet++/header/WakeOnLanLayer.h @@ -11,163 +11,171 @@ * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - /** - * Class for representing the Wake on LAN Layer - */ - class WakeOnLanLayer : public Layer - { - private: - void init(uint16_t len); - - public: - /** - * @struct wol_header - * Wake On LAN protocol header - */ +namespace pcpp { +/** + * Class for representing the Wake on LAN Layer + */ +class WakeOnLanLayer : public Layer { + private: + void init(uint16_t len); + + public: + /** + * @struct wol_header + * Wake On LAN protocol header + */ #pragma pack(push, 1) - struct wol_header - { - /// Sync stream (FF FF FF FF FF FF) - uint8_t sync[6]; - /// Target MAC address repeated 16 times - uint8_t addrBody[6 * 16]; - }; + struct wol_header { + /// Sync stream (FF FF FF FF FF FF) + uint8_t sync[6]; + /// Target MAC address repeated 16 times + uint8_t addrBody[6 * 16]; + }; #pragma pack(pop) - /** - * 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 - */ - WakeOnLanLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) - : Layer(data, dataLen, prevLayer, packet) - { - m_Protocol = WakeOnLan; - } - - /** - * Construct a new Wake On Lan Layer with provided values - * @param[in] targetAddr Target MAC address - */ - explicit WakeOnLanLayer(const pcpp::MacAddress &targetAddr); - - /** - * Construct a new Wake On Lan Layer with provided values - * @param[in] targetAddr Target MAC address - * @param[in] password Password as array - * @param[in] len Length of the password array, length of the password should be less than 6 bytes - */ - WakeOnLanLayer(const pcpp::MacAddress &targetAddr, uint8_t *password, uint8_t len); - - /** - * Construct a new Wake On Lan Layer with provided values - * @param[in] targetAddr Target MAC address - * @param[in] password Password as MAC address - */ - WakeOnLanLayer(const pcpp::MacAddress &targetAddr, const pcpp::MacAddress &password); - - /** - * Construct a new Wake On Lan Layer with provided values - * @param[in] targetAddr Target MAC address - * @param[in] password Password as IPv4 address - */ - WakeOnLanLayer(const pcpp::MacAddress &targetAddr, const IPv4Address &password); - - /** - * Get a pointer to the Wake On LAN header. Notice this points directly to the data, so every change will change - * the actual packet data - * @return A pointer to the wol_header - */ - inline wol_header *getWakeOnLanHeader() const { return (wol_header *)m_Data; } - - /** - * Get the target MAC address of the command - * @return MAC address of the target - */ - pcpp::MacAddress getTargetAddr() const; - - /** - * Set the target MAC address - * @param[in] targetAddr MAC address of the target - */ - void setTargetAddr(const pcpp::MacAddress &targetAddr); - - /** - * Get the password of the command - * @return Returns the password if exists, empty string otherwise - */ - std::string getPassword() const; - - /** - * Set the password of the command - * @param[in] password Password as array - * @param[in] len Length of the password array, length of the password should be less than 6 bytes - * @return True if operation successful, false otherwise - */ - bool setPassword(const uint8_t *password, uint8_t len); - - /** - * Set the password of the command - * @param[in] password Password as string. Length of the password should be less than 6 bytes - * @return True if operation successful, false otherwise - */ - bool setPassword(const std::string &password); - - /** - * Set the password of the command - * @param[in] addr Password as MAC address - * @return True if operation successful, false otherwise - */ - bool setPassword(const MacAddress &addr); - - /** - * Set the password of the command - * @param addr Password as IPv4 address - * @return True if operation successful, false otherwise - */ - bool setPassword(const IPv4Address &addr); - - /** - * A static method that checks whether the port is considered as Wake on LAN - * @param[in] port The port number to be checked - */ - static bool isWakeOnLanPort(uint16_t port) { return (port == 0) || (port == 7) || (port == 9); } - - /** - * A static method that takes a byte array and detects whether it is a Wake on LAN message - * @param[in] data A byte array - * @param[in] dataSize The byte array size (in bytes) - * @return True if the data is identified as Wake on LAN message - */ - static bool isDataValid(const uint8_t *data, size_t dataSize); - - // overridden methods - - /// Parses the next layer. Wake on LAN is the always last so does nothing for this layer - void parseNextLayer() {} - - /** - * @return Get the size of the layer - */ - size_t getHeaderLen() const { return m_DataLen; } - - /// Does nothing for this layer - void computeCalculateFields() {} - - /** - * @return The OSI layer level of Wake on LAN (Data Link Layer) - */ - OsiModelLayer getOsiModelLayer() const { return OsiModelDataLinkLayer; } - - /** - * @return Returns the protocol info as readable string - */ - std::string toString() const; - }; + /** + * 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 + */ + WakeOnLanLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet) + : Layer(data, dataLen, prevLayer, packet) { + m_Protocol = WakeOnLan; + } + + /** + * Construct a new Wake On Lan Layer with provided values + * @param[in] targetAddr Target MAC address + */ + explicit WakeOnLanLayer(const pcpp::MacAddress& targetAddr); + + /** + * Construct a new Wake On Lan Layer with provided values + * @param[in] targetAddr Target MAC address + * @param[in] password Password as array + * @param[in] len Length of the password array, length of the password should + * be less than 6 bytes + */ + WakeOnLanLayer(const pcpp::MacAddress& targetAddr, uint8_t* password, + uint8_t len); + + /** + * Construct a new Wake On Lan Layer with provided values + * @param[in] targetAddr Target MAC address + * @param[in] password Password as MAC address + */ + WakeOnLanLayer(const pcpp::MacAddress& targetAddr, + const pcpp::MacAddress& password); + + /** + * Construct a new Wake On Lan Layer with provided values + * @param[in] targetAddr Target MAC address + * @param[in] password Password as IPv4 address + */ + WakeOnLanLayer(const pcpp::MacAddress& targetAddr, + const IPv4Address& password); + + /** + * Get a pointer to the Wake On LAN header. Notice this points directly to the + * data, so every change will change the actual packet data + * @return A pointer to the wol_header + */ + inline wol_header* getWakeOnLanHeader() const { return (wol_header*)m_Data; } + + /** + * Get the target MAC address of the command + * @return MAC address of the target + */ + pcpp::MacAddress getTargetAddr() const; + + /** + * Set the target MAC address + * @param[in] targetAddr MAC address of the target + */ + void setTargetAddr(const pcpp::MacAddress& targetAddr); + + /** + * Get the password of the command + * @return Returns the password if exists, empty string otherwise + */ + std::string getPassword() const; + + /** + * Set the password of the command + * @param[in] password Password as array + * @param[in] len Length of the password array, length of the password should + * be less than 6 bytes + * @return True if operation successful, false otherwise + */ + bool setPassword(const uint8_t* password, uint8_t len); + + /** + * Set the password of the command + * @param[in] password Password as string. Length of the password should be + * less than 6 bytes + * @return True if operation successful, false otherwise + */ + bool setPassword(const std::string& password); + + /** + * Set the password of the command + * @param[in] addr Password as MAC address + * @return True if operation successful, false otherwise + */ + bool setPassword(const MacAddress& addr); + + /** + * Set the password of the command + * @param addr Password as IPv4 address + * @return True if operation successful, false otherwise + */ + bool setPassword(const IPv4Address& addr); + + /** + * A static method that checks whether the port is considered as Wake on LAN + * @param[in] port The port number to be checked + */ + static bool isWakeOnLanPort(uint16_t port) { + return (port == 0) || (port == 7) || (port == 9); + } + + /** + * A static method that takes a byte array and detects whether it is a Wake on + * LAN message + * @param[in] data A byte array + * @param[in] dataSize The byte array size (in bytes) + * @return True if the data is identified as Wake on LAN message + */ + static bool isDataValid(const uint8_t* data, size_t dataSize); + + // overridden methods + + /// Parses the next layer. Wake on LAN is the always last so does nothing for + /// this layer + void parseNextLayer() {} + + /** + * @return Get the size of the layer + */ + size_t getHeaderLen() const { return m_DataLen; } + + /// Does nothing for this layer + void computeCalculateFields() {} + + /** + * @return The OSI layer level of Wake on LAN (Data Link Layer) + */ + OsiModelLayer getOsiModelLayer() const { return OsiModelDataLinkLayer; } + + /** + * @return Returns the protocol info as readable string + */ + std::string toString() const; +}; } // namespace pcpp #endif /* PACKETPP_WAKEONLAN_LAYER */ diff --git a/Packet++/src/ArpLayer.cpp b/Packet++/src/ArpLayer.cpp index c821794412..25bd9bcabd 100644 --- a/Packet++/src/ArpLayer.cpp +++ b/Packet++/src/ArpLayer.cpp @@ -1,61 +1,56 @@ #define LOG_MODULE PacketLogModuleArpLayer #include "ArpLayer.h" +#include "EndianPortable.h" #include "EthLayer.h" #include -#include "EndianPortable.h" -namespace pcpp -{ - -ArpLayer::ArpLayer(ArpOpcode opCode, const MacAddress& senderMacAddr, const MacAddress& targetMacAddr, const IPv4Address& senderIpAddr, const IPv4Address& targetIpAddr) -{ - const size_t headerLen = sizeof(arphdr); - m_DataLen = headerLen; - m_Data = new uint8_t[headerLen]; - memset(m_Data, 0, sizeof(headerLen)); - m_Protocol = ARP; - - arphdr* arpHeader = getArpHeader(); - arpHeader->opcode = htobe16(static_cast(opCode)); - targetMacAddr.copyTo(arpHeader->targetMacAddr); - senderMacAddr.copyTo(arpHeader->senderMacAddr); - arpHeader->targetIpAddr = targetIpAddr.toInt(); - arpHeader->senderIpAddr = senderIpAddr.toInt(); +namespace pcpp { + +ArpLayer::ArpLayer(ArpOpcode opCode, const MacAddress& senderMacAddr, + const MacAddress& targetMacAddr, + const IPv4Address& senderIpAddr, + const IPv4Address& targetIpAddr) { + const size_t headerLen = sizeof(arphdr); + m_DataLen = headerLen; + m_Data = new uint8_t[headerLen]; + memset(m_Data, 0, sizeof(headerLen)); + m_Protocol = ARP; + + arphdr* arpHeader = getArpHeader(); + arpHeader->opcode = htobe16(static_cast(opCode)); + targetMacAddr.copyTo(arpHeader->targetMacAddr); + senderMacAddr.copyTo(arpHeader->senderMacAddr); + arpHeader->targetIpAddr = targetIpAddr.toInt(); + arpHeader->senderIpAddr = senderIpAddr.toInt(); } -void ArpLayer::computeCalculateFields() -{ - arphdr* arpHeader = getArpHeader(); - arpHeader->hardwareType = htobe16(1); //Ethernet - arpHeader->hardwareSize = 6; - arpHeader->protocolType = htobe16(PCPP_ETHERTYPE_IP); //assume IPv4 over ARP - arpHeader->protocolSize = 4; //assume IPv4 over ARP - if (arpHeader->opcode == htobe16(ARP_REQUEST)) - MacAddress::Zero.copyTo(arpHeader->targetMacAddr); +void ArpLayer::computeCalculateFields() { + arphdr* arpHeader = getArpHeader(); + arpHeader->hardwareType = htobe16(1); // Ethernet + arpHeader->hardwareSize = 6; + arpHeader->protocolType = htobe16(PCPP_ETHERTYPE_IP); // assume IPv4 over ARP + arpHeader->protocolSize = 4; // assume IPv4 over ARP + if (arpHeader->opcode == htobe16(ARP_REQUEST)) + MacAddress::Zero.copyTo(arpHeader->targetMacAddr); } -bool ArpLayer::isRequest() const -{ - return be16toh(getArpHeader()->opcode) == pcpp::ArpOpcode::ARP_REQUEST; +bool ArpLayer::isRequest() const { + return be16toh(getArpHeader()->opcode) == pcpp::ArpOpcode::ARP_REQUEST; } - -bool ArpLayer::isReply() const -{ - return be16toh(getArpHeader()->opcode) == pcpp::ArpOpcode::ARP_REPLY; +bool ArpLayer::isReply() const { + return be16toh(getArpHeader()->opcode) == pcpp::ArpOpcode::ARP_REPLY; } -std::string ArpLayer::toString() const -{ - if (be16toh(getArpHeader()->opcode) == ARP_REQUEST) - { - return "ARP Layer, ARP request, who has " + getTargetIpAddr().toString() + " ? Tell " + getSenderIpAddr().toString(); - } - else - { - return "ARP Layer, ARP reply, " + getSenderIpAddr().toString() + " is at " + getSenderMacAddress().toString(); - } +std::string ArpLayer::toString() const { + if (be16toh(getArpHeader()->opcode) == ARP_REQUEST) { + return "ARP Layer, ARP request, who has " + getTargetIpAddr().toString() + + " ? Tell " + getSenderIpAddr().toString(); + } else { + return "ARP Layer, ARP reply, " + getSenderIpAddr().toString() + " is at " + + getSenderMacAddress().toString(); + } } } // namespace pcpp diff --git a/Packet++/src/BgpLayer.cpp b/Packet++/src/BgpLayer.cpp index ddf4c91b2d..dcabe987fe 100644 --- a/Packet++/src/BgpLayer.cpp +++ b/Packet++/src/BgpLayer.cpp @@ -1,931 +1,885 @@ #define LOG_MODULE PacketLogModuleBgpLayer -#include -#include "Logger.h" #include "BgpLayer.h" #include "EndianPortable.h" #include "GeneralUtils.h" +#include "Logger.h" +#include -namespace pcpp -{ +namespace pcpp { // ~~~~~~~~ // BgpLayer // ~~~~~~~~ -size_t BgpLayer::getHeaderLen() const -{ - if (m_DataLen < sizeof(bgp_common_header)) - { - return m_DataLen; - } - - uint16_t messageLen = be16toh(getBasicHeader()->length); - if (m_DataLen < messageLen) - { - return m_DataLen; - } - - return (size_t)messageLen; -} - -BgpLayer* BgpLayer::parseBgpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) -{ - if (data == nullptr || dataLen < sizeof(bgp_common_header)) - return nullptr; - - bgp_common_header* bgpHeader = (bgp_common_header*)data; - - // illegal header data - length is too small - uint16_t messageLen = be16toh(bgpHeader->length); - if (dataLen < messageLen || messageLen < static_cast(sizeof(bgp_common_header))) - return nullptr; - - switch (bgpHeader->messageType) - { - case 1: // OPEN - return new BgpOpenMessageLayer(data, dataLen, prevLayer, packet); - case 2: // UPDATE - return BgpUpdateMessageLayer::isDataValid(data, dataLen) ? new BgpUpdateMessageLayer(data, dataLen, prevLayer, packet) : nullptr; - case 3: // NOTIFICATION - return new BgpNotificationMessageLayer(data, dataLen, prevLayer, packet); - case 4: // KEEPALIVE - return new BgpKeepaliveMessageLayer(data, dataLen, prevLayer, packet); - case 5: // ROUTE-REFRESH - return new BgpRouteRefreshMessageLayer(data, dataLen, prevLayer, packet); - default: - return nullptr; - } -} - -std::string BgpLayer::getMessageTypeAsString() const -{ - switch (getBgpMessageType()) - { - case BgpLayer::Open: - return "OPEN"; - case BgpLayer::Update: - return "UPDATE"; - case BgpLayer::Notification: - return "NOTIFICATION"; - case BgpLayer::Keepalive: - return "KEEPALIVE"; - case BgpLayer::RouteRefresh: - return "ROUTE-REFRESH"; - default: - return "Unknown"; - } -} - -void BgpLayer::parseNextLayer() -{ - size_t headerLen = getHeaderLen(); - if (m_DataLen <= headerLen || headerLen == 0) - return; - - uint8_t* payload = m_Data + headerLen; - size_t payloadLen = m_DataLen - headerLen; - - m_NextLayer = BgpLayer::parseBgpLayer(payload, payloadLen, this, m_Packet); -} - -std::string BgpLayer::toString() const -{ - return "BGP Layer, " + getMessageTypeAsString() + " message"; -} - -void BgpLayer::computeCalculateFields() -{ - bgp_common_header* bgpHeader = getBasicHeader(); - memset(bgpHeader->marker, 0xff, 16*sizeof(uint8_t)); - bgpHeader->messageType = (uint8_t)getBgpMessageType(); - bgpHeader->length = htobe16(getHeaderLen()); -} - -void BgpLayer::setBgpFields(size_t messageLen) -{ - bgp_common_header* bgpHdr = getBasicHeader(); - memset(bgpHdr->marker, 0xff, 16*sizeof(uint8_t)); - bgpHdr->messageType = (uint8_t)getBgpMessageType(); - if (messageLen != 0) - { - bgpHdr->length = htobe16((uint16_t)messageLen); - } - else - { - bgpHdr->length = m_DataLen; - } +size_t BgpLayer::getHeaderLen() const { + if (m_DataLen < sizeof(bgp_common_header)) { + return m_DataLen; + } + + uint16_t messageLen = be16toh(getBasicHeader()->length); + if (m_DataLen < messageLen) { + return m_DataLen; + } + + return (size_t)messageLen; +} + +BgpLayer* BgpLayer::parseBgpLayer(uint8_t* data, size_t dataLen, + Layer* prevLayer, Packet* packet) { + if (data == nullptr || dataLen < sizeof(bgp_common_header)) + return nullptr; + + bgp_common_header* bgpHeader = (bgp_common_header*)data; + + // illegal header data - length is too small + uint16_t messageLen = be16toh(bgpHeader->length); + if (dataLen < messageLen || + messageLen < static_cast(sizeof(bgp_common_header))) + return nullptr; + + switch (bgpHeader->messageType) { + case 1: // OPEN + return new BgpOpenMessageLayer(data, dataLen, prevLayer, packet); + case 2: // UPDATE + return BgpUpdateMessageLayer::isDataValid(data, dataLen) + ? new BgpUpdateMessageLayer(data, dataLen, prevLayer, packet) + : nullptr; + case 3: // NOTIFICATION + return new BgpNotificationMessageLayer(data, dataLen, prevLayer, packet); + case 4: // KEEPALIVE + return new BgpKeepaliveMessageLayer(data, dataLen, prevLayer, packet); + case 5: // ROUTE-REFRESH + return new BgpRouteRefreshMessageLayer(data, dataLen, prevLayer, packet); + default: + return nullptr; + } +} + +std::string BgpLayer::getMessageTypeAsString() const { + switch (getBgpMessageType()) { + case BgpLayer::Open: + return "OPEN"; + case BgpLayer::Update: + return "UPDATE"; + case BgpLayer::Notification: + return "NOTIFICATION"; + case BgpLayer::Keepalive: + return "KEEPALIVE"; + case BgpLayer::RouteRefresh: + return "ROUTE-REFRESH"; + default: + return "Unknown"; + } +} + +void BgpLayer::parseNextLayer() { + size_t headerLen = getHeaderLen(); + if (m_DataLen <= headerLen || headerLen == 0) + return; + + uint8_t* payload = m_Data + headerLen; + size_t payloadLen = m_DataLen - headerLen; + + m_NextLayer = BgpLayer::parseBgpLayer(payload, payloadLen, this, m_Packet); +} + +std::string BgpLayer::toString() const { + return "BGP Layer, " + getMessageTypeAsString() + " message"; +} + +void BgpLayer::computeCalculateFields() { + bgp_common_header* bgpHeader = getBasicHeader(); + memset(bgpHeader->marker, 0xff, 16 * sizeof(uint8_t)); + bgpHeader->messageType = (uint8_t)getBgpMessageType(); + bgpHeader->length = htobe16(getHeaderLen()); +} + +void BgpLayer::setBgpFields(size_t messageLen) { + bgp_common_header* bgpHdr = getBasicHeader(); + memset(bgpHdr->marker, 0xff, 16 * sizeof(uint8_t)); + bgpHdr->messageType = (uint8_t)getBgpMessageType(); + if (messageLen != 0) { + bgpHdr->length = htobe16((uint16_t)messageLen); + } else { + bgpHdr->length = m_DataLen; + } } - - // ~~~~~~~~~~~~~~~~~~~~ // BgpOpenMessageLayer // ~~~~~~~~~~~~~~~~~~~~ -BgpOpenMessageLayer::optional_parameter::optional_parameter(uint8_t typeVal, const std::string& valueAsHexString) -{ - type = typeVal; - length = hexStringToByteArray(valueAsHexString, value, 32); -} - -BgpOpenMessageLayer::BgpOpenMessageLayer(uint16_t myAutonomousSystem, uint16_t holdTime, const IPv4Address& bgpId, - const std::vector& optionalParams) -{ - uint8_t optionalParamsData[1500]; - size_t optionalParamsDataLen = optionalParamsToByteArray(optionalParams, optionalParamsData, 1500); - - const size_t headerLen = sizeof(bgp_open_message) + optionalParamsDataLen; - m_DataLen = headerLen; - m_Data = new uint8_t[headerLen]; - memset(m_Data, 0, headerLen); - setBgpFields(headerLen); - - bgp_open_message* msgHdr = getOpenMsgHeader(); - msgHdr->version = 4; - msgHdr->myAutonomousSystem = htobe16(myAutonomousSystem); - msgHdr->holdTime = htobe16(holdTime); - msgHdr->bgpId = bgpId.toInt(); - msgHdr->optionalParameterLength = optionalParamsDataLen; - if (optionalParamsDataLen > 0) - { - memcpy(m_Data + sizeof(bgp_open_message), optionalParamsData, optionalParamsDataLen); - } - - m_Protocol = BGP; -} - -size_t BgpOpenMessageLayer::optionalParamsToByteArray(const std::vector& optionalParams, uint8_t* resultByteArr, size_t maxByteArrSize) -{ - if (resultByteArr == nullptr || maxByteArrSize == 0) - { - return 0; - } - - size_t dataLen = 0; - - for (std::vector::const_iterator iter = optionalParams.begin(); iter != optionalParams.end(); iter++) - { - if (iter->length > 32) - { - PCPP_LOG_ERROR("Illegal optional parameter length " << (int)iter->length << ", must be 32 bytes or less"); - break; // illegal value - } - - size_t curDataSize = 2*sizeof(uint8_t) + (size_t)iter->length; - - if (dataLen + curDataSize > maxByteArrSize) - { - break; - } - - resultByteArr[0] = iter->type; - resultByteArr[1] = iter->length; - if (iter->length > 0) - { - memcpy(resultByteArr + 2*sizeof(uint8_t), iter->value, iter->length); - } - - dataLen += curDataSize; - resultByteArr += curDataSize; - } - - return dataLen; -} - -void BgpOpenMessageLayer::setBgpId(const IPv4Address& newBgpId) -{ - if (!newBgpId.isValid()) - { - return; - } - - bgp_open_message* msgHdr = getOpenMsgHeader(); - if (msgHdr == nullptr) - { - return; - } - - msgHdr->bgpId = newBgpId.toInt(); -} - -void BgpOpenMessageLayer::getOptionalParameters(std::vector& optionalParameters) -{ - bgp_open_message* msgHdr = getOpenMsgHeader(); - if (msgHdr == nullptr || msgHdr->optionalParameterLength == 0) - { - return; - } - - size_t optionalParamsLen = (size_t)be16toh(msgHdr->optionalParameterLength); - - if (optionalParamsLen > getHeaderLen() - sizeof(bgp_open_message)) - { - optionalParamsLen = getHeaderLen() - sizeof(bgp_open_message); - } - - uint8_t* dataPtr = m_Data + sizeof(bgp_open_message); - size_t byteCount = 0; - while (byteCount < optionalParamsLen) - { - optional_parameter op; - op.type = dataPtr[0]; - op.length = dataPtr[1]; - - if (op.length > optionalParamsLen - byteCount) - { - PCPP_LOG_ERROR("Optional parameter length is out of bounds: " << (int)op.length); - break; - } - - if (op.length > 0) - { - memcpy(op.value, dataPtr + 2*sizeof(uint8_t), (op.length > 32 ? 32 : op.length)); - } - - optionalParameters.push_back(op); - size_t totalLen = 2 + (size_t)op.length; - byteCount += totalLen; - dataPtr += totalLen; - } -} - -size_t BgpOpenMessageLayer::getOptionalParametersLength() -{ - bgp_open_message* msgHdr = getOpenMsgHeader(); - if (msgHdr != nullptr) - { - return (size_t)(msgHdr->optionalParameterLength); - } - - return 0; -} - -bool BgpOpenMessageLayer::setOptionalParameters(const std::vector& optionalParameters) -{ - uint8_t newOptionalParamsData[1500]; - size_t newOptionalParamsDataLen = optionalParamsToByteArray(optionalParameters, newOptionalParamsData, 1500); - size_t curOptionalParamsDataLen = getOptionalParametersLength(); - - if (newOptionalParamsDataLen > curOptionalParamsDataLen) - { - bool res = extendLayer(sizeof(bgp_open_message), newOptionalParamsDataLen - curOptionalParamsDataLen); - if (!res) - { - PCPP_LOG_ERROR("Couldn't extend BGP open layer to include the additional optional parameters"); - return res; - } - } - else if (newOptionalParamsDataLen < curOptionalParamsDataLen) - { - bool res = shortenLayer(sizeof(bgp_open_message), curOptionalParamsDataLen - newOptionalParamsDataLen); - if (!res) - { - PCPP_LOG_ERROR("Couldn't shorten BGP open layer to set the right size of the optional parameters data"); - return res; - } - } +BgpOpenMessageLayer::optional_parameter::optional_parameter( + uint8_t typeVal, const std::string& valueAsHexString) { + type = typeVal; + length = hexStringToByteArray(valueAsHexString, value, 32); +} + +BgpOpenMessageLayer::BgpOpenMessageLayer( + uint16_t myAutonomousSystem, uint16_t holdTime, const IPv4Address& bgpId, + const std::vector& optionalParams) { + uint8_t optionalParamsData[1500]; + size_t optionalParamsDataLen = + optionalParamsToByteArray(optionalParams, optionalParamsData, 1500); + + const size_t headerLen = sizeof(bgp_open_message) + optionalParamsDataLen; + m_DataLen = headerLen; + m_Data = new uint8_t[headerLen]; + memset(m_Data, 0, headerLen); + setBgpFields(headerLen); + + bgp_open_message* msgHdr = getOpenMsgHeader(); + msgHdr->version = 4; + msgHdr->myAutonomousSystem = htobe16(myAutonomousSystem); + msgHdr->holdTime = htobe16(holdTime); + msgHdr->bgpId = bgpId.toInt(); + msgHdr->optionalParameterLength = optionalParamsDataLen; + if (optionalParamsDataLen > 0) { + memcpy(m_Data + sizeof(bgp_open_message), optionalParamsData, + optionalParamsDataLen); + } - if (newOptionalParamsDataLen > 0) - { - memcpy(m_Data + sizeof(bgp_open_message), newOptionalParamsData, newOptionalParamsDataLen); - } - - getOpenMsgHeader()->optionalParameterLength = (uint8_t)newOptionalParamsDataLen; - getOpenMsgHeader()->length = htobe16(sizeof(bgp_open_message) + newOptionalParamsDataLen); - - return true; -} - -bool BgpOpenMessageLayer::clearOptionalParameters() -{ - return setOptionalParameters(std::vector()); + m_Protocol = BGP; +} + +size_t BgpOpenMessageLayer::optionalParamsToByteArray( + const std::vector& optionalParams, + uint8_t* resultByteArr, size_t maxByteArrSize) { + if (resultByteArr == nullptr || maxByteArrSize == 0) { + return 0; + } + + size_t dataLen = 0; + + for (std::vector::const_iterator iter = + optionalParams.begin(); + iter != optionalParams.end(); iter++) { + if (iter->length > 32) { + PCPP_LOG_ERROR("Illegal optional parameter length " + << (int)iter->length << ", must be 32 bytes or less"); + break; // illegal value + } + + size_t curDataSize = 2 * sizeof(uint8_t) + (size_t)iter->length; + + if (dataLen + curDataSize > maxByteArrSize) { + break; + } + + resultByteArr[0] = iter->type; + resultByteArr[1] = iter->length; + if (iter->length > 0) { + memcpy(resultByteArr + 2 * sizeof(uint8_t), iter->value, iter->length); + } + + dataLen += curDataSize; + resultByteArr += curDataSize; + } + + return dataLen; +} + +void BgpOpenMessageLayer::setBgpId(const IPv4Address& newBgpId) { + if (!newBgpId.isValid()) { + return; + } + + bgp_open_message* msgHdr = getOpenMsgHeader(); + if (msgHdr == nullptr) { + return; + } + + msgHdr->bgpId = newBgpId.toInt(); +} + +void BgpOpenMessageLayer::getOptionalParameters( + std::vector& optionalParameters) { + bgp_open_message* msgHdr = getOpenMsgHeader(); + if (msgHdr == nullptr || msgHdr->optionalParameterLength == 0) { + return; + } + + size_t optionalParamsLen = (size_t)be16toh(msgHdr->optionalParameterLength); + + if (optionalParamsLen > getHeaderLen() - sizeof(bgp_open_message)) { + optionalParamsLen = getHeaderLen() - sizeof(bgp_open_message); + } + + uint8_t* dataPtr = m_Data + sizeof(bgp_open_message); + size_t byteCount = 0; + while (byteCount < optionalParamsLen) { + optional_parameter op; + op.type = dataPtr[0]; + op.length = dataPtr[1]; + + if (op.length > optionalParamsLen - byteCount) { + PCPP_LOG_ERROR( + "Optional parameter length is out of bounds: " << (int)op.length); + break; + } + + if (op.length > 0) { + memcpy(op.value, dataPtr + 2 * sizeof(uint8_t), + (op.length > 32 ? 32 : op.length)); + } + + optionalParameters.push_back(op); + size_t totalLen = 2 + (size_t)op.length; + byteCount += totalLen; + dataPtr += totalLen; + } +} + +size_t BgpOpenMessageLayer::getOptionalParametersLength() { + bgp_open_message* msgHdr = getOpenMsgHeader(); + if (msgHdr != nullptr) { + return (size_t)(msgHdr->optionalParameterLength); + } + + return 0; +} + +bool BgpOpenMessageLayer::setOptionalParameters( + const std::vector& optionalParameters) { + uint8_t newOptionalParamsData[1500]; + size_t newOptionalParamsDataLen = optionalParamsToByteArray( + optionalParameters, newOptionalParamsData, 1500); + size_t curOptionalParamsDataLen = getOptionalParametersLength(); + + if (newOptionalParamsDataLen > curOptionalParamsDataLen) { + bool res = extendLayer(sizeof(bgp_open_message), + newOptionalParamsDataLen - curOptionalParamsDataLen); + if (!res) { + PCPP_LOG_ERROR("Couldn't extend BGP open layer to include the additional " + "optional parameters"); + return res; + } + } else if (newOptionalParamsDataLen < curOptionalParamsDataLen) { + bool res = + shortenLayer(sizeof(bgp_open_message), + curOptionalParamsDataLen - newOptionalParamsDataLen); + if (!res) { + PCPP_LOG_ERROR("Couldn't shorten BGP open layer to set the right size of " + "the optional parameters data"); + return res; + } + } + + if (newOptionalParamsDataLen > 0) { + memcpy(m_Data + sizeof(bgp_open_message), newOptionalParamsData, + newOptionalParamsDataLen); + } + + getOpenMsgHeader()->optionalParameterLength = + (uint8_t)newOptionalParamsDataLen; + getOpenMsgHeader()->length = + htobe16(sizeof(bgp_open_message) + newOptionalParamsDataLen); + + return true; +} + +bool BgpOpenMessageLayer::clearOptionalParameters() { + return setOptionalParameters(std::vector()); } - - // ~~~~~~~~~~~~~~~~~~~~~ // BgpUpdateMessageLayer // ~~~~~~~~~~~~~~~~~~~~~ -BgpUpdateMessageLayer::path_attribute::path_attribute(uint8_t flagsVal, uint8_t typeVal, const std::string& dataAsHexString) -{ - flags = flagsVal; - type = typeVal; - length = hexStringToByteArray(dataAsHexString, data, 32); +BgpUpdateMessageLayer::path_attribute::path_attribute( + uint8_t flagsVal, uint8_t typeVal, const std::string& dataAsHexString) { + flags = flagsVal; + type = typeVal; + length = hexStringToByteArray(dataAsHexString, data, 32); } BgpUpdateMessageLayer::BgpUpdateMessageLayer( - const std::vector& withdrawnRoutes, - const std::vector& pathAttributes, - const std::vector& nlri) -{ - uint8_t withdrawnRoutesData[1500]; - uint8_t pathAttributesData[1500]; - uint8_t nlriData[1500]; - size_t withdrawnRoutesDataLen = prefixAndIPDataToByteArray(withdrawnRoutes, withdrawnRoutesData, 1500); - size_t pathAttributesDataLen = pathAttributesToByteArray(pathAttributes, pathAttributesData, 1500); - size_t nlriDataLen = prefixAndIPDataToByteArray(nlri, nlriData, 1500); - - size_t headerLen = sizeof(bgp_common_header) + 2*sizeof(uint16_t) + withdrawnRoutesDataLen + pathAttributesDataLen + nlriDataLen; - m_DataLen = headerLen; - m_Data = new uint8_t[headerLen]; - memset(m_Data, 0, headerLen); - setBgpFields(headerLen); - - uint8_t* dataPtr = m_Data + sizeof(bgp_common_header); - - // copy withdrawn routes data - uint16_t withdrawnRoutesDataLenBE = htobe16(withdrawnRoutesDataLen); - memcpy(dataPtr, &withdrawnRoutesDataLenBE, sizeof(uint16_t)); - dataPtr += sizeof(uint16_t); - if (withdrawnRoutesDataLen > 0) - { - memcpy(dataPtr, withdrawnRoutesData, withdrawnRoutesDataLen); - dataPtr += withdrawnRoutesDataLen; - } - - // copy path attributes data - uint16_t pathAttributesDataLenBE = htobe16(pathAttributesDataLen); - memcpy(dataPtr, &pathAttributesDataLenBE, sizeof(uint16_t)); - dataPtr += sizeof(uint16_t); - if (pathAttributesDataLen > 0) - { - memcpy(dataPtr, pathAttributesData, pathAttributesDataLen); - dataPtr += pathAttributesDataLen; - } - - // copy nlri data - if (nlriDataLen > 0) - { - memcpy(dataPtr, nlriData, nlriDataLen); - } - - m_Protocol = BGP; -} - -void BgpUpdateMessageLayer::parsePrefixAndIPData(uint8_t* dataPtr, size_t dataLen, std::vector& result) -{ - size_t byteCount = 0; - while (byteCount < dataLen) - { - prefix_and_ip wr; - wr.prefix = dataPtr[0]; - size_t curByteCount = 1; - if (wr.prefix == 32) - { - uint8_t octets[4] = { dataPtr[1], dataPtr[2], dataPtr[3], dataPtr[4] }; - wr.ipAddr = IPv4Address(octets); - curByteCount += 4; - } - else if (wr.prefix == 24) - { - uint8_t octets[4] = { dataPtr[1], dataPtr[2], dataPtr[3], 0 }; - wr.ipAddr = IPv4Address(octets); - curByteCount += 3; - } - else if (wr.prefix == 16) - { - uint8_t octets[4] = { dataPtr[1], dataPtr[2], 0, 0 }; - wr.ipAddr = IPv4Address(octets); - curByteCount += 2; - } - else if (wr.prefix == 8) - { - uint8_t octets[4] = { dataPtr[1], 0, 0, 0 }; - wr.ipAddr = IPv4Address(octets); - curByteCount += 1; - } - else - { - PCPP_LOG_DEBUG("Illegal prefix value " << (int)wr.prefix); - break; // illegal value - } - - result.push_back(wr); - dataPtr += curByteCount; - byteCount += curByteCount; - } -} - -size_t BgpUpdateMessageLayer::prefixAndIPDataToByteArray(const std::vector& prefixAndIpData, uint8_t* resultByteArr, size_t maxByteArrSize) -{ - if (resultByteArr == nullptr || maxByteArrSize == 0) - { - return 0; - } - - size_t dataLen = 0; - - for (std::vector::const_iterator iter = prefixAndIpData.begin(); iter != prefixAndIpData.end(); iter++) - { - uint8_t curData[5]; - curData[0] = iter->prefix; - size_t curDataSize = 1; - const uint8_t* octets = iter->ipAddr.toBytes(); - if (iter->prefix == 32) - { - curDataSize += 4; - curData[1] = octets[0]; - curData[2] = octets[1]; - curData[3] = octets[2]; - curData[4] = octets[3]; - } - else if (iter->prefix == 24) - { - curDataSize += 3; - curData[1] = octets[0]; - curData[2] = octets[1]; - curData[3] = octets[2]; - } - else if (iter->prefix == 16) - { - curDataSize += 2; - curData[1] = octets[0]; - curData[2] = octets[1]; - } - else if (iter->prefix == 8) - { - curDataSize += 1; - curData[1] = octets[0]; - } - else - { - PCPP_LOG_ERROR("Illegal prefix value " << (int)iter->prefix); - break; // illegal value - } - - if (dataLen + curDataSize > maxByteArrSize) - { - break; - } - - dataLen += curDataSize; - - memcpy(resultByteArr, curData, curDataSize); - resultByteArr += curDataSize; - } - - return dataLen; -} - -size_t BgpUpdateMessageLayer::pathAttributesToByteArray(const std::vector& pathAttributes, uint8_t* resultByteArr, size_t maxByteArrSize) -{ - if (resultByteArr == nullptr || maxByteArrSize == 0) - { - return 0; - } - - size_t dataLen = 0; - - for (std::vector::const_iterator iter = pathAttributes.begin(); iter != pathAttributes.end(); iter++) - { - if (iter->length > 32) - { - PCPP_LOG_ERROR("Illegal path attribute length " << (int)iter->length); - break; // illegal value - } - - size_t curDataSize = 3*sizeof(uint8_t) + (size_t)iter->length; - - if (dataLen + curDataSize > maxByteArrSize) - { - break; - } - - resultByteArr[0] = iter->flags; - resultByteArr[1] = iter->type; - resultByteArr[2] = iter->length; - if (iter->length > 0) - { - memcpy(resultByteArr + 3*sizeof(uint8_t), iter->data, iter->length); - } - - dataLen += curDataSize; - resultByteArr += curDataSize; - } - - return dataLen; -} - -size_t BgpUpdateMessageLayer::getWithdrawnRoutesLength() const -{ - size_t headerLen = getHeaderLen(); - size_t minLen = sizeof(bgp_common_header) + sizeof(uint16_t); - if (headerLen >= minLen) - { - uint16_t res = be16toh(*(uint16_t*)(m_Data + sizeof(bgp_common_header))); - if ((size_t)res > headerLen - minLen) - { - return headerLen - minLen; - } - - return (size_t)res; - } - - return 0; -} - -void BgpUpdateMessageLayer::getWithdrawnRoutes(std::vector& withdrawnRoutes) -{ - size_t withdrawnRouteLen = getWithdrawnRoutesLength(); - if (withdrawnRouteLen == 0) - { - return; - } - - uint8_t* dataPtr = m_Data + sizeof(bgp_common_header) + sizeof(uint16_t); - parsePrefixAndIPData(dataPtr, withdrawnRouteLen, withdrawnRoutes); -} - -size_t BgpUpdateMessageLayer::getPathAttributesLength() const -{ - size_t headerLen = getHeaderLen(); - size_t minLen = sizeof(bgp_common_header) + 2*sizeof(uint16_t); - if (headerLen >= minLen) - { - size_t withdrawnRouteLen = getWithdrawnRoutesLength(); - uint16_t res = be16toh(*(uint16_t*)(m_Data + sizeof(bgp_common_header) + sizeof(uint16_t) + withdrawnRouteLen)); - if ((size_t)res > headerLen - minLen - withdrawnRouteLen) - { - return headerLen - minLen - withdrawnRouteLen; - } - - return (size_t)res; - } - - return 0; -} - -bool BgpUpdateMessageLayer::setWithdrawnRoutes(const std::vector& withdrawnRoutes) -{ - uint8_t newWithdrawnRoutesData[1500]; - size_t newWithdrawnRoutesDataLen = prefixAndIPDataToByteArray(withdrawnRoutes, newWithdrawnRoutesData, 1500); - size_t curWithdrawnRoutesDataLen = getWithdrawnRoutesLength(); - - if (newWithdrawnRoutesDataLen > curWithdrawnRoutesDataLen) - { - bool res = extendLayer(sizeof(bgp_common_header) + sizeof(uint16_t), newWithdrawnRoutesDataLen - curWithdrawnRoutesDataLen); - if (!res) - { - PCPP_LOG_ERROR("Couldn't extend BGP update layer to include the additional withdrawn routes"); - return res; - } - } - else if (newWithdrawnRoutesDataLen < curWithdrawnRoutesDataLen) - { - bool res = shortenLayer(sizeof(bgp_common_header) + sizeof(uint16_t), curWithdrawnRoutesDataLen - newWithdrawnRoutesDataLen); - if (!res) - { - PCPP_LOG_ERROR("Couldn't shorten BGP update layer to set the right size of the withdrawn routes data"); - return res; - } - } - - if (newWithdrawnRoutesDataLen > 0) - { - memcpy(m_Data + sizeof(bgp_common_header) + sizeof(uint16_t), newWithdrawnRoutesData, newWithdrawnRoutesDataLen); - } - - getBasicHeader()->length = htobe16(be16toh(getBasicHeader()->length) + newWithdrawnRoutesDataLen - curWithdrawnRoutesDataLen); - - uint16_t newWithdrawnRoutesDataLenBE = htobe16(newWithdrawnRoutesDataLen); - memcpy(m_Data + sizeof(bgp_common_header), &newWithdrawnRoutesDataLenBE, sizeof(uint16_t)); - - return true; -} - -bool BgpUpdateMessageLayer::clearWithdrawnRoutes() -{ - return setWithdrawnRoutes(std::vector()); -} - -void BgpUpdateMessageLayer::getPathAttributes(std::vector& pathAttributes) -{ - size_t pathAttrLen = getPathAttributesLength(); - if (pathAttrLen == 0) - { - return; - } - - uint8_t* dataPtr = m_Data + sizeof(bgp_common_header) + 2*sizeof(uint16_t) + getWithdrawnRoutesLength(); - size_t byteCount = 0; - while (byteCount < pathAttrLen) - { - path_attribute pa; - pa.flags = dataPtr[0]; - pa.type = dataPtr[1]; - pa.length = dataPtr[2]; - size_t curByteCount = 3 + pa.length; - if (pa.length > 0) - { - size_t dataLenToCopy = (pa.length <= 32 ? pa.length : 32); - memcpy(pa.data, dataPtr+3, dataLenToCopy); - } - - pathAttributes.push_back(pa); - dataPtr += curByteCount; - byteCount += curByteCount; - } -} - -bool BgpUpdateMessageLayer::setPathAttributes(const std::vector& pathAttributes) -{ - uint8_t newPathAttributesData[1500]; - size_t newPathAttributesDataLen = pathAttributesToByteArray(pathAttributes, newPathAttributesData, 1500); - size_t curPathAttributesDataLen = getPathAttributesLength(); - size_t curWithdrawnRoutesDataLen = getWithdrawnRoutesLength(); - - if (newPathAttributesDataLen > curPathAttributesDataLen) - { - bool res = extendLayer(sizeof(bgp_common_header) + 2*sizeof(uint16_t) + curWithdrawnRoutesDataLen, newPathAttributesDataLen - curPathAttributesDataLen); - if (!res) - { - PCPP_LOG_ERROR("Couldn't extend BGP update layer to include the additional path attributes"); - return res; - } - } - else if (newPathAttributesDataLen < curPathAttributesDataLen) - { - bool res = shortenLayer(sizeof(bgp_common_header) + 2*sizeof(uint16_t) + curWithdrawnRoutesDataLen, curPathAttributesDataLen - newPathAttributesDataLen); - if (!res) - { - PCPP_LOG_ERROR("Couldn't shorten BGP update layer to set the right size of the path attributes data"); - return res; - } - } - - if (newPathAttributesDataLen > 0) - { - memcpy(m_Data + sizeof(bgp_common_header) + 2*sizeof(uint16_t) + curWithdrawnRoutesDataLen, newPathAttributesData, newPathAttributesDataLen); - } - - getBasicHeader()->length = htobe16(be16toh(getBasicHeader()->length) + newPathAttributesDataLen - curPathAttributesDataLen); - - uint16_t newWithdrawnRoutesDataLenBE = htobe16(newPathAttributesDataLen); - memcpy(m_Data + sizeof(bgp_common_header) + sizeof(uint16_t) + curWithdrawnRoutesDataLen, &newWithdrawnRoutesDataLenBE, sizeof(uint16_t)); - - return true; -} - -bool BgpUpdateMessageLayer::clearPathAttributes() -{ - return setPathAttributes(std::vector()); -} - -size_t BgpUpdateMessageLayer::getNetworkLayerReachabilityInfoLength() const -{ - size_t headerLen = getHeaderLen(); - size_t minLen = sizeof(bgp_common_header) + 2*sizeof(uint16_t); - if (headerLen >= minLen) - { - size_t withdrawnRouteLen = getWithdrawnRoutesLength(); - size_t pathAttrLen = getPathAttributesLength(); - int nlriSize = headerLen - minLen - withdrawnRouteLen - pathAttrLen; - if (nlriSize >= 0) - { - return (size_t)nlriSize; - } - - return 0; - } - - return 0; -} - -void BgpUpdateMessageLayer::getNetworkLayerReachabilityInfo(std::vector& nlri) -{ - size_t nlriSize = getNetworkLayerReachabilityInfoLength(); - if (nlriSize == 0) - { - return; - } - - uint8_t* dataPtr = m_Data + sizeof(bgp_common_header) + 2*sizeof(uint16_t) + getWithdrawnRoutesLength() + getPathAttributesLength(); - parsePrefixAndIPData(dataPtr, nlriSize, nlri); -} - -bool BgpUpdateMessageLayer::isDataValid(const uint8_t *data, size_t dataSize) -{ - if (dataSize < sizeof(bgp_common_header) + 2*sizeof(uint16_t)) - return false; - - uint16_t withdrLen = be16toh(*(uint16_t*)(data + sizeof(bgp_common_header))); - if (dataSize < sizeof(bgp_common_header) + 2*sizeof(uint16_t) + withdrLen) - return false; - - uint16_t attrLen = be16toh(*(uint16_t*)(data + sizeof(bgp_common_header) + sizeof(uint16_t) + withdrLen)); - if (dataSize < sizeof(bgp_common_header) + 2*sizeof(uint16_t) + withdrLen + attrLen) - return false; - - return true; -} - -bool BgpUpdateMessageLayer::setNetworkLayerReachabilityInfo(const std::vector& nlri) -{ - uint8_t newNlriData[1500]; - size_t newNlriDataLen = prefixAndIPDataToByteArray(nlri, newNlriData, 1500); - size_t curNlriDataLen = getNetworkLayerReachabilityInfoLength(); - size_t curPathAttributesDataLen = getPathAttributesLength(); - size_t curWithdrawnRoutesDataLen = getWithdrawnRoutesLength(); - - if (newNlriDataLen > curNlriDataLen) - { - bool res = extendLayer(sizeof(bgp_common_header) + 2*sizeof(uint16_t) + curWithdrawnRoutesDataLen + curPathAttributesDataLen, newNlriDataLen - curNlriDataLen); - if (!res) - { - PCPP_LOG_ERROR("Couldn't extend BGP update layer to include the additional NLRI data"); - return res; - } - } - else if (newNlriDataLen < curNlriDataLen) - { - bool res = shortenLayer(sizeof(bgp_common_header) + 2*sizeof(uint16_t) + curWithdrawnRoutesDataLen + curPathAttributesDataLen, curNlriDataLen - newNlriDataLen); - if (!res) - { - PCPP_LOG_ERROR("Couldn't shorten BGP update layer to set the right size of the NLRI data"); - return res; - } - } - - if (newNlriDataLen > 0) - { - memcpy(m_Data + sizeof(bgp_common_header) + 2*sizeof(uint16_t) + curWithdrawnRoutesDataLen + curPathAttributesDataLen, newNlriData, newNlriDataLen); - } + const std::vector& withdrawnRoutes, + const std::vector& pathAttributes, + const std::vector& nlri) { + uint8_t withdrawnRoutesData[1500]; + uint8_t pathAttributesData[1500]; + uint8_t nlriData[1500]; + size_t withdrawnRoutesDataLen = + prefixAndIPDataToByteArray(withdrawnRoutes, withdrawnRoutesData, 1500); + size_t pathAttributesDataLen = + pathAttributesToByteArray(pathAttributes, pathAttributesData, 1500); + size_t nlriDataLen = prefixAndIPDataToByteArray(nlri, nlriData, 1500); + + size_t headerLen = sizeof(bgp_common_header) + 2 * sizeof(uint16_t) + + withdrawnRoutesDataLen + pathAttributesDataLen + + nlriDataLen; + m_DataLen = headerLen; + m_Data = new uint8_t[headerLen]; + memset(m_Data, 0, headerLen); + setBgpFields(headerLen); + + uint8_t* dataPtr = m_Data + sizeof(bgp_common_header); + + // copy withdrawn routes data + uint16_t withdrawnRoutesDataLenBE = htobe16(withdrawnRoutesDataLen); + memcpy(dataPtr, &withdrawnRoutesDataLenBE, sizeof(uint16_t)); + dataPtr += sizeof(uint16_t); + if (withdrawnRoutesDataLen > 0) { + memcpy(dataPtr, withdrawnRoutesData, withdrawnRoutesDataLen); + dataPtr += withdrawnRoutesDataLen; + } + + // copy path attributes data + uint16_t pathAttributesDataLenBE = htobe16(pathAttributesDataLen); + memcpy(dataPtr, &pathAttributesDataLenBE, sizeof(uint16_t)); + dataPtr += sizeof(uint16_t); + if (pathAttributesDataLen > 0) { + memcpy(dataPtr, pathAttributesData, pathAttributesDataLen); + dataPtr += pathAttributesDataLen; + } + + // copy nlri data + if (nlriDataLen > 0) { + memcpy(dataPtr, nlriData, nlriDataLen); + } + + m_Protocol = BGP; +} + +void BgpUpdateMessageLayer::parsePrefixAndIPData( + uint8_t* dataPtr, size_t dataLen, std::vector& result) { + size_t byteCount = 0; + while (byteCount < dataLen) { + prefix_and_ip wr; + wr.prefix = dataPtr[0]; + size_t curByteCount = 1; + if (wr.prefix == 32) { + uint8_t octets[4] = {dataPtr[1], dataPtr[2], dataPtr[3], dataPtr[4]}; + wr.ipAddr = IPv4Address(octets); + curByteCount += 4; + } else if (wr.prefix == 24) { + uint8_t octets[4] = {dataPtr[1], dataPtr[2], dataPtr[3], 0}; + wr.ipAddr = IPv4Address(octets); + curByteCount += 3; + } else if (wr.prefix == 16) { + uint8_t octets[4] = {dataPtr[1], dataPtr[2], 0, 0}; + wr.ipAddr = IPv4Address(octets); + curByteCount += 2; + } else if (wr.prefix == 8) { + uint8_t octets[4] = {dataPtr[1], 0, 0, 0}; + wr.ipAddr = IPv4Address(octets); + curByteCount += 1; + } else { + PCPP_LOG_DEBUG("Illegal prefix value " << (int)wr.prefix); + break; // illegal value + } + + result.push_back(wr); + dataPtr += curByteCount; + byteCount += curByteCount; + } +} + +size_t BgpUpdateMessageLayer::prefixAndIPDataToByteArray( + const std::vector& prefixAndIpData, uint8_t* resultByteArr, + size_t maxByteArrSize) { + if (resultByteArr == nullptr || maxByteArrSize == 0) { + return 0; + } + + size_t dataLen = 0; + + for (std::vector::const_iterator iter = + prefixAndIpData.begin(); + iter != prefixAndIpData.end(); iter++) { + uint8_t curData[5]; + curData[0] = iter->prefix; + size_t curDataSize = 1; + const uint8_t* octets = iter->ipAddr.toBytes(); + if (iter->prefix == 32) { + curDataSize += 4; + curData[1] = octets[0]; + curData[2] = octets[1]; + curData[3] = octets[2]; + curData[4] = octets[3]; + } else if (iter->prefix == 24) { + curDataSize += 3; + curData[1] = octets[0]; + curData[2] = octets[1]; + curData[3] = octets[2]; + } else if (iter->prefix == 16) { + curDataSize += 2; + curData[1] = octets[0]; + curData[2] = octets[1]; + } else if (iter->prefix == 8) { + curDataSize += 1; + curData[1] = octets[0]; + } else { + PCPP_LOG_ERROR("Illegal prefix value " << (int)iter->prefix); + break; // illegal value + } + + if (dataLen + curDataSize > maxByteArrSize) { + break; + } + + dataLen += curDataSize; + + memcpy(resultByteArr, curData, curDataSize); + resultByteArr += curDataSize; + } + + return dataLen; +} + +size_t BgpUpdateMessageLayer::pathAttributesToByteArray( + const std::vector& pathAttributes, uint8_t* resultByteArr, + size_t maxByteArrSize) { + if (resultByteArr == nullptr || maxByteArrSize == 0) { + return 0; + } + + size_t dataLen = 0; + + for (std::vector::const_iterator iter = + pathAttributes.begin(); + iter != pathAttributes.end(); iter++) { + if (iter->length > 32) { + PCPP_LOG_ERROR("Illegal path attribute length " << (int)iter->length); + break; // illegal value + } + + size_t curDataSize = 3 * sizeof(uint8_t) + (size_t)iter->length; + + if (dataLen + curDataSize > maxByteArrSize) { + break; + } + + resultByteArr[0] = iter->flags; + resultByteArr[1] = iter->type; + resultByteArr[2] = iter->length; + if (iter->length > 0) { + memcpy(resultByteArr + 3 * sizeof(uint8_t), iter->data, iter->length); + } + + dataLen += curDataSize; + resultByteArr += curDataSize; + } + + return dataLen; +} + +size_t BgpUpdateMessageLayer::getWithdrawnRoutesLength() const { + size_t headerLen = getHeaderLen(); + size_t minLen = sizeof(bgp_common_header) + sizeof(uint16_t); + if (headerLen >= minLen) { + uint16_t res = be16toh(*(uint16_t*)(m_Data + sizeof(bgp_common_header))); + if ((size_t)res > headerLen - minLen) { + return headerLen - minLen; + } + + return (size_t)res; + } + + return 0; +} + +void BgpUpdateMessageLayer::getWithdrawnRoutes( + std::vector& withdrawnRoutes) { + size_t withdrawnRouteLen = getWithdrawnRoutesLength(); + if (withdrawnRouteLen == 0) { + return; + } + + uint8_t* dataPtr = m_Data + sizeof(bgp_common_header) + sizeof(uint16_t); + parsePrefixAndIPData(dataPtr, withdrawnRouteLen, withdrawnRoutes); +} + +size_t BgpUpdateMessageLayer::getPathAttributesLength() const { + size_t headerLen = getHeaderLen(); + size_t minLen = sizeof(bgp_common_header) + 2 * sizeof(uint16_t); + if (headerLen >= minLen) { + size_t withdrawnRouteLen = getWithdrawnRoutesLength(); + uint16_t res = be16toh(*(uint16_t*)(m_Data + sizeof(bgp_common_header) + + sizeof(uint16_t) + withdrawnRouteLen)); + if ((size_t)res > headerLen - minLen - withdrawnRouteLen) { + return headerLen - minLen - withdrawnRouteLen; + } + + return (size_t)res; + } + + return 0; +} + +bool BgpUpdateMessageLayer::setWithdrawnRoutes( + const std::vector& withdrawnRoutes) { + uint8_t newWithdrawnRoutesData[1500]; + size_t newWithdrawnRoutesDataLen = + prefixAndIPDataToByteArray(withdrawnRoutes, newWithdrawnRoutesData, 1500); + size_t curWithdrawnRoutesDataLen = getWithdrawnRoutesLength(); + + if (newWithdrawnRoutesDataLen > curWithdrawnRoutesDataLen) { + bool res = + extendLayer(sizeof(bgp_common_header) + sizeof(uint16_t), + newWithdrawnRoutesDataLen - curWithdrawnRoutesDataLen); + if (!res) { + PCPP_LOG_ERROR("Couldn't extend BGP update layer to include the " + "additional withdrawn routes"); + return res; + } + } else if (newWithdrawnRoutesDataLen < curWithdrawnRoutesDataLen) { + bool res = + shortenLayer(sizeof(bgp_common_header) + sizeof(uint16_t), + curWithdrawnRoutesDataLen - newWithdrawnRoutesDataLen); + if (!res) { + PCPP_LOG_ERROR("Couldn't shorten BGP update layer to set the right size " + "of the withdrawn routes data"); + return res; + } + } + + if (newWithdrawnRoutesDataLen > 0) { + memcpy(m_Data + sizeof(bgp_common_header) + sizeof(uint16_t), + newWithdrawnRoutesData, newWithdrawnRoutesDataLen); + } + + getBasicHeader()->length = + htobe16(be16toh(getBasicHeader()->length) + newWithdrawnRoutesDataLen - + curWithdrawnRoutesDataLen); + + uint16_t newWithdrawnRoutesDataLenBE = htobe16(newWithdrawnRoutesDataLen); + memcpy(m_Data + sizeof(bgp_common_header), &newWithdrawnRoutesDataLenBE, + sizeof(uint16_t)); + + return true; +} + +bool BgpUpdateMessageLayer::clearWithdrawnRoutes() { + return setWithdrawnRoutes(std::vector()); +} + +void BgpUpdateMessageLayer::getPathAttributes( + std::vector& pathAttributes) { + size_t pathAttrLen = getPathAttributesLength(); + if (pathAttrLen == 0) { + return; + } + + uint8_t* dataPtr = m_Data + sizeof(bgp_common_header) + 2 * sizeof(uint16_t) + + getWithdrawnRoutesLength(); + size_t byteCount = 0; + while (byteCount < pathAttrLen) { + path_attribute pa; + pa.flags = dataPtr[0]; + pa.type = dataPtr[1]; + pa.length = dataPtr[2]; + size_t curByteCount = 3 + pa.length; + if (pa.length > 0) { + size_t dataLenToCopy = (pa.length <= 32 ? pa.length : 32); + memcpy(pa.data, dataPtr + 3, dataLenToCopy); + } + + pathAttributes.push_back(pa); + dataPtr += curByteCount; + byteCount += curByteCount; + } +} + +bool BgpUpdateMessageLayer::setPathAttributes( + const std::vector& pathAttributes) { + uint8_t newPathAttributesData[1500]; + size_t newPathAttributesDataLen = + pathAttributesToByteArray(pathAttributes, newPathAttributesData, 1500); + size_t curPathAttributesDataLen = getPathAttributesLength(); + size_t curWithdrawnRoutesDataLen = getWithdrawnRoutesLength(); + + if (newPathAttributesDataLen > curPathAttributesDataLen) { + bool res = extendLayer(sizeof(bgp_common_header) + 2 * sizeof(uint16_t) + + curWithdrawnRoutesDataLen, + newPathAttributesDataLen - curPathAttributesDataLen); + if (!res) { + PCPP_LOG_ERROR("Couldn't extend BGP update layer to include the " + "additional path attributes"); + return res; + } + } else if (newPathAttributesDataLen < curPathAttributesDataLen) { + bool res = + shortenLayer(sizeof(bgp_common_header) + 2 * sizeof(uint16_t) + + curWithdrawnRoutesDataLen, + curPathAttributesDataLen - newPathAttributesDataLen); + if (!res) { + PCPP_LOG_ERROR("Couldn't shorten BGP update layer to set the right size " + "of the path attributes data"); + return res; + } + } + + if (newPathAttributesDataLen > 0) { + memcpy(m_Data + sizeof(bgp_common_header) + 2 * sizeof(uint16_t) + + curWithdrawnRoutesDataLen, + newPathAttributesData, newPathAttributesDataLen); + } + + getBasicHeader()->length = + htobe16(be16toh(getBasicHeader()->length) + newPathAttributesDataLen - + curPathAttributesDataLen); + + uint16_t newWithdrawnRoutesDataLenBE = htobe16(newPathAttributesDataLen); + memcpy(m_Data + sizeof(bgp_common_header) + sizeof(uint16_t) + + curWithdrawnRoutesDataLen, + &newWithdrawnRoutesDataLenBE, sizeof(uint16_t)); + + return true; +} + +bool BgpUpdateMessageLayer::clearPathAttributes() { + return setPathAttributes(std::vector()); +} + +size_t BgpUpdateMessageLayer::getNetworkLayerReachabilityInfoLength() const { + size_t headerLen = getHeaderLen(); + size_t minLen = sizeof(bgp_common_header) + 2 * sizeof(uint16_t); + if (headerLen >= minLen) { + size_t withdrawnRouteLen = getWithdrawnRoutesLength(); + size_t pathAttrLen = getPathAttributesLength(); + int nlriSize = headerLen - minLen - withdrawnRouteLen - pathAttrLen; + if (nlriSize >= 0) { + return (size_t)nlriSize; + } + + return 0; + } + + return 0; +} + +void BgpUpdateMessageLayer::getNetworkLayerReachabilityInfo( + std::vector& nlri) { + size_t nlriSize = getNetworkLayerReachabilityInfoLength(); + if (nlriSize == 0) { + return; + } + + uint8_t* dataPtr = m_Data + sizeof(bgp_common_header) + 2 * sizeof(uint16_t) + + getWithdrawnRoutesLength() + getPathAttributesLength(); + parsePrefixAndIPData(dataPtr, nlriSize, nlri); +} + +bool BgpUpdateMessageLayer::isDataValid(const uint8_t* data, size_t dataSize) { + if (dataSize < sizeof(bgp_common_header) + 2 * sizeof(uint16_t)) + return false; + + uint16_t withdrLen = be16toh(*(uint16_t*)(data + sizeof(bgp_common_header))); + if (dataSize < sizeof(bgp_common_header) + 2 * sizeof(uint16_t) + withdrLen) + return false; + + uint16_t attrLen = be16toh(*(uint16_t*)(data + sizeof(bgp_common_header) + + sizeof(uint16_t) + withdrLen)); + if (dataSize < + sizeof(bgp_common_header) + 2 * sizeof(uint16_t) + withdrLen + attrLen) + return false; + + return true; +} + +bool BgpUpdateMessageLayer::setNetworkLayerReachabilityInfo( + const std::vector& nlri) { + uint8_t newNlriData[1500]; + size_t newNlriDataLen = prefixAndIPDataToByteArray(nlri, newNlriData, 1500); + size_t curNlriDataLen = getNetworkLayerReachabilityInfoLength(); + size_t curPathAttributesDataLen = getPathAttributesLength(); + size_t curWithdrawnRoutesDataLen = getWithdrawnRoutesLength(); + + if (newNlriDataLen > curNlriDataLen) { + bool res = + extendLayer(sizeof(bgp_common_header) + 2 * sizeof(uint16_t) + + curWithdrawnRoutesDataLen + curPathAttributesDataLen, + newNlriDataLen - curNlriDataLen); + if (!res) { + PCPP_LOG_ERROR("Couldn't extend BGP update layer to include the " + "additional NLRI data"); + return res; + } + } else if (newNlriDataLen < curNlriDataLen) { + bool res = + shortenLayer(sizeof(bgp_common_header) + 2 * sizeof(uint16_t) + + curWithdrawnRoutesDataLen + curPathAttributesDataLen, + curNlriDataLen - newNlriDataLen); + if (!res) { + PCPP_LOG_ERROR("Couldn't shorten BGP update layer to set the right size " + "of the NLRI data"); + return res; + } + } + + if (newNlriDataLen > 0) { + memcpy(m_Data + sizeof(bgp_common_header) + 2 * sizeof(uint16_t) + + curWithdrawnRoutesDataLen + curPathAttributesDataLen, + newNlriData, newNlriDataLen); + } - getBasicHeader()->length = htobe16(be16toh(getBasicHeader()->length) + newNlriDataLen - curNlriDataLen); - - return true; + getBasicHeader()->length = htobe16(be16toh(getBasicHeader()->length) + + newNlriDataLen - curNlriDataLen); + + return true; } -bool BgpUpdateMessageLayer::clearNetworkLayerReachabilityInfo() -{ - return setNetworkLayerReachabilityInfo(std::vector()); +bool BgpUpdateMessageLayer::clearNetworkLayerReachabilityInfo() { + return setNetworkLayerReachabilityInfo(std::vector()); } - - - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~ // BgpNotificationMessageLayer // ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -BgpNotificationMessageLayer::BgpNotificationMessageLayer(uint8_t errorCode, uint8_t errorSubCode) -{ - initMessageData(errorCode, errorSubCode, nullptr, 0); -} - -BgpNotificationMessageLayer::BgpNotificationMessageLayer(uint8_t errorCode, uint8_t errorSubCode, const uint8_t* notificationData, size_t notificationDataLen) -{ - initMessageData(errorCode, errorSubCode, notificationData, notificationDataLen); -} - -BgpNotificationMessageLayer::BgpNotificationMessageLayer(uint8_t errorCode, uint8_t errorSubCode, const std::string& notificationData) -{ - uint8_t notificationDataByteArr[1500]; - size_t notificationDataLen = hexStringToByteArray(notificationData, notificationDataByteArr, 1500); - initMessageData(errorCode, errorSubCode, notificationDataByteArr, notificationDataLen); -} - -void BgpNotificationMessageLayer::initMessageData(uint8_t errorCode, uint8_t errorSubCode, const uint8_t* notificationData, size_t notificationDataLen) -{ - size_t headerLen = sizeof(bgp_notification_message); - if (notificationData != nullptr && notificationDataLen > 0) - { - headerLen += notificationDataLen; - } - m_DataLen = headerLen; - m_Data = new uint8_t[headerLen]; - memset(m_Data, 0, headerLen); - setBgpFields(headerLen); - bgp_notification_message* msgHdr = getNotificationMsgHeader(); - msgHdr->errorCode = errorCode; - msgHdr->errorSubCode = errorSubCode; - memcpy(m_Data + sizeof(bgp_notification_message), notificationData, notificationDataLen); - m_Protocol = BGP; -} - -size_t BgpNotificationMessageLayer::getNotificationDataLen() const -{ - size_t headerLen = getHeaderLen(); - if (headerLen > sizeof(bgp_notification_message)) - { - return headerLen - sizeof(bgp_notification_message); - } - - return 0; -} - -uint8_t* BgpNotificationMessageLayer::getNotificationData() const -{ - if (getNotificationDataLen() > 0) - { - return m_Data + sizeof(bgp_notification_message); - } - - return nullptr; -} - -std::string BgpNotificationMessageLayer::getNotificationDataAsHexString() const -{ - uint8_t* notificationData = getNotificationData(); - if (notificationData == nullptr) - { - return ""; - } - - return byteArrayToHexString(notificationData, getNotificationDataLen()); -} - -bool BgpNotificationMessageLayer::setNotificationData(const uint8_t* newNotificationData, size_t newNotificationDataLen) -{ - if (newNotificationData == nullptr) - { - newNotificationDataLen = 0; - } - - size_t curNotificationDataLen = getNotificationDataLen(); - - if (newNotificationDataLen > curNotificationDataLen) - { - bool res = extendLayer(sizeof(bgp_notification_message), newNotificationDataLen - curNotificationDataLen); - if (!res) - { - PCPP_LOG_ERROR("Couldn't extend BGP notification layer to include the additional notification data"); - return res; - } - } - else if (newNotificationDataLen < curNotificationDataLen) - { - bool res = shortenLayer(sizeof(bgp_notification_message), curNotificationDataLen - newNotificationDataLen); - if (!res) - { - PCPP_LOG_ERROR("Couldn't shorten BGP notification layer to set the right size of the notification data"); - return res; - } - } - - if (newNotificationDataLen > 0) - { - memcpy(m_Data + sizeof(bgp_notification_message), newNotificationData, newNotificationDataLen); - } - - getNotificationMsgHeader()->length = htobe16(sizeof(bgp_notification_message) + newNotificationDataLen); - - return true; +BgpNotificationMessageLayer::BgpNotificationMessageLayer(uint8_t errorCode, + uint8_t errorSubCode) { + initMessageData(errorCode, errorSubCode, nullptr, 0); +} + +BgpNotificationMessageLayer::BgpNotificationMessageLayer( + uint8_t errorCode, uint8_t errorSubCode, const uint8_t* notificationData, + size_t notificationDataLen) { + initMessageData(errorCode, errorSubCode, notificationData, + notificationDataLen); +} + +BgpNotificationMessageLayer::BgpNotificationMessageLayer( + uint8_t errorCode, uint8_t errorSubCode, + const std::string& notificationData) { + uint8_t notificationDataByteArr[1500]; + size_t notificationDataLen = + hexStringToByteArray(notificationData, notificationDataByteArr, 1500); + initMessageData(errorCode, errorSubCode, notificationDataByteArr, + notificationDataLen); +} + +void BgpNotificationMessageLayer::initMessageData( + uint8_t errorCode, uint8_t errorSubCode, const uint8_t* notificationData, + size_t notificationDataLen) { + size_t headerLen = sizeof(bgp_notification_message); + if (notificationData != nullptr && notificationDataLen > 0) { + headerLen += notificationDataLen; + } + m_DataLen = headerLen; + m_Data = new uint8_t[headerLen]; + memset(m_Data, 0, headerLen); + setBgpFields(headerLen); + bgp_notification_message* msgHdr = getNotificationMsgHeader(); + msgHdr->errorCode = errorCode; + msgHdr->errorSubCode = errorSubCode; + memcpy(m_Data + sizeof(bgp_notification_message), notificationData, + notificationDataLen); + m_Protocol = BGP; +} + +size_t BgpNotificationMessageLayer::getNotificationDataLen() const { + size_t headerLen = getHeaderLen(); + if (headerLen > sizeof(bgp_notification_message)) { + return headerLen - sizeof(bgp_notification_message); + } + + return 0; +} + +uint8_t* BgpNotificationMessageLayer::getNotificationData() const { + if (getNotificationDataLen() > 0) { + return m_Data + sizeof(bgp_notification_message); + } + + return nullptr; +} + +std::string +BgpNotificationMessageLayer::getNotificationDataAsHexString() const { + uint8_t* notificationData = getNotificationData(); + if (notificationData == nullptr) { + return ""; + } + + return byteArrayToHexString(notificationData, getNotificationDataLen()); +} + +bool BgpNotificationMessageLayer::setNotificationData( + const uint8_t* newNotificationData, size_t newNotificationDataLen) { + if (newNotificationData == nullptr) { + newNotificationDataLen = 0; + } + + size_t curNotificationDataLen = getNotificationDataLen(); + + if (newNotificationDataLen > curNotificationDataLen) { + bool res = extendLayer(sizeof(bgp_notification_message), + newNotificationDataLen - curNotificationDataLen); + if (!res) { + PCPP_LOG_ERROR("Couldn't extend BGP notification layer to include the " + "additional notification data"); + return res; + } + } else if (newNotificationDataLen < curNotificationDataLen) { + bool res = shortenLayer(sizeof(bgp_notification_message), + curNotificationDataLen - newNotificationDataLen); + if (!res) { + PCPP_LOG_ERROR("Couldn't shorten BGP notification layer to set the right " + "size of the notification data"); + return res; + } + } + + if (newNotificationDataLen > 0) { + memcpy(m_Data + sizeof(bgp_notification_message), newNotificationData, + newNotificationDataLen); + } + + getNotificationMsgHeader()->length = + htobe16(sizeof(bgp_notification_message) + newNotificationDataLen); + + return true; +} + +bool BgpNotificationMessageLayer::setNotificationData( + const std::string& newNotificationDataAsHexString) { + if (newNotificationDataAsHexString.empty()) { + return setNotificationData(nullptr, 0); + } + + uint8_t newNotificationData[1500]; + size_t newNotificationDataLen = hexStringToByteArray( + newNotificationDataAsHexString, newNotificationData, 1500); + + if (newNotificationDataLen == 0) { + PCPP_LOG_ERROR("newNotificationDataAsHexString is not a valid hex string"); + return false; + } + + return setNotificationData(newNotificationData, newNotificationDataLen); } -bool BgpNotificationMessageLayer::setNotificationData(const std::string& newNotificationDataAsHexString) -{ - if (newNotificationDataAsHexString.empty()) - { - return setNotificationData(nullptr, 0); - } - - uint8_t newNotificationData[1500]; - size_t newNotificationDataLen = hexStringToByteArray(newNotificationDataAsHexString, newNotificationData, 1500); - - if (newNotificationDataLen == 0) - { - PCPP_LOG_ERROR("newNotificationDataAsHexString is not a valid hex string"); - return false; - } - - return setNotificationData(newNotificationData, newNotificationDataLen); -} - - - // ~~~~~~~~~~~~~~~~~~~~~~~~ // BgpKeepaliveMessageLayer // ~~~~~~~~~~~~~~~~~~~~~~~~ -BgpKeepaliveMessageLayer::BgpKeepaliveMessageLayer() : BgpLayer() -{ - const size_t headerLen = sizeof(bgp_common_header); - m_DataLen = headerLen; - m_Data = new uint8_t[headerLen]; - memset(m_Data, 0, headerLen); - setBgpFields(headerLen); - m_Protocol = BGP; +BgpKeepaliveMessageLayer::BgpKeepaliveMessageLayer() : BgpLayer() { + const size_t headerLen = sizeof(bgp_common_header); + m_DataLen = headerLen; + m_Data = new uint8_t[headerLen]; + memset(m_Data, 0, headerLen); + setBgpFields(headerLen); + m_Protocol = BGP; } - - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~ // BgpRouteRefreshMessageLayer // ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -BgpRouteRefreshMessageLayer::BgpRouteRefreshMessageLayer(uint16_t afi, uint8_t safi) -{ - const size_t headerLen = sizeof(bgp_route_refresh_message); - m_DataLen = headerLen; - m_Data = new uint8_t[headerLen]; - memset(m_Data, 0, headerLen); - setBgpFields(headerLen); - bgp_route_refresh_message* msgHdr = getRouteRefreshHeader(); - msgHdr->afi = htobe16(afi); - msgHdr->safi = safi; - m_Protocol = BGP; +BgpRouteRefreshMessageLayer::BgpRouteRefreshMessageLayer(uint16_t afi, + uint8_t safi) { + const size_t headerLen = sizeof(bgp_route_refresh_message); + m_DataLen = headerLen; + m_Data = new uint8_t[headerLen]; + memset(m_Data, 0, headerLen); + setBgpFields(headerLen); + bgp_route_refresh_message* msgHdr = getRouteRefreshHeader(); + msgHdr->afi = htobe16(afi); + msgHdr->safi = safi; + m_Protocol = BGP; } -} +} // namespace pcpp diff --git a/Packet++/src/CotpLayer.cpp b/Packet++/src/CotpLayer.cpp index 353ebdfabf..1cf535cedb 100644 --- a/Packet++/src/CotpLayer.cpp +++ b/Packet++/src/CotpLayer.cpp @@ -6,56 +6,58 @@ #include #include -namespace pcpp -{ +namespace pcpp { - pcpp::CotpLayer::CotpLayer(uint8_t tpduNumber) - { - const size_t headerLen = sizeof(cotphdr); - m_DataLen = headerLen; - m_Data = new uint8_t[headerLen]; - memset(m_Data, 0, headerLen); - cotphdr *cotpHdr = (cotphdr *)m_Data; - cotpHdr->length = 0x02; - cotpHdr->pduType = 0x0f; - cotpHdr->tpduNumber = tpduNumber; - m_Protocol = COTP; - } +pcpp::CotpLayer::CotpLayer(uint8_t tpduNumber) { + const size_t headerLen = sizeof(cotphdr); + m_DataLen = headerLen; + m_Data = new uint8_t[headerLen]; + memset(m_Data, 0, headerLen); + cotphdr* cotpHdr = (cotphdr*)m_Data; + cotpHdr->length = 0x02; + cotpHdr->pduType = 0x0f; + cotpHdr->tpduNumber = tpduNumber; + m_Protocol = COTP; +} - std::string CotpLayer::toString() const { return "Cotp Layer"; } +std::string CotpLayer::toString() const { return "Cotp Layer"; } - uint8_t CotpLayer::getLength() const { return getCotpHeader()->length; } +uint8_t CotpLayer::getLength() const { return getCotpHeader()->length; } - uint8_t CotpLayer::getPduType() const { return getCotpHeader()->pduType; } +uint8_t CotpLayer::getPduType() const { return getCotpHeader()->pduType; } - uint8_t CotpLayer::getTpduNumber() const { return getCotpHeader()->tpduNumber; } +uint8_t CotpLayer::getTpduNumber() const { return getCotpHeader()->tpduNumber; } - void CotpLayer::setLength(uint8_t length) const { getCotpHeader()->length = length; } +void CotpLayer::setLength(uint8_t length) const { + getCotpHeader()->length = length; +} - void CotpLayer::setPduType(uint8_t pduType) const { getCotpHeader()->pduType = pduType; } +void CotpLayer::setPduType(uint8_t pduType) const { + getCotpHeader()->pduType = pduType; +} - void CotpLayer::setTpduNumber(uint8_t tpduNumber) const { getCotpHeader()->tpduNumber = tpduNumber; } +void CotpLayer::setTpduNumber(uint8_t tpduNumber) const { + getCotpHeader()->tpduNumber = tpduNumber; +} - bool CotpLayer::isDataValid(const uint8_t *data, size_t dataSize) - { - if (!data || dataSize < sizeof(cotphdr)) - return false; +bool CotpLayer::isDataValid(const uint8_t* data, size_t dataSize) { + if (!data || dataSize < sizeof(cotphdr)) + return false; - return data[1] == 0xf0 && data[0] == 2; - } + return data[1] == 0xf0 && data[0] == 2; +} - void CotpLayer::parseNextLayer() - { - size_t headerLen = getHeaderLen(); - if (m_DataLen <= headerLen) - return; +void CotpLayer::parseNextLayer() { + size_t headerLen = getHeaderLen(); + if (m_DataLen <= headerLen) + return; - uint8_t *payload = m_Data + headerLen; - size_t payloadLen = m_DataLen - headerLen; + uint8_t* payload = m_Data + headerLen; + size_t payloadLen = m_DataLen - headerLen; - if (S7CommLayer::isDataValid(payload, payloadLen)) - m_NextLayer = new S7CommLayer(payload, payloadLen, this, m_Packet); - else - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - } + if (S7CommLayer::isDataValid(payload, payloadLen)) + m_NextLayer = new S7CommLayer(payload, payloadLen, this, m_Packet); + else + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); +} } // namespace pcpp diff --git a/Packet++/src/DhcpLayer.cpp b/Packet++/src/DhcpLayer.cpp index c75c370a08..6d0ffd472a 100644 --- a/Packet++/src/DhcpLayer.cpp +++ b/Packet++/src/DhcpLayer.cpp @@ -3,312 +3,281 @@ #include "DhcpLayer.h" #include "Logger.h" -namespace pcpp -{ +namespace pcpp { #define DHCP_MAGIC_NUMBER 0x63538263 - -DhcpOption DhcpOptionBuilder::build() const -{ - size_t recSize = 2 * sizeof(uint8_t) + m_RecValueLen; - uint8_t recType = static_cast(m_RecType); - - if ((recType == DHCPOPT_END || recType == DHCPOPT_PAD)) - { - if (m_RecValueLen != 0) - { - PCPP_LOG_ERROR("Can't set DHCP END option or DHCP PAD option with size different than 0, tried to set size " << (int)m_RecValueLen); - return DhcpOption(nullptr); - } - - recSize = sizeof(uint8_t); - } - - uint8_t* recordBuffer = new uint8_t[recSize]; - memset(recordBuffer, 0, recSize); - recordBuffer[0] = recType; - if (recSize > 1) - { - recordBuffer[1] = static_cast(m_RecValueLen); - if (m_RecValue != nullptr) - memcpy(recordBuffer+2, m_RecValue, m_RecValueLen); - else - memset(recordBuffer+2, 0, m_RecValueLen); - } - - return DhcpOption(recordBuffer); +DhcpOption DhcpOptionBuilder::build() const { + size_t recSize = 2 * sizeof(uint8_t) + m_RecValueLen; + uint8_t recType = static_cast(m_RecType); + + if ((recType == DHCPOPT_END || recType == DHCPOPT_PAD)) { + if (m_RecValueLen != 0) { + PCPP_LOG_ERROR("Can't set DHCP END option or DHCP PAD option with size " + "different than 0, tried to set size " + << (int)m_RecValueLen); + return DhcpOption(nullptr); + } + + recSize = sizeof(uint8_t); + } + + uint8_t* recordBuffer = new uint8_t[recSize]; + memset(recordBuffer, 0, recSize); + recordBuffer[0] = recType; + if (recSize > 1) { + recordBuffer[1] = static_cast(m_RecValueLen); + if (m_RecValue != nullptr) + memcpy(recordBuffer + 2, m_RecValue, m_RecValueLen); + else + memset(recordBuffer + 2, 0, m_RecValueLen); + } + + return DhcpOption(recordBuffer); } -DhcpLayer::DhcpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) -{ - m_Protocol = DHCP; +DhcpLayer::DhcpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet) + : Layer(data, dataLen, prevLayer, packet) { + m_Protocol = DHCP; } -void DhcpLayer::initDhcpLayer(size_t numOfBytesToAllocate) -{ - m_DataLen = numOfBytesToAllocate; - m_Data = new uint8_t[numOfBytesToAllocate]; - memset(m_Data, 0, numOfBytesToAllocate); - m_Protocol = DHCP; +void DhcpLayer::initDhcpLayer(size_t numOfBytesToAllocate) { + m_DataLen = numOfBytesToAllocate; + m_Data = new uint8_t[numOfBytesToAllocate]; + memset(m_Data, 0, numOfBytesToAllocate); + m_Protocol = DHCP; } -DhcpLayer::DhcpLayer() : Layer() -{ - initDhcpLayer(sizeof(dhcp_header)); -} +DhcpLayer::DhcpLayer() : Layer() { initDhcpLayer(sizeof(dhcp_header)); } -DhcpLayer::DhcpLayer(DhcpMessageType msgType, const MacAddress& clientMacAddr) : Layer() -{ - initDhcpLayer(sizeof(dhcp_header) + 4*sizeof(uint8_t)); +DhcpLayer::DhcpLayer(DhcpMessageType msgType, const MacAddress& clientMacAddr) + : Layer() { + initDhcpLayer(sizeof(dhcp_header) + 4 * sizeof(uint8_t)); - setClientHardwareAddress(clientMacAddr); + setClientHardwareAddress(clientMacAddr); - uint8_t* msgTypeOptionPtr = m_Data + sizeof(dhcp_header); - msgTypeOptionPtr[0] = (uint8_t)DHCPOPT_DHCP_MESSAGE_TYPE; // option code - msgTypeOptionPtr[1] = 1; // option len - msgTypeOptionPtr[2] = (uint8_t)msgType; // option data - message type + uint8_t* msgTypeOptionPtr = m_Data + sizeof(dhcp_header); + msgTypeOptionPtr[0] = (uint8_t)DHCPOPT_DHCP_MESSAGE_TYPE; // option code + msgTypeOptionPtr[1] = 1; // option len + msgTypeOptionPtr[2] = (uint8_t)msgType; // option data - message type - msgTypeOptionPtr[3] = (uint8_t)DHCPOPT_END; + msgTypeOptionPtr[3] = (uint8_t)DHCPOPT_END; } -MacAddress DhcpLayer::getClientHardwareAddress() const -{ - dhcp_header* hdr = getDhcpHeader(); - if (hdr != nullptr && hdr->hardwareType == 1 && hdr->hardwareAddressLength == 6) - return MacAddress(hdr->clientHardwareAddress); +MacAddress DhcpLayer::getClientHardwareAddress() const { + dhcp_header* hdr = getDhcpHeader(); + if (hdr != nullptr && hdr->hardwareType == 1 && + hdr->hardwareAddressLength == 6) + return MacAddress(hdr->clientHardwareAddress); - PCPP_LOG_DEBUG("Hardware type isn't Ethernet or hardware addr len != 6, returning MacAddress:Zero"); + PCPP_LOG_DEBUG("Hardware type isn't Ethernet or hardware addr len != 6, " + "returning MacAddress:Zero"); - return MacAddress::Zero; + return MacAddress::Zero; } -void DhcpLayer::setClientHardwareAddress(const MacAddress& addr) -{ - dhcp_header* hdr = getDhcpHeader(); - hdr->hardwareType = 1; // Ethernet - hdr->hardwareAddressLength = 6; // MAC address length - addr.copyTo(hdr->clientHardwareAddress); +void DhcpLayer::setClientHardwareAddress(const MacAddress& addr) { + dhcp_header* hdr = getDhcpHeader(); + hdr->hardwareType = 1; // Ethernet + hdr->hardwareAddressLength = 6; // MAC address length + addr.copyTo(hdr->clientHardwareAddress); } -void DhcpLayer::computeCalculateFields() -{ - dhcp_header* hdr = getDhcpHeader(); - - hdr->magicNumber = DHCP_MAGIC_NUMBER; - - DhcpMessageType msgType = getMessageType(); - switch(msgType) - { - case DHCP_DISCOVER: - case DHCP_REQUEST: - case DHCP_DECLINE: - case DHCP_RELEASE: - case DHCP_INFORM: - case DHCP_UNKNOWN_MSG_TYPE: - hdr->opCode = DHCP_BOOTREQUEST; - break; - case DHCP_OFFER: - case DHCP_ACK: - case DHCP_NAK: - hdr->opCode = DHCP_BOOTREPLY; - break; - default: - break; - } - - hdr->hardwareType = 1; //Ethernet - hdr->hardwareAddressLength = 6; // MAC address length +void DhcpLayer::computeCalculateFields() { + dhcp_header* hdr = getDhcpHeader(); + + hdr->magicNumber = DHCP_MAGIC_NUMBER; + + DhcpMessageType msgType = getMessageType(); + switch (msgType) { + case DHCP_DISCOVER: + case DHCP_REQUEST: + case DHCP_DECLINE: + case DHCP_RELEASE: + case DHCP_INFORM: + case DHCP_UNKNOWN_MSG_TYPE: + hdr->opCode = DHCP_BOOTREQUEST; + break; + case DHCP_OFFER: + case DHCP_ACK: + case DHCP_NAK: + hdr->opCode = DHCP_BOOTREPLY; + break; + default: + break; + } + + hdr->hardwareType = 1; // Ethernet + hdr->hardwareAddressLength = 6; // MAC address length } -std::string DhcpLayer::toString() const -{ - std::string msgType = "Unknown"; - switch (getMessageType()) - { - case DHCP_DISCOVER: - { - msgType = "Discover"; - break; - } - case DHCP_OFFER: - { - msgType = "Offer"; - break; - } - case DHCP_REQUEST: - { - msgType = "Request"; - break; - } - case DHCP_DECLINE: - { - msgType = "Decline"; - break; - } - case DHCP_ACK: - { - msgType = "Acknowledge"; - break; - } - case DHCP_NAK: - { - msgType = "Negative Acknowledge"; - break; - } - case DHCP_RELEASE: - { - msgType = "Release"; - break; - } - case DHCP_INFORM: - { - msgType = "Inform"; - break; - } - default: - break; - - } - - return "DHCP layer (" + msgType + ")"; +std::string DhcpLayer::toString() const { + std::string msgType = "Unknown"; + switch (getMessageType()) { + case DHCP_DISCOVER: { + msgType = "Discover"; + break; + } + case DHCP_OFFER: { + msgType = "Offer"; + break; + } + case DHCP_REQUEST: { + msgType = "Request"; + break; + } + case DHCP_DECLINE: { + msgType = "Decline"; + break; + } + case DHCP_ACK: { + msgType = "Acknowledge"; + break; + } + case DHCP_NAK: { + msgType = "Negative Acknowledge"; + break; + } + case DHCP_RELEASE: { + msgType = "Release"; + break; + } + case DHCP_INFORM: { + msgType = "Inform"; + break; + } + default: + break; + } + + return "DHCP layer (" + msgType + ")"; } -DhcpMessageType DhcpLayer::getMessageType() const -{ - DhcpOption opt = getOptionData(DHCPOPT_DHCP_MESSAGE_TYPE); - if (opt.isNull()) - return DHCP_UNKNOWN_MSG_TYPE; +DhcpMessageType DhcpLayer::getMessageType() const { + DhcpOption opt = getOptionData(DHCPOPT_DHCP_MESSAGE_TYPE); + if (opt.isNull()) + return DHCP_UNKNOWN_MSG_TYPE; - return (DhcpMessageType)opt.getValueAs(); + return (DhcpMessageType)opt.getValueAs(); } -bool DhcpLayer::setMessageType(DhcpMessageType msgType) -{ - if (msgType == DHCP_UNKNOWN_MSG_TYPE) - return false; - - DhcpOption opt = getOptionData(DHCPOPT_DHCP_MESSAGE_TYPE); - if (opt.isNull()) - { - opt = addOptionAfter(DhcpOptionBuilder(DHCPOPT_DHCP_MESSAGE_TYPE, (uint8_t)msgType), DHCPOPT_UNKNOWN); - if (opt.isNull()) - return false; - } - - opt.setValue((uint8_t)msgType); - return true; +bool DhcpLayer::setMessageType(DhcpMessageType msgType) { + if (msgType == DHCP_UNKNOWN_MSG_TYPE) + return false; + + DhcpOption opt = getOptionData(DHCPOPT_DHCP_MESSAGE_TYPE); + if (opt.isNull()) { + opt = addOptionAfter( + DhcpOptionBuilder(DHCPOPT_DHCP_MESSAGE_TYPE, (uint8_t)msgType), + DHCPOPT_UNKNOWN); + if (opt.isNull()) + return false; + } + + opt.setValue((uint8_t)msgType); + return true; } -DhcpOption DhcpLayer::getOptionData(DhcpOptionTypes option) const -{ - return m_OptionReader.getTLVRecord((uint8_t)option, getOptionsBasePtr(), getHeaderLen() - sizeof(dhcp_header)); +DhcpOption DhcpLayer::getOptionData(DhcpOptionTypes option) const { + return m_OptionReader.getTLVRecord((uint8_t)option, getOptionsBasePtr(), + getHeaderLen() - sizeof(dhcp_header)); } -DhcpOption DhcpLayer::getFirstOptionData() const -{ - return m_OptionReader.getFirstTLVRecord(getOptionsBasePtr(), getHeaderLen() - sizeof(dhcp_header)); +DhcpOption DhcpLayer::getFirstOptionData() const { + return m_OptionReader.getFirstTLVRecord(getOptionsBasePtr(), + getHeaderLen() - sizeof(dhcp_header)); } -DhcpOption DhcpLayer::getNextOptionData(DhcpOption dhcpOption) const -{ - return m_OptionReader.getNextTLVRecord(dhcpOption, getOptionsBasePtr(), getHeaderLen() - sizeof(dhcp_header)); +DhcpOption DhcpLayer::getNextOptionData(DhcpOption dhcpOption) const { + return m_OptionReader.getNextTLVRecord(dhcpOption, getOptionsBasePtr(), + getHeaderLen() - sizeof(dhcp_header)); } -size_t DhcpLayer::getOptionsCount() const -{ - return m_OptionReader.getTLVRecordCount(getOptionsBasePtr(), getHeaderLen() - sizeof(dhcp_header)); +size_t DhcpLayer::getOptionsCount() const { + return m_OptionReader.getTLVRecordCount(getOptionsBasePtr(), + getHeaderLen() - sizeof(dhcp_header)); } -DhcpOption DhcpLayer::addOptionAt(const DhcpOptionBuilder& optionBuilder, int offset) -{ - DhcpOption newOpt = optionBuilder.build(); +DhcpOption DhcpLayer::addOptionAt(const DhcpOptionBuilder& optionBuilder, + int offset) { + DhcpOption newOpt = optionBuilder.build(); - if (newOpt.isNull()) - { - PCPP_LOG_ERROR("Cannot build new option of type " << (int)newOpt.getType()); - return DhcpOption(nullptr); - } + if (newOpt.isNull()) { + PCPP_LOG_ERROR("Cannot build new option of type " << (int)newOpt.getType()); + return DhcpOption(nullptr); + } - size_t sizeToExtend = newOpt.getTotalSize(); + size_t sizeToExtend = newOpt.getTotalSize(); - if (!extendLayer(offset, sizeToExtend)) - { - PCPP_LOG_ERROR("Could not extend DhcpLayer in [" << newOpt.getTotalSize() << "] bytes"); - newOpt.purgeRecordData(); - return DhcpOption(nullptr); - } + if (!extendLayer(offset, sizeToExtend)) { + PCPP_LOG_ERROR("Could not extend DhcpLayer in [" << newOpt.getTotalSize() + << "] bytes"); + newOpt.purgeRecordData(); + return DhcpOption(nullptr); + } - memcpy(m_Data + offset, newOpt.getRecordBasePtr(), newOpt.getTotalSize()); + memcpy(m_Data + offset, newOpt.getRecordBasePtr(), newOpt.getTotalSize()); - uint8_t* newOptPtr = m_Data + offset; + uint8_t* newOptPtr = m_Data + offset; - m_OptionReader.changeTLVRecordCount(1); + m_OptionReader.changeTLVRecordCount(1); - newOpt.purgeRecordData(); + newOpt.purgeRecordData(); - return DhcpOption(newOptPtr); + return DhcpOption(newOptPtr); } -DhcpOption DhcpLayer::addOption(const DhcpOptionBuilder& optionBuilder) -{ - int offset = 0; - DhcpOption endOpt = getOptionData(DHCPOPT_END); - if (!endOpt.isNull()) - offset = endOpt.getRecordBasePtr() - m_Data; - else - offset = getHeaderLen(); +DhcpOption DhcpLayer::addOption(const DhcpOptionBuilder& optionBuilder) { + int offset = 0; + DhcpOption endOpt = getOptionData(DHCPOPT_END); + if (!endOpt.isNull()) + offset = endOpt.getRecordBasePtr() - m_Data; + else + offset = getHeaderLen(); - return addOptionAt(optionBuilder, offset); + return addOptionAt(optionBuilder, offset); } -DhcpOption DhcpLayer::addOptionAfter(const DhcpOptionBuilder& optionBuilder, DhcpOptionTypes prevOption) -{ - int offset = 0; +DhcpOption DhcpLayer::addOptionAfter(const DhcpOptionBuilder& optionBuilder, + DhcpOptionTypes prevOption) { + int offset = 0; - DhcpOption prevOpt = getOptionData(prevOption); + DhcpOption prevOpt = getOptionData(prevOption); - if (prevOpt.isNull()) - { - offset = sizeof(dhcp_header); - } - else - { - offset = prevOpt.getRecordBasePtr() + prevOpt.getTotalSize() - m_Data; - } + if (prevOpt.isNull()) { + offset = sizeof(dhcp_header); + } else { + offset = prevOpt.getRecordBasePtr() + prevOpt.getTotalSize() - m_Data; + } - return addOptionAt(optionBuilder, offset); + return addOptionAt(optionBuilder, offset); } -bool DhcpLayer::removeOption(DhcpOptionTypes optionType) -{ - DhcpOption optToRemove = getOptionData(optionType); - if (optToRemove.isNull()) - { - return false; - } +bool DhcpLayer::removeOption(DhcpOptionTypes optionType) { + DhcpOption optToRemove = getOptionData(optionType); + if (optToRemove.isNull()) { + return false; + } - int offset = optToRemove.getRecordBasePtr() - m_Data; + int offset = optToRemove.getRecordBasePtr() - m_Data; - if (!shortenLayer(offset, optToRemove.getTotalSize())) - { - return false; - } + if (!shortenLayer(offset, optToRemove.getTotalSize())) { + return false; + } - m_OptionReader.changeTLVRecordCount(-1); - return true; + m_OptionReader.changeTLVRecordCount(-1); + return true; } -bool DhcpLayer::removeAllOptions() -{ - int offset = sizeof(dhcp_header); +bool DhcpLayer::removeAllOptions() { + int offset = sizeof(dhcp_header); - if (!shortenLayer(offset, getHeaderLen()-offset)) - return false; + if (!shortenLayer(offset, getHeaderLen() - offset)) + return false; - m_OptionReader.changeTLVRecordCount(0-getOptionsCount()); - return true; + m_OptionReader.changeTLVRecordCount(0 - getOptionsCount()); + return true; } - -} +} // namespace pcpp diff --git a/Packet++/src/DhcpV6Layer.cpp b/Packet++/src/DhcpV6Layer.cpp index 2d7eac7a5f..f4d829ab3f 100644 --- a/Packet++/src/DhcpV6Layer.cpp +++ b/Packet++/src/DhcpV6Layer.cpp @@ -1,274 +1,257 @@ #define LOG_MODULE PacketLogModuleDhcpV6Layer #include "DhcpV6Layer.h" -#include "Logger.h" -#include "GeneralUtils.h" #include "EndianPortable.h" +#include "GeneralUtils.h" +#include "Logger.h" +namespace pcpp { -namespace pcpp -{ - -DhcpV6OptionType DhcpV6Option::getType() const -{ - if (m_Data == nullptr) - return DhcpV6OptionType::DHCPV6_OPT_UNKNOWN; +DhcpV6OptionType DhcpV6Option::getType() const { + if (m_Data == nullptr) + return DhcpV6OptionType::DHCPV6_OPT_UNKNOWN; - uint16_t optionType = be16toh(m_Data->recordType); - if (optionType <= 62 && optionType != 10 && optionType != 35 && optionType != 57 && optionType != 58) - { - return static_cast(optionType); - } - if (optionType == 65 || optionType == 66 || optionType == 68 || optionType == 79 || optionType == 112) - { - return static_cast(optionType); - } + uint16_t optionType = be16toh(m_Data->recordType); + if (optionType <= 62 && optionType != 10 && optionType != 35 && + optionType != 57 && optionType != 58) { + return static_cast(optionType); + } + if (optionType == 65 || optionType == 66 || optionType == 68 || + optionType == 79 || optionType == 112) { + return static_cast(optionType); + } - return DHCPV6_OPT_UNKNOWN; + return DHCPV6_OPT_UNKNOWN; } -std::string DhcpV6Option::getValueAsHexString() const -{ - if (m_Data == nullptr) - return ""; +std::string DhcpV6Option::getValueAsHexString() const { + if (m_Data == nullptr) + return ""; - return byteArrayToHexString(m_Data->recordValue, getDataSize()); + return byteArrayToHexString(m_Data->recordValue, getDataSize()); } -size_t DhcpV6Option::getTotalSize() const -{ - if (m_Data == nullptr) - return 0; +size_t DhcpV6Option::getTotalSize() const { + if (m_Data == nullptr) + return 0; - return 2*sizeof(uint16_t) + be16toh(m_Data->recordLen); + return 2 * sizeof(uint16_t) + be16toh(m_Data->recordLen); } -size_t DhcpV6Option::getDataSize() const -{ - if (m_Data == nullptr) - return 0; +size_t DhcpV6Option::getDataSize() const { + if (m_Data == nullptr) + return 0; - return static_cast(be16toh(m_Data->recordLen)); + return static_cast(be16toh(m_Data->recordLen)); } -DhcpV6Option DhcpV6OptionBuilder::build() const -{ - if (m_RecType == 0) - return DhcpV6Option(nullptr); - - size_t optionSize = 2 * sizeof(uint16_t) + m_RecValueLen; - uint8_t* recordBuffer = new uint8_t[optionSize]; - uint16_t optionTypeVal = htobe16(static_cast(m_RecType)); - uint16_t optionLength = htobe16(static_cast(m_RecValueLen)); - memcpy(recordBuffer, &optionTypeVal, sizeof(uint16_t)); - memcpy(recordBuffer + sizeof(uint16_t), &optionLength, sizeof(uint16_t)); - if (optionSize > 0 && m_RecValue != nullptr) - memcpy(recordBuffer + 2*sizeof(uint16_t), m_RecValue, m_RecValueLen); - - return DhcpV6Option(recordBuffer); +DhcpV6Option DhcpV6OptionBuilder::build() const { + if (m_RecType == 0) + return DhcpV6Option(nullptr); + + size_t optionSize = 2 * sizeof(uint16_t) + m_RecValueLen; + uint8_t* recordBuffer = new uint8_t[optionSize]; + uint16_t optionTypeVal = htobe16(static_cast(m_RecType)); + uint16_t optionLength = htobe16(static_cast(m_RecValueLen)); + memcpy(recordBuffer, &optionTypeVal, sizeof(uint16_t)); + memcpy(recordBuffer + sizeof(uint16_t), &optionLength, sizeof(uint16_t)); + if (optionSize > 0 && m_RecValue != nullptr) + memcpy(recordBuffer + 2 * sizeof(uint16_t), m_RecValue, m_RecValueLen); + + return DhcpV6Option(recordBuffer); } -DhcpV6Layer::DhcpV6Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) -{ - m_Protocol = DHCPv6; +DhcpV6Layer::DhcpV6Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet) + : Layer(data, dataLen, prevLayer, packet) { + m_Protocol = DHCPv6; } -DhcpV6Layer::DhcpV6Layer(DhcpV6MessageType messageType, uint32_t transactionId) -{ - m_DataLen = sizeof(dhcpv6_header); - m_Data = new uint8_t[m_DataLen]; - memset(m_Data, 0, m_DataLen); - m_Protocol = DHCPv6; +DhcpV6Layer::DhcpV6Layer(DhcpV6MessageType messageType, + uint32_t transactionId) { + m_DataLen = sizeof(dhcpv6_header); + m_Data = new uint8_t[m_DataLen]; + memset(m_Data, 0, m_DataLen); + m_Protocol = DHCPv6; - setMessageType(messageType); - setTransactionID(transactionId); + setMessageType(messageType); + setTransactionID(transactionId); } -DhcpV6MessageType DhcpV6Layer::getMessageType() const -{ - uint8_t messageType = getDhcpHeader()->messageType; - if (messageType > 13) - { - return DHCPV6_UNKNOWN_MSG_TYPE; - } +DhcpV6MessageType DhcpV6Layer::getMessageType() const { + uint8_t messageType = getDhcpHeader()->messageType; + if (messageType > 13) { + return DHCPV6_UNKNOWN_MSG_TYPE; + } - return static_cast(messageType); + return static_cast(messageType); } -std::string DhcpV6Layer::getMessageTypeAsString() const -{ - DhcpV6MessageType messageType = getMessageType(); - switch (messageType) - { - case DHCPV6_SOLICIT: - return "Solicit"; - case DHCPV6_ADVERTISE: - return "Advertise"; - case DHCPV6_REQUEST: - return "Request"; - case DHCPV6_CONFIRM: - return "Confirm"; - case DHCPV6_RENEW: - return "Renew"; - case DHCPV6_REBIND: - return "Rebind"; - case DHCPV6_REPLY: - return "Reply"; - case DHCPV6_RELEASE: - return "Release"; - case DHCPV6_DECLINE: - return "Decline"; - case DHCPV6_RECONFIGURE: - return "Reconfigure"; - case DHCPV6_INFORMATION_REQUEST: - return "Information-Request"; - case DHCPV6_RELAY_FORWARD: - return "Relay-Forward"; - case DHCPV6_RELAY_REPLY: - return "Relay-Reply"; - default: - return "Unknown"; - } +std::string DhcpV6Layer::getMessageTypeAsString() const { + DhcpV6MessageType messageType = getMessageType(); + switch (messageType) { + case DHCPV6_SOLICIT: + return "Solicit"; + case DHCPV6_ADVERTISE: + return "Advertise"; + case DHCPV6_REQUEST: + return "Request"; + case DHCPV6_CONFIRM: + return "Confirm"; + case DHCPV6_RENEW: + return "Renew"; + case DHCPV6_REBIND: + return "Rebind"; + case DHCPV6_REPLY: + return "Reply"; + case DHCPV6_RELEASE: + return "Release"; + case DHCPV6_DECLINE: + return "Decline"; + case DHCPV6_RECONFIGURE: + return "Reconfigure"; + case DHCPV6_INFORMATION_REQUEST: + return "Information-Request"; + case DHCPV6_RELAY_FORWARD: + return "Relay-Forward"; + case DHCPV6_RELAY_REPLY: + return "Relay-Reply"; + default: + return "Unknown"; + } } -void DhcpV6Layer::setMessageType(DhcpV6MessageType messageType) -{ - getDhcpHeader()->messageType = static_cast(messageType); +void DhcpV6Layer::setMessageType(DhcpV6MessageType messageType) { + getDhcpHeader()->messageType = static_cast(messageType); } -uint32_t DhcpV6Layer::getTransactionID() const -{ - dhcpv6_header* hdr = getDhcpHeader(); - uint32_t result = hdr->transactionId1 << 16 | hdr->transactionId2 << 8 | hdr->transactionId3; - return result; +uint32_t DhcpV6Layer::getTransactionID() const { + dhcpv6_header* hdr = getDhcpHeader(); + uint32_t result = hdr->transactionId1 << 16 | hdr->transactionId2 << 8 | + hdr->transactionId3; + return result; } -void DhcpV6Layer::setTransactionID(uint32_t transactionId) const -{ - dhcpv6_header* hdr = getDhcpHeader(); - hdr->transactionId1 = (transactionId >> 16) & 0xff; - hdr->transactionId2 = (transactionId >> 8) & 0xff; - hdr->transactionId3 = transactionId & 0xff; +void DhcpV6Layer::setTransactionID(uint32_t transactionId) const { + dhcpv6_header* hdr = getDhcpHeader(); + hdr->transactionId1 = (transactionId >> 16) & 0xff; + hdr->transactionId2 = (transactionId >> 8) & 0xff; + hdr->transactionId3 = transactionId & 0xff; } -DhcpV6Option DhcpV6Layer::getFirstOptionData() const -{ - return m_OptionReader.getFirstTLVRecord(getOptionsBasePtr(), getHeaderLen() - sizeof(dhcpv6_header)); +DhcpV6Option DhcpV6Layer::getFirstOptionData() const { + return m_OptionReader.getFirstTLVRecord( + getOptionsBasePtr(), getHeaderLen() - sizeof(dhcpv6_header)); } -DhcpV6Option DhcpV6Layer::getNextOptionData(DhcpV6Option dhcpv6Option) const -{ - return m_OptionReader.getNextTLVRecord(dhcpv6Option, getOptionsBasePtr(), getHeaderLen() - sizeof(dhcpv6_header)); +DhcpV6Option DhcpV6Layer::getNextOptionData(DhcpV6Option dhcpv6Option) const { + return m_OptionReader.getNextTLVRecord(dhcpv6Option, getOptionsBasePtr(), + getHeaderLen() - + sizeof(dhcpv6_header)); } -DhcpV6Option DhcpV6Layer::getOptionData(DhcpV6OptionType option) const -{ - return m_OptionReader.getTLVRecord(static_cast(option), getOptionsBasePtr(), getHeaderLen() - sizeof(dhcpv6_header)); +DhcpV6Option DhcpV6Layer::getOptionData(DhcpV6OptionType option) const { + return m_OptionReader.getTLVRecord(static_cast(option), + getOptionsBasePtr(), + getHeaderLen() - sizeof(dhcpv6_header)); } -size_t DhcpV6Layer::getOptionCount() const -{ - return m_OptionReader.getTLVRecordCount(getOptionsBasePtr(), getHeaderLen() - sizeof(dhcpv6_header)); +size_t DhcpV6Layer::getOptionCount() const { + return m_OptionReader.getTLVRecordCount( + getOptionsBasePtr(), getHeaderLen() - sizeof(dhcpv6_header)); } -DhcpV6Option DhcpV6Layer::addOptionAt(const DhcpV6OptionBuilder& optionBuilder, int offset) -{ - DhcpV6Option newOpt = optionBuilder.build(); - if (newOpt.isNull()) - { - PCPP_LOG_ERROR("Cannot build new option"); - return DhcpV6Option(nullptr); - } +DhcpV6Option DhcpV6Layer::addOptionAt(const DhcpV6OptionBuilder& optionBuilder, + int offset) { + DhcpV6Option newOpt = optionBuilder.build(); + if (newOpt.isNull()) { + PCPP_LOG_ERROR("Cannot build new option"); + return DhcpV6Option(nullptr); + } - size_t sizeToExtend = newOpt.getTotalSize(); + size_t sizeToExtend = newOpt.getTotalSize(); - if (!extendLayer(offset, sizeToExtend)) - { - PCPP_LOG_ERROR("Could not extend DhcpLayer in [" << newOpt.getTotalSize() << "] bytes"); - newOpt.purgeRecordData(); - return DhcpV6Option(nullptr); - } + if (!extendLayer(offset, sizeToExtend)) { + PCPP_LOG_ERROR("Could not extend DhcpLayer in [" << newOpt.getTotalSize() + << "] bytes"); + newOpt.purgeRecordData(); + return DhcpV6Option(nullptr); + } - memcpy(m_Data + offset, newOpt.getRecordBasePtr(), newOpt.getTotalSize()); + memcpy(m_Data + offset, newOpt.getRecordBasePtr(), newOpt.getTotalSize()); - uint8_t* newOptPtr = m_Data + offset; + uint8_t* newOptPtr = m_Data + offset; - m_OptionReader.changeTLVRecordCount(1); + m_OptionReader.changeTLVRecordCount(1); - newOpt.purgeRecordData(); + newOpt.purgeRecordData(); - return DhcpV6Option(newOptPtr); + return DhcpV6Option(newOptPtr); } -DhcpV6Option DhcpV6Layer::addOption(const DhcpV6OptionBuilder& optionBuilder) -{ - return addOptionAt(optionBuilder, getHeaderLen()); +DhcpV6Option DhcpV6Layer::addOption(const DhcpV6OptionBuilder& optionBuilder) { + return addOptionAt(optionBuilder, getHeaderLen()); } -DhcpV6Option DhcpV6Layer::addOptionAfter(const DhcpV6OptionBuilder& optionBuilder, DhcpV6OptionType optionType) -{ - int offset = 0; +DhcpV6Option +DhcpV6Layer::addOptionAfter(const DhcpV6OptionBuilder& optionBuilder, + DhcpV6OptionType optionType) { + int offset = 0; - DhcpV6Option prevOpt = getOptionData(optionType); + DhcpV6Option prevOpt = getOptionData(optionType); - if (prevOpt.isNull()) - { - PCPP_LOG_ERROR("Option type " << optionType << " doesn't exist in layer"); - return DhcpV6Option(nullptr); - } - offset = prevOpt.getRecordBasePtr() + prevOpt.getTotalSize() - m_Data; - return addOptionAt(optionBuilder, offset); + if (prevOpt.isNull()) { + PCPP_LOG_ERROR("Option type " << optionType << " doesn't exist in layer"); + return DhcpV6Option(nullptr); + } + offset = prevOpt.getRecordBasePtr() + prevOpt.getTotalSize() - m_Data; + return addOptionAt(optionBuilder, offset); } -DhcpV6Option DhcpV6Layer::addOptionBefore(const DhcpV6OptionBuilder& optionBuilder, DhcpV6OptionType optionType) -{ - int offset = 0; +DhcpV6Option +DhcpV6Layer::addOptionBefore(const DhcpV6OptionBuilder& optionBuilder, + DhcpV6OptionType optionType) { + int offset = 0; - DhcpV6Option nextOpt = getOptionData(optionType); + DhcpV6Option nextOpt = getOptionData(optionType); - if (nextOpt.isNull()) - { - PCPP_LOG_ERROR("Option type " << optionType << " doesn't exist in layer"); - return DhcpV6Option(nullptr); - } + if (nextOpt.isNull()) { + PCPP_LOG_ERROR("Option type " << optionType << " doesn't exist in layer"); + return DhcpV6Option(nullptr); + } - offset = nextOpt.getRecordBasePtr() - m_Data; - return addOptionAt(optionBuilder, offset); + offset = nextOpt.getRecordBasePtr() - m_Data; + return addOptionAt(optionBuilder, offset); } -bool DhcpV6Layer::removeOption(DhcpV6OptionType optionType) -{ - DhcpV6Option optToRemove = getOptionData(optionType); - if (optToRemove.isNull()) - { - return false; - } +bool DhcpV6Layer::removeOption(DhcpV6OptionType optionType) { + DhcpV6Option optToRemove = getOptionData(optionType); + if (optToRemove.isNull()) { + return false; + } - int offset = optToRemove.getRecordBasePtr() - m_Data; + int offset = optToRemove.getRecordBasePtr() - m_Data; - if (!shortenLayer(offset, optToRemove.getTotalSize())) - { - return false; - } + if (!shortenLayer(offset, optToRemove.getTotalSize())) { + return false; + } - m_OptionReader.changeTLVRecordCount(-1); - return true; + m_OptionReader.changeTLVRecordCount(-1); + return true; } -bool DhcpV6Layer::removeAllOptions() -{ - int offset = sizeof(dhcpv6_header); +bool DhcpV6Layer::removeAllOptions() { + int offset = sizeof(dhcpv6_header); - if (!shortenLayer(offset, getHeaderLen()-offset)) - return false; + if (!shortenLayer(offset, getHeaderLen() - offset)) + return false; - m_OptionReader.changeTLVRecordCount(0-getOptionCount()); - return true; + m_OptionReader.changeTLVRecordCount(0 - getOptionCount()); + return true; } -std::string DhcpV6Layer::toString() const -{ - return "DHCPv6 Layer, " + getMessageTypeAsString() + " message"; +std::string DhcpV6Layer::toString() const { + return "DHCPv6 Layer, " + getMessageTypeAsString() + " message"; } -} +} // namespace pcpp diff --git a/Packet++/src/DnsLayer.cpp b/Packet++/src/DnsLayer.cpp index 3863c4a7b8..d9dd3caf99 100644 --- a/Packet++/src/DnsLayer.cpp +++ b/Packet++/src/DnsLayer.cpp @@ -1,878 +1,789 @@ #define LOG_MODULE PacketLogModuleDnsLayer #include "DnsLayer.h" -#include "Logger.h" +#include "EndianPortable.h" #include "IpAddress.h" -#include -#include +#include "Logger.h" #include +#include #include -#include "EndianPortable.h" +#include -namespace pcpp -{ +namespace pcpp { // ~~~~~~~~ // DnsLayer // ~~~~~~~~ -DnsLayer::DnsLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) - : Layer(data, dataLen, prevLayer, packet) -{ - init(0, true); +DnsLayer::DnsLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet) + : Layer(data, dataLen, prevLayer, packet) { + init(0, true); } -DnsLayer::DnsLayer() -{ - initNewLayer(0); -} +DnsLayer::DnsLayer() { initNewLayer(0); } -DnsLayer::DnsLayer(const DnsLayer& other) : Layer(other) -{ - init(other.m_OffsetAdjustment, true); +DnsLayer::DnsLayer(const DnsLayer& other) : Layer(other) { + init(other.m_OffsetAdjustment, true); } -DnsLayer::DnsLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet, size_t offsetAdjustment) - : Layer(data, dataLen, prevLayer, packet) -{ - init(offsetAdjustment, true); +DnsLayer::DnsLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet, size_t offsetAdjustment) + : Layer(data, dataLen, prevLayer, packet) { + init(offsetAdjustment, true); } -DnsLayer::DnsLayer(size_t offsetAdjustment) -{ - initNewLayer(offsetAdjustment); -} +DnsLayer::DnsLayer(size_t offsetAdjustment) { initNewLayer(offsetAdjustment); } -DnsLayer& DnsLayer::operator=(const DnsLayer& other) -{ - Layer::operator=(other); +DnsLayer& DnsLayer::operator=(const DnsLayer& other) { + Layer::operator=(other); - IDnsResource* curResource = m_ResourceList; - while (curResource != nullptr) - { - IDnsResource* temp = curResource->getNextResource(); - delete curResource; - curResource = temp; - } + IDnsResource* curResource = m_ResourceList; + while (curResource != nullptr) { + IDnsResource* temp = curResource->getNextResource(); + delete curResource; + curResource = temp; + } - init(other.m_OffsetAdjustment, true); + init(other.m_OffsetAdjustment, true); - return (*this); + return (*this); } -DnsLayer::~DnsLayer() -{ - IDnsResource* curResource = m_ResourceList; - while (curResource != nullptr) - { - IDnsResource* nextResource = curResource->getNextResource(); - delete curResource; - curResource = nextResource; - } +DnsLayer::~DnsLayer() { + IDnsResource* curResource = m_ResourceList; + while (curResource != nullptr) { + IDnsResource* nextResource = curResource->getNextResource(); + delete curResource; + curResource = nextResource; + } } -void DnsLayer::init(size_t offsetAdjustment, bool callParseResource) -{ - m_OffsetAdjustment = offsetAdjustment; - m_Protocol = DNS; - m_ResourceList = nullptr; +void DnsLayer::init(size_t offsetAdjustment, bool callParseResource) { + m_OffsetAdjustment = offsetAdjustment; + m_Protocol = DNS; + m_ResourceList = nullptr; - m_FirstQuery = nullptr; - m_FirstAnswer = nullptr; - m_FirstAuthority = nullptr; - m_FirstAdditional = nullptr; + m_FirstQuery = nullptr; + m_FirstAnswer = nullptr; + m_FirstAuthority = nullptr; + m_FirstAdditional = nullptr; - if (callParseResource) - parseResources(); + if (callParseResource) + parseResources(); } +void DnsLayer::initNewLayer(size_t offsetAdjustment) { + m_OffsetAdjustment = offsetAdjustment; + const size_t headerLen = getBasicHeaderSize(); + m_DataLen = headerLen; + m_Data = new uint8_t[headerLen]; + memset(m_Data, 0, headerLen); + + init(m_OffsetAdjustment, false); +} + +size_t DnsLayer::getBasicHeaderSize() { + return sizeof(dnshdr) + m_OffsetAdjustment; +} + +dnshdr* DnsLayer::getDnsHeader() const { + uint8_t* ptr = m_Data + m_OffsetAdjustment; + return (dnshdr*)ptr; +} + +bool DnsLayer::extendLayer(int offsetInLayer, size_t numOfBytesToExtend, + IDnsResource* resource) { + if (!Layer::extendLayer(offsetInLayer, numOfBytesToExtend)) + return false; + + IDnsResource* curResource = resource->getNextResource(); + while (curResource != nullptr) { + curResource->m_OffsetInLayer += numOfBytesToExtend; + curResource = curResource->getNextResource(); + } + return true; +} + +bool DnsLayer::shortenLayer(int offsetInLayer, size_t numOfBytesToShorten, + IDnsResource* resource) { + if (!Layer::shortenLayer(offsetInLayer, numOfBytesToShorten)) + return false; + + IDnsResource* curResource = resource->getNextResource(); + while (curResource != nullptr) { + curResource->m_OffsetInLayer -= numOfBytesToShorten; + curResource = curResource->getNextResource(); + } + return true; +} + +void DnsLayer::parseResources() { + size_t offsetInPacket = getBasicHeaderSize(); + IDnsResource* curResource = m_ResourceList; + + uint16_t numOfQuestions = be16toh(getDnsHeader()->numberOfQuestions); + uint16_t numOfAnswers = be16toh(getDnsHeader()->numberOfAnswers); + uint16_t numOfAuthority = be16toh(getDnsHeader()->numberOfAuthority); + uint16_t numOfAdditional = be16toh(getDnsHeader()->numberOfAdditional); + + uint32_t numOfOtherResources = + numOfQuestions + numOfAnswers + numOfAuthority + numOfAdditional; + + if (numOfOtherResources > 300) { + PCPP_LOG_ERROR( + "DNS layer contains more than 300 resources, probably a bad packet. " + "Skipping parsing DNS resources"); + return; + } + + for (uint32_t i = 0; i < numOfOtherResources; i++) { + DnsResourceType resType; + if (numOfQuestions > 0) { + resType = DnsQueryType; + numOfQuestions--; + } else if (numOfAnswers > 0) { + resType = DnsAnswerType; + numOfAnswers--; + } else if (numOfAuthority > 0) { + resType = DnsAuthorityType; + numOfAuthority--; + } else { + resType = DnsAdditionalType; + numOfAdditional--; + } + + DnsResource* newResource = nullptr; + DnsQuery* newQuery = nullptr; + IDnsResource* newGenResource = nullptr; + if (resType == DnsQueryType) { + newQuery = new DnsQuery(this, offsetInPacket); + newGenResource = newQuery; + offsetInPacket += newQuery->getSize(); + } else { + newResource = new DnsResource(this, offsetInPacket, resType); + newGenResource = newResource; + offsetInPacket += newResource->getSize(); + } + + if (offsetInPacket > m_DataLen) { + // Parse packet failed, DNS resource is out of bounds. Probably a bad + // packet + delete newGenResource; + return; + } + + // this resource is the first resource + if (m_ResourceList == nullptr) { + m_ResourceList = newGenResource; + curResource = m_ResourceList; + } else { + curResource->setNexResource(newGenResource); + curResource = curResource->getNextResource(); + } + + if (resType == DnsQueryType && m_FirstQuery == nullptr) + m_FirstQuery = newQuery; + else if (resType == DnsAnswerType && m_FirstAnswer == nullptr) + m_FirstAnswer = newResource; + else if (resType == DnsAuthorityType && m_FirstAuthority == nullptr) + m_FirstAuthority = newResource; + else if (resType == DnsAdditionalType && m_FirstAdditional == nullptr) + m_FirstAdditional = newResource; + } +} + +IDnsResource* DnsLayer::getResourceByName(IDnsResource* startFrom, + size_t resourceCount, + const std::string& name, + bool exactMatch) const { + size_t index = 0; + while (index < resourceCount) { + if (startFrom == nullptr) + return nullptr; + + std::string resourceName = startFrom->getName(); + if (exactMatch && resourceName == name) + return startFrom; + else if (!exactMatch && resourceName.find(name) != std::string::npos) + return startFrom; + + startFrom = startFrom->getNextResource(); + + index++; + } + + return nullptr; +} + +DnsQuery* DnsLayer::getQuery(const std::string& name, bool exactMatch) const { + uint16_t numOfQueries = be16toh(getDnsHeader()->numberOfQuestions); + IDnsResource* res = + getResourceByName(m_FirstQuery, numOfQueries, name, exactMatch); + if (res != nullptr) + return dynamic_cast(res); + return nullptr; +} + +DnsQuery* DnsLayer::getFirstQuery() const { return m_FirstQuery; } -void DnsLayer::initNewLayer(size_t offsetAdjustment) -{ - m_OffsetAdjustment = offsetAdjustment; - const size_t headerLen = getBasicHeaderSize(); - m_DataLen = headerLen; - m_Data = new uint8_t[headerLen]; - memset(m_Data, 0, headerLen); - - init(m_OffsetAdjustment, false); -} - -size_t DnsLayer::getBasicHeaderSize() -{ - return sizeof(dnshdr) + m_OffsetAdjustment; -} - -dnshdr* DnsLayer::getDnsHeader() const -{ - uint8_t* ptr = m_Data + m_OffsetAdjustment; - return (dnshdr*)ptr; -} - -bool DnsLayer::extendLayer(int offsetInLayer, size_t numOfBytesToExtend, IDnsResource* resource) -{ - if (!Layer::extendLayer(offsetInLayer, numOfBytesToExtend)) - return false; - - IDnsResource* curResource = resource->getNextResource(); - while (curResource != nullptr) - { - curResource->m_OffsetInLayer += numOfBytesToExtend; - curResource = curResource->getNextResource(); - } - return true; -} - - -bool DnsLayer::shortenLayer(int offsetInLayer, size_t numOfBytesToShorten, IDnsResource* resource) -{ - if (!Layer::shortenLayer(offsetInLayer, numOfBytesToShorten)) - return false; - - IDnsResource* curResource = resource->getNextResource(); - while (curResource != nullptr) - { - curResource->m_OffsetInLayer -= numOfBytesToShorten; - curResource = curResource->getNextResource(); - } - return true; -} - - -void DnsLayer::parseResources() -{ - size_t offsetInPacket = getBasicHeaderSize(); - IDnsResource* curResource = m_ResourceList; - - uint16_t numOfQuestions = be16toh(getDnsHeader()->numberOfQuestions); - uint16_t numOfAnswers = be16toh(getDnsHeader()->numberOfAnswers); - uint16_t numOfAuthority = be16toh(getDnsHeader()->numberOfAuthority); - uint16_t numOfAdditional = be16toh(getDnsHeader()->numberOfAdditional); - - uint32_t numOfOtherResources = numOfQuestions + numOfAnswers + numOfAuthority + numOfAdditional; - - if (numOfOtherResources > 300) - { - PCPP_LOG_ERROR("DNS layer contains more than 300 resources, probably a bad packet. " - "Skipping parsing DNS resources"); - return; - } - - for (uint32_t i = 0; i < numOfOtherResources; i++) - { - DnsResourceType resType; - if (numOfQuestions > 0) - { - resType = DnsQueryType; - numOfQuestions--; - } - else if (numOfAnswers > 0) - { - resType = DnsAnswerType; - numOfAnswers--; - } - else if (numOfAuthority > 0) - { - resType = DnsAuthorityType; - numOfAuthority--; - } - else - { - resType = DnsAdditionalType; - numOfAdditional--; - } - - DnsResource* newResource = nullptr; - DnsQuery* newQuery = nullptr; - IDnsResource* newGenResource = nullptr; - if (resType == DnsQueryType) - { - newQuery = new DnsQuery(this, offsetInPacket); - newGenResource = newQuery; - offsetInPacket += newQuery->getSize(); - } - else - { - newResource = new DnsResource(this, offsetInPacket, resType); - newGenResource = newResource; - offsetInPacket += newResource->getSize(); - } - - if (offsetInPacket > m_DataLen) - { - //Parse packet failed, DNS resource is out of bounds. Probably a bad packet - delete newGenResource; - return; - } - - // this resource is the first resource - if (m_ResourceList == nullptr) - { - m_ResourceList = newGenResource; - curResource = m_ResourceList; - } - else - { - curResource->setNexResource(newGenResource); - curResource = curResource->getNextResource(); - } - - if (resType == DnsQueryType && m_FirstQuery == nullptr) - m_FirstQuery = newQuery; - else if (resType == DnsAnswerType && m_FirstAnswer == nullptr) - m_FirstAnswer = newResource; - else if (resType == DnsAuthorityType && m_FirstAuthority == nullptr) - m_FirstAuthority = newResource; - else if (resType == DnsAdditionalType && m_FirstAdditional == nullptr) - m_FirstAdditional = newResource; - } - -} - -IDnsResource* DnsLayer::getResourceByName(IDnsResource* startFrom, size_t resourceCount, const std::string& name, bool exactMatch) const -{ - size_t index = 0; - while (index < resourceCount) - { - if (startFrom == nullptr) - return nullptr; - - std::string resourceName = startFrom->getName(); - if (exactMatch && resourceName == name) - return startFrom; - else if (!exactMatch && resourceName.find(name) != std::string::npos) - return startFrom; - - startFrom = startFrom->getNextResource(); - - index++; - } - - return nullptr; -} - -DnsQuery* DnsLayer::getQuery(const std::string& name, bool exactMatch) const -{ - uint16_t numOfQueries = be16toh(getDnsHeader()->numberOfQuestions); - IDnsResource* res = getResourceByName(m_FirstQuery, numOfQueries, name, exactMatch); - if (res != nullptr) - return dynamic_cast(res); - return nullptr; -} - - -DnsQuery* DnsLayer::getFirstQuery() const -{ - return m_FirstQuery; -} - - -DnsQuery* DnsLayer::getNextQuery(DnsQuery* query) const -{ - if (query == nullptr - || query->getNextResource() == nullptr - || query->getType() != DnsQueryType - || query->getNextResource()->getType() != DnsQueryType) - return nullptr; - - return (DnsQuery*)(query->getNextResource()); -} +DnsQuery* DnsLayer::getNextQuery(DnsQuery* query) const { + if (query == nullptr || query->getNextResource() == nullptr || + query->getType() != DnsQueryType || + query->getNextResource()->getType() != DnsQueryType) + return nullptr; -size_t DnsLayer::getQueryCount() const -{ - return be16toh(getDnsHeader()->numberOfQuestions); + return (DnsQuery*)(query->getNextResource()); } -DnsResource* DnsLayer::getAnswer(const std::string& name, bool exactMatch) const -{ - uint16_t numOfAnswers = be16toh(getDnsHeader()->numberOfAnswers); - IDnsResource* res = getResourceByName(m_FirstAnswer, numOfAnswers, name, exactMatch); - if (res != nullptr) - return dynamic_cast(res); - return nullptr; +size_t DnsLayer::getQueryCount() const { + return be16toh(getDnsHeader()->numberOfQuestions); } -DnsResource* DnsLayer::getFirstAnswer() const -{ - return m_FirstAnswer; +DnsResource* DnsLayer::getAnswer(const std::string& name, + bool exactMatch) const { + uint16_t numOfAnswers = be16toh(getDnsHeader()->numberOfAnswers); + IDnsResource* res = + getResourceByName(m_FirstAnswer, numOfAnswers, name, exactMatch); + if (res != nullptr) + return dynamic_cast(res); + return nullptr; } -DnsResource* DnsLayer::getNextAnswer(DnsResource* answer) const -{ - if (answer == nullptr - || answer->getNextResource() == nullptr - || answer->getType() != DnsAnswerType - || answer->getNextResource()->getType() != DnsAnswerType) - return nullptr; +DnsResource* DnsLayer::getFirstAnswer() const { return m_FirstAnswer; } - return (DnsResource*)(answer->getNextResource()); -} +DnsResource* DnsLayer::getNextAnswer(DnsResource* answer) const { + if (answer == nullptr || answer->getNextResource() == nullptr || + answer->getType() != DnsAnswerType || + answer->getNextResource()->getType() != DnsAnswerType) + return nullptr; -size_t DnsLayer::getAnswerCount() const -{ - return be16toh(getDnsHeader()->numberOfAnswers); + return (DnsResource*)(answer->getNextResource()); } -DnsResource* DnsLayer::getAuthority(const std::string& name, bool exactMatch) const -{ - uint16_t numOfAuthorities = be16toh(getDnsHeader()->numberOfAuthority); - IDnsResource* res = getResourceByName(m_FirstAuthority, numOfAuthorities, name, exactMatch); - if (res != nullptr) - return dynamic_cast(res); - return nullptr; +size_t DnsLayer::getAnswerCount() const { + return be16toh(getDnsHeader()->numberOfAnswers); } -DnsResource* DnsLayer::getFirstAuthority() const -{ - return m_FirstAuthority; +DnsResource* DnsLayer::getAuthority(const std::string& name, + bool exactMatch) const { + uint16_t numOfAuthorities = be16toh(getDnsHeader()->numberOfAuthority); + IDnsResource* res = + getResourceByName(m_FirstAuthority, numOfAuthorities, name, exactMatch); + if (res != nullptr) + return dynamic_cast(res); + return nullptr; } -DnsResource* DnsLayer::getNextAuthority(DnsResource* authority) const -{ - if (authority == nullptr - || authority->getNextResource() == nullptr - || authority->getType() != DnsAuthorityType - || authority->getNextResource()->getType() != DnsAuthorityType) - return nullptr; +DnsResource* DnsLayer::getFirstAuthority() const { return m_FirstAuthority; } - return (DnsResource*)(authority->getNextResource()); -} +DnsResource* DnsLayer::getNextAuthority(DnsResource* authority) const { + if (authority == nullptr || authority->getNextResource() == nullptr || + authority->getType() != DnsAuthorityType || + authority->getNextResource()->getType() != DnsAuthorityType) + return nullptr; -size_t DnsLayer::getAuthorityCount() const -{ - return be16toh(getDnsHeader()->numberOfAuthority); + return (DnsResource*)(authority->getNextResource()); } -DnsResource* DnsLayer::getAdditionalRecord(const std::string& name, bool exactMatch) const -{ - uint16_t numOfAdditionalRecords = be16toh(getDnsHeader()->numberOfAdditional); - IDnsResource* res = getResourceByName(m_FirstAdditional, numOfAdditionalRecords, name, exactMatch); - if (res != nullptr) - return dynamic_cast(res); - return nullptr; -} - -DnsResource* DnsLayer::getFirstAdditionalRecord() const -{ - return m_FirstAdditional; -} - -DnsResource* DnsLayer::getNextAdditionalRecord(DnsResource* additionalRecord) const -{ - if (additionalRecord == nullptr - || additionalRecord->getNextResource() == nullptr - || additionalRecord->getType() != DnsAdditionalType - || additionalRecord->getNextResource()->getType() != DnsAdditionalType) - return nullptr; - - return (DnsResource*)(additionalRecord->getNextResource()); -} - -size_t DnsLayer::getAdditionalRecordCount() const -{ - return be16toh(getDnsHeader()->numberOfAdditional); -} - -std::string DnsLayer::toString() const -{ - std::ostringstream tidAsString; - tidAsString << be16toh(getDnsHeader()->transactionID); - - std::ostringstream queryCount; - queryCount << getQueryCount(); - - std::ostringstream answerCount; - answerCount << getAnswerCount(); - - std::ostringstream authorityCount; - authorityCount << getAuthorityCount(); - - std::ostringstream additionalCount; - additionalCount << getAdditionalRecordCount(); - - if (getDnsHeader()->queryOrResponse == 1) - { - return "DNS query response, ID: " + tidAsString.str() + ";" + - " queries: " + queryCount.str() + - ", answers: " + answerCount.str() + - ", authorities: " + authorityCount.str() + - ", additional record: " + additionalCount.str(); - } - else if (getDnsHeader()->queryOrResponse == 0) - { - return "DNS query, ID: " + tidAsString.str() + ";" + - " queries: " + queryCount.str() + - ", answers: " + answerCount.str() + - ", authorities: " + authorityCount.str() + - ", additional record: " + additionalCount.str(); - - } - else // not likely - a DNS with no answers and no queries - { - return "DNS record without queries and answers, ID: " + tidAsString.str() + ";" + - " queries: " + queryCount.str() + - ", answers: " + answerCount.str() + - ", authorities: " + authorityCount.str() + - ", additional record: " + additionalCount.str(); - } -} - -IDnsResource* DnsLayer::getFirstResource(DnsResourceType resType) const -{ - switch (resType) - { - case DnsQueryType: - { - return m_FirstQuery; - } - case DnsAnswerType: - { - return m_FirstAnswer; - } - case DnsAuthorityType: - { - return m_FirstAuthority; - } - case DnsAdditionalType: - { - return m_FirstAdditional; - } - default: - return nullptr; - } -} - -void DnsLayer::setFirstResource(DnsResourceType resType, IDnsResource* resource) -{ - switch (resType) - { - case DnsQueryType: - { - m_FirstQuery = dynamic_cast(resource); - break; - } - case DnsAnswerType: - { - m_FirstAnswer = dynamic_cast(resource); - break; - } - case DnsAuthorityType: - { - m_FirstAuthority = dynamic_cast(resource); - break; - } - case DnsAdditionalType: - { - m_FirstAdditional = dynamic_cast(resource); - break; - } - default: - return; - } -} - -DnsResource* DnsLayer::addResource(DnsResourceType resType, const std::string& name, DnsType dnsType, DnsClass dnsClass, - uint32_t ttl, IDnsResourceData* data) -{ - // create new query on temporary buffer - uint8_t newResourceRawData[4096]; - memset(newResourceRawData, 0, sizeof(newResourceRawData)); - - DnsResource* newResource = new DnsResource(newResourceRawData, resType); - - newResource->setDnsClass(dnsClass); - - newResource->setDnsType(dnsType); - - // cannot return false since layer shouldn't be extended or shortened in this stage - newResource->setName(name); - - newResource->setTTL(ttl); - - if (!newResource->setData(data)) - { - delete newResource; - PCPP_LOG_ERROR("Couldn't set new resource data"); - return nullptr; - } - - size_t newResourceOffsetInLayer = getBasicHeaderSize(); - IDnsResource* curResource = m_ResourceList; - while (curResource != nullptr && curResource->getType() <= resType) - { - newResourceOffsetInLayer += curResource->getSize(); - IDnsResource* nextResource = curResource->getNextResource(); - if (nextResource == nullptr || nextResource->getType() > resType) - break; - curResource = nextResource; - } - - - // set next resource for new resource. This must happen here for extendLayer to succeed - if (curResource != nullptr) - { - if (curResource->getType() > newResource->getType()) - newResource->setNexResource(m_ResourceList); - else - newResource->setNexResource(curResource->getNextResource()); - } - else //curResource != NULL - newResource->setNexResource(m_ResourceList); - - // extend layer to make room for the new resource - if (!extendLayer(newResourceOffsetInLayer, newResource->getSize(), newResource)) - { - PCPP_LOG_ERROR("Couldn't extend DNS layer, addResource failed"); - delete newResource; - return nullptr; - } - - // connect the new resource to layer - newResource->setDnsLayer(this, newResourceOffsetInLayer); - - // connect the new resource to the layer's resource list - if (curResource != nullptr) - { - curResource->setNexResource(newResource); - // this means the new resource is the first of it's type - if (curResource->getType() < newResource->getType()) - { - setFirstResource(resType, newResource); - } - // this means the new resource should be the first resource in the packet - else if (curResource->getType() > newResource->getType()) - { - m_ResourceList = newResource; - - setFirstResource(resType, newResource); - } - } - else // curResource != NULL, meaning this is the first resource in layer - { - m_ResourceList = newResource; - - setFirstResource(resType, newResource); - } - - return newResource; -} - - -DnsQuery* DnsLayer::addQuery(const std::string& name, DnsType dnsType, DnsClass dnsClass) -{ - // create new query on temporary buffer - uint8_t newQueryRawData[256]; - DnsQuery* newQuery = new DnsQuery(newQueryRawData); - - newQuery->setDnsClass(dnsClass); - newQuery->setDnsType(dnsType); - - // cannot return false since layer shouldn't be extended or shortened in this stage - newQuery->setName(name); - - - // find the offset in the layer to insert the new query - size_t newQueryOffsetInLayer = getBasicHeaderSize(); - DnsQuery* curQuery = getFirstQuery(); - while (curQuery != nullptr) - { - newQueryOffsetInLayer += curQuery->getSize(); - DnsQuery* nextQuery = getNextQuery(curQuery); - if (nextQuery == nullptr) - break; - curQuery = nextQuery; - - } - - // set next resource for new query. This must happen here for extendLayer to succeed - if (curQuery != nullptr) - newQuery->setNexResource(curQuery->getNextResource()); - else - newQuery->setNexResource(m_ResourceList); - - // extend layer to make room for the new query - if (!extendLayer(newQueryOffsetInLayer, newQuery->getSize(), newQuery)) - { - PCPP_LOG_ERROR("Couldn't extend DNS layer, addQuery failed"); - delete newQuery; - return nullptr; - } - - // connect the new query to layer - newQuery->setDnsLayer(this, newQueryOffsetInLayer); - - // connect the new query to the layer's resource list - if (curQuery != nullptr) - curQuery->setNexResource(newQuery); - else // curQuery == NULL, meaning this is the first query - { - m_ResourceList = newQuery; - m_FirstQuery = newQuery; - } - - // increase number of queries - getDnsHeader()->numberOfQuestions = htobe16(getQueryCount() + 1); - - return newQuery; +size_t DnsLayer::getAuthorityCount() const { + return be16toh(getDnsHeader()->numberOfAuthority); } -DnsQuery* DnsLayer::addQuery(DnsQuery* const copyQuery) -{ - if (copyQuery == nullptr) - return nullptr; - - return addQuery(copyQuery->getName(), copyQuery->getDnsType(), copyQuery->getDnsClass()); +DnsResource* DnsLayer::getAdditionalRecord(const std::string& name, + bool exactMatch) const { + uint16_t numOfAdditionalRecords = be16toh(getDnsHeader()->numberOfAdditional); + IDnsResource* res = getResourceByName( + m_FirstAdditional, numOfAdditionalRecords, name, exactMatch); + if (res != nullptr) + return dynamic_cast(res); + return nullptr; } -bool DnsLayer::removeQuery(const std::string& queryNameToRemove, bool exactMatch) -{ - DnsQuery* queryToRemove = getQuery(queryNameToRemove, exactMatch); - if (queryToRemove == nullptr) - { - PCPP_LOG_DEBUG("Query not found"); - return false; - } - - return removeQuery(queryToRemove); +DnsResource* DnsLayer::getFirstAdditionalRecord() const { + return m_FirstAdditional; } -bool DnsLayer::removeQuery(DnsQuery* queryToRemove) -{ - bool res = removeResource(queryToRemove); - if (res) - { - // decrease number of query records - getDnsHeader()->numberOfQuestions = htobe16(getQueryCount() - 1); - } +DnsResource* +DnsLayer::getNextAdditionalRecord(DnsResource* additionalRecord) const { + if (additionalRecord == nullptr || + additionalRecord->getNextResource() == nullptr || + additionalRecord->getType() != DnsAdditionalType || + additionalRecord->getNextResource()->getType() != DnsAdditionalType) + return nullptr; - return res; + return (DnsResource*)(additionalRecord->getNextResource()); } -DnsResource* DnsLayer::addAnswer(const std::string& name, DnsType dnsType, DnsClass dnsClass, uint32_t ttl, IDnsResourceData* data) -{ - DnsResource* res = addResource(DnsAnswerType, name, dnsType, dnsClass, ttl, data); - if (res != nullptr) - { - // increase number of answer records - getDnsHeader()->numberOfAnswers = htobe16(getAnswerCount() + 1); - } - - return res; +size_t DnsLayer::getAdditionalRecordCount() const { + return be16toh(getDnsHeader()->numberOfAdditional); } -DnsResource* DnsLayer::addAnswer(DnsResource* const copyAnswer) -{ - if (copyAnswer == nullptr) - return nullptr; +std::string DnsLayer::toString() const { + std::ostringstream tidAsString; + tidAsString << be16toh(getDnsHeader()->transactionID); - return addAnswer(copyAnswer->getName(), copyAnswer->getDnsType(), copyAnswer->getDnsClass(), copyAnswer->getTTL(), copyAnswer->getData().get()); -} + std::ostringstream queryCount; + queryCount << getQueryCount(); -bool DnsLayer::removeAnswer(const std::string& answerNameToRemove, bool exactMatch) -{ - DnsResource* answerToRemove = getAnswer(answerNameToRemove, exactMatch); - if (answerToRemove == nullptr) - { - PCPP_LOG_DEBUG("Answer record not found"); - return false; - } + std::ostringstream answerCount; + answerCount << getAnswerCount(); + + std::ostringstream authorityCount; + authorityCount << getAuthorityCount(); + + std::ostringstream additionalCount; + additionalCount << getAdditionalRecordCount(); + + if (getDnsHeader()->queryOrResponse == 1) { + return "DNS query response, ID: " + tidAsString.str() + ";" + + " queries: " + queryCount.str() + ", answers: " + answerCount.str() + + ", authorities: " + authorityCount.str() + + ", additional record: " + additionalCount.str(); + } else if (getDnsHeader()->queryOrResponse == 0) { + return "DNS query, ID: " + tidAsString.str() + ";" + + " queries: " + queryCount.str() + ", answers: " + answerCount.str() + + ", authorities: " + authorityCount.str() + + ", additional record: " + additionalCount.str(); + + } else // not likely - a DNS with no answers and no queries + { + return "DNS record without queries and answers, ID: " + tidAsString.str() + + ";" + " queries: " + queryCount.str() + + ", answers: " + answerCount.str() + + ", authorities: " + authorityCount.str() + + ", additional record: " + additionalCount.str(); + } +} + +IDnsResource* DnsLayer::getFirstResource(DnsResourceType resType) const { + switch (resType) { + case DnsQueryType: { + return m_FirstQuery; + } + case DnsAnswerType: { + return m_FirstAnswer; + } + case DnsAuthorityType: { + return m_FirstAuthority; + } + case DnsAdditionalType: { + return m_FirstAdditional; + } + default: + return nullptr; + } +} + +void DnsLayer::setFirstResource(DnsResourceType resType, + IDnsResource* resource) { + switch (resType) { + case DnsQueryType: { + m_FirstQuery = dynamic_cast(resource); + break; + } + case DnsAnswerType: { + m_FirstAnswer = dynamic_cast(resource); + break; + } + case DnsAuthorityType: { + m_FirstAuthority = dynamic_cast(resource); + break; + } + case DnsAdditionalType: { + m_FirstAdditional = dynamic_cast(resource); + break; + } + default: + return; + } +} + +DnsResource* DnsLayer::addResource(DnsResourceType resType, + const std::string& name, DnsType dnsType, + DnsClass dnsClass, uint32_t ttl, + IDnsResourceData* data) { + // create new query on temporary buffer + uint8_t newResourceRawData[4096]; + memset(newResourceRawData, 0, sizeof(newResourceRawData)); + + DnsResource* newResource = new DnsResource(newResourceRawData, resType); + + newResource->setDnsClass(dnsClass); + + newResource->setDnsType(dnsType); + + // cannot return false since layer shouldn't be extended or shortened in this + // stage + newResource->setName(name); + + newResource->setTTL(ttl); + + if (!newResource->setData(data)) { + delete newResource; + PCPP_LOG_ERROR("Couldn't set new resource data"); + return nullptr; + } + + size_t newResourceOffsetInLayer = getBasicHeaderSize(); + IDnsResource* curResource = m_ResourceList; + while (curResource != nullptr && curResource->getType() <= resType) { + newResourceOffsetInLayer += curResource->getSize(); + IDnsResource* nextResource = curResource->getNextResource(); + if (nextResource == nullptr || nextResource->getType() > resType) + break; + curResource = nextResource; + } + + // set next resource for new resource. This must happen here for extendLayer + // to succeed + if (curResource != nullptr) { + if (curResource->getType() > newResource->getType()) + newResource->setNexResource(m_ResourceList); + else + newResource->setNexResource(curResource->getNextResource()); + } else // curResource != NULL + newResource->setNexResource(m_ResourceList); + + // extend layer to make room for the new resource + if (!extendLayer(newResourceOffsetInLayer, newResource->getSize(), + newResource)) { + PCPP_LOG_ERROR("Couldn't extend DNS layer, addResource failed"); + delete newResource; + return nullptr; + } + + // connect the new resource to layer + newResource->setDnsLayer(this, newResourceOffsetInLayer); + + // connect the new resource to the layer's resource list + if (curResource != nullptr) { + curResource->setNexResource(newResource); + // this means the new resource is the first of it's type + if (curResource->getType() < newResource->getType()) { + setFirstResource(resType, newResource); + } + // this means the new resource should be the first resource in the packet + else if (curResource->getType() > newResource->getType()) { + m_ResourceList = newResource; + + setFirstResource(resType, newResource); + } + } else // curResource != NULL, meaning this is the first resource in layer + { + m_ResourceList = newResource; + + setFirstResource(resType, newResource); + } + + return newResource; +} + +DnsQuery* DnsLayer::addQuery(const std::string& name, DnsType dnsType, + DnsClass dnsClass) { + // create new query on temporary buffer + uint8_t newQueryRawData[256]; + DnsQuery* newQuery = new DnsQuery(newQueryRawData); + + newQuery->setDnsClass(dnsClass); + newQuery->setDnsType(dnsType); + + // cannot return false since layer shouldn't be extended or shortened in this + // stage + newQuery->setName(name); + + // find the offset in the layer to insert the new query + size_t newQueryOffsetInLayer = getBasicHeaderSize(); + DnsQuery* curQuery = getFirstQuery(); + while (curQuery != nullptr) { + newQueryOffsetInLayer += curQuery->getSize(); + DnsQuery* nextQuery = getNextQuery(curQuery); + if (nextQuery == nullptr) + break; + curQuery = nextQuery; + } + + // set next resource for new query. This must happen here for extendLayer to + // succeed + if (curQuery != nullptr) + newQuery->setNexResource(curQuery->getNextResource()); + else + newQuery->setNexResource(m_ResourceList); + + // extend layer to make room for the new query + if (!extendLayer(newQueryOffsetInLayer, newQuery->getSize(), newQuery)) { + PCPP_LOG_ERROR("Couldn't extend DNS layer, addQuery failed"); + delete newQuery; + return nullptr; + } + + // connect the new query to layer + newQuery->setDnsLayer(this, newQueryOffsetInLayer); + + // connect the new query to the layer's resource list + if (curQuery != nullptr) + curQuery->setNexResource(newQuery); + else // curQuery == NULL, meaning this is the first query + { + m_ResourceList = newQuery; + m_FirstQuery = newQuery; + } + + // increase number of queries + getDnsHeader()->numberOfQuestions = htobe16(getQueryCount() + 1); + + return newQuery; +} + +DnsQuery* DnsLayer::addQuery(DnsQuery* const copyQuery) { + if (copyQuery == nullptr) + return nullptr; + + return addQuery(copyQuery->getName(), copyQuery->getDnsType(), + copyQuery->getDnsClass()); +} + +bool DnsLayer::removeQuery(const std::string& queryNameToRemove, + bool exactMatch) { + DnsQuery* queryToRemove = getQuery(queryNameToRemove, exactMatch); + if (queryToRemove == nullptr) { + PCPP_LOG_DEBUG("Query not found"); + return false; + } - return removeAnswer(answerToRemove); + return removeQuery(queryToRemove); } -bool DnsLayer::removeAnswer(DnsResource* answerToRemove) -{ - bool res = removeResource(answerToRemove); - if (res) - { - // decrease number of answer records - getDnsHeader()->numberOfAnswers = htobe16(getAnswerCount() - 1); - } - - return res; +bool DnsLayer::removeQuery(DnsQuery* queryToRemove) { + bool res = removeResource(queryToRemove); + if (res) { + // decrease number of query records + getDnsHeader()->numberOfQuestions = htobe16(getQueryCount() - 1); + } + + return res; } +DnsResource* DnsLayer::addAnswer(const std::string& name, DnsType dnsType, + DnsClass dnsClass, uint32_t ttl, + IDnsResourceData* data) { + DnsResource* res = + addResource(DnsAnswerType, name, dnsType, dnsClass, ttl, data); + if (res != nullptr) { + // increase number of answer records + getDnsHeader()->numberOfAnswers = htobe16(getAnswerCount() + 1); + } -DnsResource* DnsLayer::addAuthority(const std::string& name, DnsType dnsType, DnsClass dnsClass, uint32_t ttl, IDnsResourceData* data) -{ - DnsResource* res = addResource(DnsAuthorityType, name, dnsType, dnsClass, ttl, data); - if (res != nullptr) - { - // increase number of authority records - getDnsHeader()->numberOfAuthority = htobe16(getAuthorityCount() + 1); - } - - return res; + return res; } -DnsResource* DnsLayer::addAuthority(DnsResource* const copyAuthority) -{ - if (copyAuthority == nullptr) - return nullptr; +DnsResource* DnsLayer::addAnswer(DnsResource* const copyAnswer) { + if (copyAnswer == nullptr) + return nullptr; - return addAuthority(copyAuthority->getName(), copyAuthority->getDnsType(), copyAuthority->getDnsClass(), copyAuthority->getTTL(), copyAuthority->getData().get()); + return addAnswer(copyAnswer->getName(), copyAnswer->getDnsType(), + copyAnswer->getDnsClass(), copyAnswer->getTTL(), + copyAnswer->getData().get()); } -bool DnsLayer::removeAuthority(const std::string& authorityNameToRemove, bool exactMatch) -{ - DnsResource* authorityToRemove = getAuthority(authorityNameToRemove, exactMatch); - if (authorityToRemove == nullptr) - { - PCPP_LOG_DEBUG("Authority not found"); - return false; - } +bool DnsLayer::removeAnswer(const std::string& answerNameToRemove, + bool exactMatch) { + DnsResource* answerToRemove = getAnswer(answerNameToRemove, exactMatch); + if (answerToRemove == nullptr) { + PCPP_LOG_DEBUG("Answer record not found"); + return false; + } - return removeAuthority(authorityToRemove); + return removeAnswer(answerToRemove); } -bool DnsLayer::removeAuthority(DnsResource* authorityToRemove) -{ - bool res = removeResource(authorityToRemove); - if (res) - { - // decrease number of authority records - getDnsHeader()->numberOfAuthority = htobe16(getAuthorityCount() - 1); - } +bool DnsLayer::removeAnswer(DnsResource* answerToRemove) { + bool res = removeResource(answerToRemove); + if (res) { + // decrease number of answer records + getDnsHeader()->numberOfAnswers = htobe16(getAnswerCount() - 1); + } - return res; + return res; } +DnsResource* DnsLayer::addAuthority(const std::string& name, DnsType dnsType, + DnsClass dnsClass, uint32_t ttl, + IDnsResourceData* data) { + DnsResource* res = + addResource(DnsAuthorityType, name, dnsType, dnsClass, ttl, data); + if (res != nullptr) { + // increase number of authority records + getDnsHeader()->numberOfAuthority = htobe16(getAuthorityCount() + 1); + } -DnsResource* DnsLayer::addAdditionalRecord(const std::string& name, DnsType dnsType, DnsClass dnsClass, uint32_t ttl, IDnsResourceData* data) -{ - DnsResource* res = addResource(DnsAdditionalType, name, dnsType, dnsClass, ttl, data); - if (res != nullptr) - { - // increase number of authority records - getDnsHeader()->numberOfAdditional = htobe16(getAdditionalRecordCount() + 1); - } - - return res; -} - -DnsResource* DnsLayer::addAdditionalRecord(const std::string& name, DnsType dnsType, uint16_t customData1, uint32_t customData2, IDnsResourceData* data) -{ - DnsResource* res = addAdditionalRecord(name, dnsType, DNS_CLASS_ANY, customData2, data); - if (res != nullptr) - { - res->setCustomDnsClass(customData1); - } - - return res; + return res; } -DnsResource* DnsLayer::addAdditionalRecord(DnsResource* const copyAdditionalRecord) -{ - if (copyAdditionalRecord == nullptr) - return nullptr; +DnsResource* DnsLayer::addAuthority(DnsResource* const copyAuthority) { + if (copyAuthority == nullptr) + return nullptr; - return addAdditionalRecord(copyAdditionalRecord->getName(), copyAdditionalRecord->getDnsType(), copyAdditionalRecord->getCustomDnsClass(), copyAdditionalRecord->getTTL(), copyAdditionalRecord->getData().get()); + return addAuthority(copyAuthority->getName(), copyAuthority->getDnsType(), + copyAuthority->getDnsClass(), copyAuthority->getTTL(), + copyAuthority->getData().get()); } -bool DnsLayer::removeAdditionalRecord(const std::string& additionalRecordNameToRemove, bool exactMatch) -{ - DnsResource* additionalRecordToRemove = getAdditionalRecord(additionalRecordNameToRemove, exactMatch); - if (additionalRecordToRemove == nullptr) - { - PCPP_LOG_DEBUG("Additional record not found"); - return false; - } +bool DnsLayer::removeAuthority(const std::string& authorityNameToRemove, + bool exactMatch) { + DnsResource* authorityToRemove = + getAuthority(authorityNameToRemove, exactMatch); + if (authorityToRemove == nullptr) { + PCPP_LOG_DEBUG("Authority not found"); + return false; + } - return removeAdditionalRecord(additionalRecordToRemove); + return removeAuthority(authorityToRemove); } -bool DnsLayer::removeAdditionalRecord(DnsResource* additionalRecordToRemove) -{ - bool res = removeResource(additionalRecordToRemove); - if (res) - { - // decrease number of additional records - getDnsHeader()->numberOfAdditional = htobe16(getAdditionalRecordCount() - 1); - } +bool DnsLayer::removeAuthority(DnsResource* authorityToRemove) { + bool res = removeResource(authorityToRemove); + if (res) { + // decrease number of authority records + getDnsHeader()->numberOfAuthority = htobe16(getAuthorityCount() - 1); + } - return res; + return res; } -bool DnsLayer::removeResource(IDnsResource* resourceToRemove) -{ - if (resourceToRemove == nullptr) - { - PCPP_LOG_DEBUG("resourceToRemove cannot be NULL"); - return false; - } - - // find the resource preceding resourceToRemove - IDnsResource* prevResource = m_ResourceList; +DnsResource* DnsLayer::addAdditionalRecord(const std::string& name, + DnsType dnsType, DnsClass dnsClass, + uint32_t ttl, + IDnsResourceData* data) { + DnsResource* res = + addResource(DnsAdditionalType, name, dnsType, dnsClass, ttl, data); + if (res != nullptr) { + // increase number of authority records + getDnsHeader()->numberOfAdditional = + htobe16(getAdditionalRecordCount() + 1); + } + + return res; +} + +DnsResource* DnsLayer::addAdditionalRecord(const std::string& name, + DnsType dnsType, + uint16_t customData1, + uint32_t customData2, + IDnsResourceData* data) { + DnsResource* res = + addAdditionalRecord(name, dnsType, DNS_CLASS_ANY, customData2, data); + if (res != nullptr) { + res->setCustomDnsClass(customData1); + } - if (m_ResourceList != resourceToRemove) - { - while (prevResource != nullptr) - { - IDnsResource* temp = prevResource->getNextResource(); - if (temp == resourceToRemove) - break; + return res; +} - prevResource = temp; - } - } - - if (prevResource == nullptr) - { - PCPP_LOG_DEBUG("Resource not found"); - return false; - } +DnsResource* +DnsLayer::addAdditionalRecord(DnsResource* const copyAdditionalRecord) { + if (copyAdditionalRecord == nullptr) + return nullptr; - // shorten the layer and fix offset in layer for all next DNS resources in the packet - if (!shortenLayer(resourceToRemove->m_OffsetInLayer, resourceToRemove->getSize(), resourceToRemove)) - { - PCPP_LOG_ERROR("Couldn't shorten the DNS layer, resource cannot be removed"); - return false; - } - - // remove resourceToRemove from the resources linked list - if (m_ResourceList != resourceToRemove) - { - prevResource->setNexResource(resourceToRemove->getNextResource()); - } - else - { - m_ResourceList = resourceToRemove->getNextResource(); - } - - // check whether resourceToRemove was the first of its type - if (getFirstResource(resourceToRemove->getType()) == resourceToRemove) - { - IDnsResource* nextResource = resourceToRemove->getNextResource(); - if (nextResource != nullptr && nextResource->getType() == resourceToRemove->getType()) - setFirstResource(resourceToRemove->getType(), nextResource); - else - setFirstResource(resourceToRemove->getType(), nullptr); - } - - // free resourceToRemove memory - delete resourceToRemove; - - return true; + return addAdditionalRecord( + copyAdditionalRecord->getName(), copyAdditionalRecord->getDnsType(), + copyAdditionalRecord->getCustomDnsClass(), copyAdditionalRecord->getTTL(), + copyAdditionalRecord->getData().get()); +} + +bool DnsLayer::removeAdditionalRecord( + const std::string& additionalRecordNameToRemove, bool exactMatch) { + DnsResource* additionalRecordToRemove = + getAdditionalRecord(additionalRecordNameToRemove, exactMatch); + if (additionalRecordToRemove == nullptr) { + PCPP_LOG_DEBUG("Additional record not found"); + return false; + } + + return removeAdditionalRecord(additionalRecordToRemove); +} + +bool DnsLayer::removeAdditionalRecord(DnsResource* additionalRecordToRemove) { + bool res = removeResource(additionalRecordToRemove); + if (res) { + // decrease number of additional records + getDnsHeader()->numberOfAdditional = + htobe16(getAdditionalRecordCount() - 1); + } + + return res; +} + +bool DnsLayer::removeResource(IDnsResource* resourceToRemove) { + if (resourceToRemove == nullptr) { + PCPP_LOG_DEBUG("resourceToRemove cannot be NULL"); + return false; + } + + // find the resource preceding resourceToRemove + IDnsResource* prevResource = m_ResourceList; + + if (m_ResourceList != resourceToRemove) { + while (prevResource != nullptr) { + IDnsResource* temp = prevResource->getNextResource(); + if (temp == resourceToRemove) + break; + + prevResource = temp; + } + } + + if (prevResource == nullptr) { + PCPP_LOG_DEBUG("Resource not found"); + return false; + } + + // shorten the layer and fix offset in layer for all next DNS resources in the + // packet + if (!shortenLayer(resourceToRemove->m_OffsetInLayer, + resourceToRemove->getSize(), resourceToRemove)) { + PCPP_LOG_ERROR( + "Couldn't shorten the DNS layer, resource cannot be removed"); + return false; + } + + // remove resourceToRemove from the resources linked list + if (m_ResourceList != resourceToRemove) { + prevResource->setNexResource(resourceToRemove->getNextResource()); + } else { + m_ResourceList = resourceToRemove->getNextResource(); + } + + // check whether resourceToRemove was the first of its type + if (getFirstResource(resourceToRemove->getType()) == resourceToRemove) { + IDnsResource* nextResource = resourceToRemove->getNextResource(); + if (nextResource != nullptr && + nextResource->getType() == resourceToRemove->getType()) + setFirstResource(resourceToRemove->getType(), nextResource); + else + setFirstResource(resourceToRemove->getType(), nullptr); + } + + // free resourceToRemove memory + delete resourceToRemove; + + return true; } - // ~~~~~~~~~~~~~~~ // DnsOverTcpLayer // ~~~~~~~~~~~~~~~ -uint16_t DnsOverTcpLayer::getTcpMessageLength() -{ - return be16toh(*(uint16_t*)m_Data); +uint16_t DnsOverTcpLayer::getTcpMessageLength() { + return be16toh(*(uint16_t*)m_Data); } -void DnsOverTcpLayer::setTcpMessageLength(uint16_t value) -{ - ((uint16_t*)m_Data)[0] = htobe16(value); +void DnsOverTcpLayer::setTcpMessageLength(uint16_t value) { + ((uint16_t*)m_Data)[0] = htobe16(value); } -void DnsOverTcpLayer::computeCalculateFields() -{ - setTcpMessageLength(m_DataLen - sizeof(uint16_t)); +void DnsOverTcpLayer::computeCalculateFields() { + setTcpMessageLength(m_DataLen - sizeof(uint16_t)); } } // namespace pcpp diff --git a/Packet++/src/DnsResource.cpp b/Packet++/src/DnsResource.cpp index fec1bf0597..824cb4f4fa 100644 --- a/Packet++/src/DnsResource.cpp +++ b/Packet++/src/DnsResource.cpp @@ -1,459 +1,421 @@ #define LOG_MODULE PacketLogModuleDnsLayer #include "DnsResource.h" +#include "EndianPortable.h" #include "Logger.h" #include #include -#include "EndianPortable.h" -namespace pcpp -{ +namespace pcpp { IDnsResource::IDnsResource(DnsLayer* dnsLayer, size_t offsetInLayer) - : m_DnsLayer(dnsLayer), m_OffsetInLayer(offsetInLayer), m_NextResource(nullptr) -{ - char decodedName[4096]; - m_NameLength = decodeName((const char*)getRawData(), decodedName); - if (m_NameLength > 0) - m_DecodedName = decodedName; + : m_DnsLayer(dnsLayer), m_OffsetInLayer(offsetInLayer), + m_NextResource(nullptr) { + char decodedName[4096]; + m_NameLength = decodeName((const char*)getRawData(), decodedName); + if (m_NameLength > 0) + m_DecodedName = decodedName; } IDnsResource::IDnsResource(uint8_t* emptyRawData) - : m_DnsLayer(nullptr), m_OffsetInLayer(0), m_NextResource(nullptr), m_DecodedName(""), m_NameLength(0), m_ExternalRawData(emptyRawData) -{ -} + : m_DnsLayer(nullptr), m_OffsetInLayer(0), m_NextResource(nullptr), + m_DecodedName(""), m_NameLength(0), m_ExternalRawData(emptyRawData) {} -uint8_t* IDnsResource::getRawData() const -{ - if (m_DnsLayer == nullptr) - return m_ExternalRawData; +uint8_t* IDnsResource::getRawData() const { + if (m_DnsLayer == nullptr) + return m_ExternalRawData; - return m_DnsLayer->m_Data + m_OffsetInLayer; + return m_DnsLayer->m_Data + m_OffsetInLayer; } -static size_t cleanup(char* resultPtr, char* result, size_t encodedNameLength) -{ - // remove the last "." - if (resultPtr > result) - { - result[resultPtr - result - 1] = 0; - } - - if (resultPtr - result < 256) - { - // add the last '\0' to encodedNameLength - resultPtr[0] = 0; - encodedNameLength++; - } - - return encodedNameLength; -} +static size_t cleanup(char* resultPtr, char* result, size_t encodedNameLength) { + // remove the last "." + if (resultPtr > result) { + result[resultPtr - result - 1] = 0; + } -size_t IDnsResource::decodeName(const char* encodedName, char* result, int iteration) -{ - size_t encodedNameLength = 0; - size_t decodedNameLength = 0; - char* resultPtr = result; - resultPtr[0] = 0; - - size_t curOffsetInLayer = (uint8_t*)encodedName - m_DnsLayer->m_Data; - if (curOffsetInLayer + 1 > m_DnsLayer->m_DataLen) - return encodedNameLength; - - if (iteration > 20) - { - return encodedNameLength; - } - - uint8_t wordLength = encodedName[0]; - - // A string to parse - while (wordLength != 0) - { - // A pointer to another place in the packet - if ((wordLength & 0xc0) == 0xc0) - { - if (curOffsetInLayer + 2 > m_DnsLayer->m_DataLen || encodedNameLength > 255) - return cleanup(resultPtr, result, encodedNameLength); - - uint16_t offsetInLayer = (wordLength & 0x3f)*256 + (0xFF & encodedName[1]) + m_DnsLayer->m_OffsetAdjustment; - if (offsetInLayer < sizeof(dnshdr) || offsetInLayer >= m_DnsLayer->m_DataLen) - { - PCPP_LOG_ERROR("DNS parsing error: name pointer is illegal"); - return 0; - } - - char tempResult[4096]; - memset(tempResult, 0, sizeof(tempResult)); - int i = 0; - decodeName((const char*)(m_DnsLayer->m_Data + offsetInLayer), tempResult, iteration+1); - while (tempResult[i] != 0 && decodedNameLength < 255) - { - resultPtr[0] = tempResult[i++]; - resultPtr++; - decodedNameLength++; - } - - resultPtr[0] = 0; - - // in this case the length of the pointer is: 1 byte for 0xc0 + 1 byte for the offset itself - return encodedNameLength + sizeof(uint16_t); - } - else - { - // return if next word would be outside of the DNS layer or overflow the buffer behind resultPtr - if (curOffsetInLayer + wordLength + 1 > m_DnsLayer->m_DataLen || encodedNameLength + wordLength > 255) - { - // add the last '\0' to the decoded string - if (encodedNameLength == 256) - { - resultPtr--; - // cppcheck-suppress unreadVariable - decodedNameLength--; - } - else - { - encodedNameLength++; - } - - resultPtr[0] = 0; - return encodedNameLength; - } - - - memcpy(resultPtr, encodedName+1, wordLength); - resultPtr += wordLength; - resultPtr[0] = '.'; - resultPtr++; - decodedNameLength += wordLength + 1; - encodedName += wordLength + 1; - encodedNameLength += wordLength + 1; - - curOffsetInLayer = (uint8_t*)encodedName - m_DnsLayer->m_Data; - if (curOffsetInLayer + 1 > m_DnsLayer->m_DataLen) - { - // add the last '\0' to the decoded string - if (encodedNameLength == 256) - { - // cppcheck-suppress unreadVariable - decodedNameLength--; - resultPtr--; - } - else - { - encodedNameLength++; - } - - resultPtr[0] = 0; - return encodedNameLength; - } - - wordLength = encodedName[0]; - } - } - - return cleanup(resultPtr, result, encodedNameLength); -} + if (resultPtr - result < 256) { + // add the last '\0' to encodedNameLength + resultPtr[0] = 0; + encodedNameLength++; + } + return encodedNameLength; +} -void IDnsResource::encodeName(const std::string& decodedName, char* result, size_t& resultLen) -{ - resultLen = 0; - std::stringstream strstream(decodedName); - std::string word; - while (getline(strstream, word, '.')) - { - // pointer to a different hostname in the packet - if (word[0] == '#') - { - // convert the number from string to int - std::stringstream stream(word.substr(1)); - int pointerInPacket = 0; - stream >> pointerInPacket; - - // verify it's indeed a number and that is in the range of [0-255] - if (stream.fail() || pointerInPacket < 0 || pointerInPacket > 0xff) - { - PCPP_LOG_ERROR("Error encoding the string '" << decodedName << "'"); - return; - } - - // set the pointer to the encoded string result - result[0] = (uint8_t)0xc0; - result[1] = (uint8_t)pointerInPacket; - result += 2; - resultLen += 2; - return; // pointer always comes last - } - - result[0] = word.length(); - result++; - memcpy(result, word.c_str(), word.length()); - result += word.length(); - resultLen += word.length() + 1; - } - - result[0] = 0; - resultLen++; +size_t IDnsResource::decodeName(const char* encodedName, char* result, + int iteration) { + size_t encodedNameLength = 0; + size_t decodedNameLength = 0; + char* resultPtr = result; + resultPtr[0] = 0; + + size_t curOffsetInLayer = (uint8_t*)encodedName - m_DnsLayer->m_Data; + if (curOffsetInLayer + 1 > m_DnsLayer->m_DataLen) + return encodedNameLength; + + if (iteration > 20) { + return encodedNameLength; + } + + uint8_t wordLength = encodedName[0]; + + // A string to parse + while (wordLength != 0) { + // A pointer to another place in the packet + if ((wordLength & 0xc0) == 0xc0) { + if (curOffsetInLayer + 2 > m_DnsLayer->m_DataLen || + encodedNameLength > 255) + return cleanup(resultPtr, result, encodedNameLength); + + uint16_t offsetInLayer = (wordLength & 0x3f) * 256 + + (0xFF & encodedName[1]) + + m_DnsLayer->m_OffsetAdjustment; + if (offsetInLayer < sizeof(dnshdr) || + offsetInLayer >= m_DnsLayer->m_DataLen) { + PCPP_LOG_ERROR("DNS parsing error: name pointer is illegal"); + return 0; + } + + char tempResult[4096]; + memset(tempResult, 0, sizeof(tempResult)); + int i = 0; + decodeName((const char*)(m_DnsLayer->m_Data + offsetInLayer), tempResult, + iteration + 1); + while (tempResult[i] != 0 && decodedNameLength < 255) { + resultPtr[0] = tempResult[i++]; + resultPtr++; + decodedNameLength++; + } + + resultPtr[0] = 0; + + // in this case the length of the pointer is: 1 byte for 0xc0 + 1 byte for + // the offset itself + return encodedNameLength + sizeof(uint16_t); + } else { + // return if next word would be outside of the DNS layer or overflow the + // buffer behind resultPtr + if (curOffsetInLayer + wordLength + 1 > m_DnsLayer->m_DataLen || + encodedNameLength + wordLength > 255) { + // add the last '\0' to the decoded string + if (encodedNameLength == 256) { + resultPtr--; + // cppcheck-suppress unreadVariable + decodedNameLength--; + } else { + encodedNameLength++; + } + + resultPtr[0] = 0; + return encodedNameLength; + } + + memcpy(resultPtr, encodedName + 1, wordLength); + resultPtr += wordLength; + resultPtr[0] = '.'; + resultPtr++; + decodedNameLength += wordLength + 1; + encodedName += wordLength + 1; + encodedNameLength += wordLength + 1; + + curOffsetInLayer = (uint8_t*)encodedName - m_DnsLayer->m_Data; + if (curOffsetInLayer + 1 > m_DnsLayer->m_DataLen) { + // add the last '\0' to the decoded string + if (encodedNameLength == 256) { + // cppcheck-suppress unreadVariable + decodedNameLength--; + resultPtr--; + } else { + encodedNameLength++; + } + + resultPtr[0] = 0; + return encodedNameLength; + } + + wordLength = encodedName[0]; + } + } + + return cleanup(resultPtr, result, encodedNameLength); } +void IDnsResource::encodeName(const std::string& decodedName, char* result, + size_t& resultLen) { + resultLen = 0; + std::stringstream strstream(decodedName); + std::string word; + while (getline(strstream, word, '.')) { + // pointer to a different hostname in the packet + if (word[0] == '#') { + // convert the number from string to int + std::stringstream stream(word.substr(1)); + int pointerInPacket = 0; + stream >> pointerInPacket; + + // verify it's indeed a number and that is in the range of [0-255] + if (stream.fail() || pointerInPacket < 0 || pointerInPacket > 0xff) { + PCPP_LOG_ERROR("Error encoding the string '" << decodedName << "'"); + return; + } + + // set the pointer to the encoded string result + result[0] = (uint8_t)0xc0; + result[1] = (uint8_t)pointerInPacket; + result += 2; + resultLen += 2; + return; // pointer always comes last + } + + result[0] = word.length(); + result++; + memcpy(result, word.c_str(), word.length()); + result += word.length(); + resultLen += word.length() + 1; + } + + result[0] = 0; + resultLen++; +} -DnsType IDnsResource::getDnsType() const -{ - uint16_t dnsType = *(uint16_t*)(getRawData() + m_NameLength); - return (DnsType)be16toh(dnsType); +DnsType IDnsResource::getDnsType() const { + uint16_t dnsType = *(uint16_t*)(getRawData() + m_NameLength); + return (DnsType)be16toh(dnsType); } -void IDnsResource::setDnsType(DnsType newType) -{ - uint16_t newTypeAsInt = htobe16((uint16_t)newType); - memcpy(getRawData() + m_NameLength, &newTypeAsInt, sizeof(uint16_t)); +void IDnsResource::setDnsType(DnsType newType) { + uint16_t newTypeAsInt = htobe16((uint16_t)newType); + memcpy(getRawData() + m_NameLength, &newTypeAsInt, sizeof(uint16_t)); } -DnsClass IDnsResource::getDnsClass() const -{ - uint16_t dnsClass = *(uint16_t*)(getRawData() + m_NameLength + sizeof(uint16_t)); - return (DnsClass)be16toh(dnsClass); +DnsClass IDnsResource::getDnsClass() const { + uint16_t dnsClass = + *(uint16_t*)(getRawData() + m_NameLength + sizeof(uint16_t)); + return (DnsClass)be16toh(dnsClass); } -void IDnsResource::setDnsClass(DnsClass newClass) -{ - uint16_t newClassAsInt = htobe16((uint16_t)newClass); - memcpy(getRawData() + m_NameLength + sizeof(uint16_t), &newClassAsInt, sizeof(uint16_t)); +void IDnsResource::setDnsClass(DnsClass newClass) { + uint16_t newClassAsInt = htobe16((uint16_t)newClass); + memcpy(getRawData() + m_NameLength + sizeof(uint16_t), &newClassAsInt, + sizeof(uint16_t)); } -bool IDnsResource::setName(const std::string& newName) -{ - char encodedName[4096]; - size_t encodedNameLen = 0; - encodeName(newName, encodedName, encodedNameLen); - if (m_DnsLayer != nullptr) - { - if (encodedNameLen > m_NameLength) - { - if (!m_DnsLayer->extendLayer(m_OffsetInLayer, encodedNameLen-m_NameLength, this)) - { - PCPP_LOG_ERROR("Couldn't set name for DNS query, unable to extend layer"); - return false; - } - } - else if (encodedNameLen < m_NameLength) - { - if (!m_DnsLayer->shortenLayer(m_OffsetInLayer, m_NameLength-encodedNameLen, this)) - { - PCPP_LOG_ERROR("Couldn't set name for DNS query, unable to shorten layer"); - return false; - } - } - } - else - { - size_t size = getSize(); - char* tempData = new char[size]; - memcpy(tempData, m_ExternalRawData, size); - memcpy(m_ExternalRawData + encodedNameLen, tempData, size); - delete[] tempData; - } - - memcpy(getRawData(), encodedName, encodedNameLen); - m_NameLength = encodedNameLen; - m_DecodedName = newName; - - return true; +bool IDnsResource::setName(const std::string& newName) { + char encodedName[4096]; + size_t encodedNameLen = 0; + encodeName(newName, encodedName, encodedNameLen); + if (m_DnsLayer != nullptr) { + if (encodedNameLen > m_NameLength) { + if (!m_DnsLayer->extendLayer(m_OffsetInLayer, + encodedNameLen - m_NameLength, this)) { + PCPP_LOG_ERROR( + "Couldn't set name for DNS query, unable to extend layer"); + return false; + } + } else if (encodedNameLen < m_NameLength) { + if (!m_DnsLayer->shortenLayer(m_OffsetInLayer, + m_NameLength - encodedNameLen, this)) { + PCPP_LOG_ERROR( + "Couldn't set name for DNS query, unable to shorten layer"); + return false; + } + } + } else { + size_t size = getSize(); + char* tempData = new char[size]; + memcpy(tempData, m_ExternalRawData, size); + memcpy(m_ExternalRawData + encodedNameLen, tempData, size); + delete[] tempData; + } + + memcpy(getRawData(), encodedName, encodedNameLen); + m_NameLength = encodedNameLen; + m_DecodedName = newName; + + return true; } -void IDnsResource::setDnsLayer(DnsLayer* dnsLayer, size_t offsetInLayer) -{ - memcpy(dnsLayer->m_Data + offsetInLayer, m_ExternalRawData, getSize()); - m_DnsLayer = dnsLayer; - m_OffsetInLayer = offsetInLayer; - m_ExternalRawData = nullptr; +void IDnsResource::setDnsLayer(DnsLayer* dnsLayer, size_t offsetInLayer) { + memcpy(dnsLayer->m_Data + offsetInLayer, m_ExternalRawData, getSize()); + m_DnsLayer = dnsLayer; + m_OffsetInLayer = offsetInLayer; + m_ExternalRawData = nullptr; } -uint32_t DnsResource::getTTL() const -{ - uint32_t ttl = *(uint32_t*)(getRawData() + m_NameLength + 2*sizeof(uint16_t)); - return be32toh(ttl); +uint32_t DnsResource::getTTL() const { + uint32_t ttl = + *(uint32_t*)(getRawData() + m_NameLength + 2 * sizeof(uint16_t)); + return be32toh(ttl); } -void DnsResource::setTTL(uint32_t newTTL) -{ - newTTL = htobe32(newTTL); - memcpy(getRawData() + m_NameLength + 2*sizeof(uint16_t), &newTTL, sizeof(uint32_t)); +void DnsResource::setTTL(uint32_t newTTL) { + newTTL = htobe32(newTTL); + memcpy(getRawData() + m_NameLength + 2 * sizeof(uint16_t), &newTTL, + sizeof(uint32_t)); } -size_t DnsResource::getDataLength() const -{ +size_t DnsResource::getDataLength() const { - size_t sizeToRead = m_NameLength + 2*sizeof(uint16_t) + sizeof(uint32_t); + size_t sizeToRead = m_NameLength + 2 * sizeof(uint16_t) + sizeof(uint32_t); - // Heap buffer overflow may occur here, check boundary of m_DnsLayer->m_Data first - // Due to dataLength which is uint16_t, here m_DnsLayer->m_Data must have at least 2 bytes to read - if (m_DnsLayer && m_OffsetInLayer + sizeToRead >= m_DnsLayer->m_DataLen - 1) - { - return 0; - } + // Heap buffer overflow may occur here, check boundary of m_DnsLayer->m_Data + // first Due to dataLength which is uint16_t, here m_DnsLayer->m_Data must + // have at least 2 bytes to read + if (m_DnsLayer && m_OffsetInLayer + sizeToRead >= m_DnsLayer->m_DataLen - 1) { + return 0; + } - uint16_t dataLength = *(uint16_t*)(getRawData() + sizeToRead); - return be16toh(dataLength); + uint16_t dataLength = *(uint16_t*)(getRawData() + sizeToRead); + return be16toh(dataLength); } -DnsResourceDataPtr DnsResource::getData() const -{ - uint8_t* resourceRawData = getRawData() + m_NameLength + 3*sizeof(uint16_t) + sizeof(uint32_t); - size_t dataLength = getDataLength(); - - switch (getDnsType()) - { - case DNS_TYPE_A: - { - return DnsResourceDataPtr(new IPv4DnsResourceData(resourceRawData, dataLength)); - } - - case DNS_TYPE_AAAA: - { - return DnsResourceDataPtr(new IPv6DnsResourceData(resourceRawData, dataLength)); - } - - case DNS_TYPE_NS: - case DNS_TYPE_CNAME: - case DNS_TYPE_DNAM: - case DNS_TYPE_PTR: - { - return DnsResourceDataPtr(new StringDnsResourceData(resourceRawData, dataLength, const_cast(static_cast(this)))); - } - - case DNS_TYPE_MX: - { - return DnsResourceDataPtr(new MxDnsResourceData(resourceRawData, dataLength, const_cast(static_cast(this)))); - } - - default: - { - return DnsResourceDataPtr(new GenericDnsResourceData(resourceRawData, dataLength)); - } - - } +DnsResourceDataPtr DnsResource::getData() const { + uint8_t* resourceRawData = + getRawData() + m_NameLength + 3 * sizeof(uint16_t) + sizeof(uint32_t); + size_t dataLength = getDataLength(); + + switch (getDnsType()) { + case DNS_TYPE_A: { + return DnsResourceDataPtr( + new IPv4DnsResourceData(resourceRawData, dataLength)); + } + + case DNS_TYPE_AAAA: { + return DnsResourceDataPtr( + new IPv6DnsResourceData(resourceRawData, dataLength)); + } + + case DNS_TYPE_NS: + case DNS_TYPE_CNAME: + case DNS_TYPE_DNAM: + case DNS_TYPE_PTR: { + return DnsResourceDataPtr(new StringDnsResourceData( + resourceRawData, dataLength, + const_cast(static_cast(this)))); + } + + case DNS_TYPE_MX: { + return DnsResourceDataPtr(new MxDnsResourceData( + resourceRawData, dataLength, + const_cast(static_cast(this)))); + } + + default: { + return DnsResourceDataPtr( + new GenericDnsResourceData(resourceRawData, dataLength)); + } + } } -size_t DnsResource::getDataOffset() const -{ - return (size_t)(m_OffsetInLayer + m_NameLength + 3*sizeof(uint16_t) + sizeof(uint32_t)); +size_t DnsResource::getDataOffset() const { + return (size_t)(m_OffsetInLayer + m_NameLength + 3 * sizeof(uint16_t) + + sizeof(uint32_t)); } -bool DnsResource::setData(IDnsResourceData* data) -{ - // convert data to byte array according to the DNS type - size_t dataLength = 0; - uint8_t dataAsByteArr[4096]; - - if (data == nullptr) - { - PCPP_LOG_ERROR("Given data is NULL"); - return false; - } - - switch (getDnsType()) - { - case DNS_TYPE_A: - { - if (!data->isTypeOf()) - { - PCPP_LOG_ERROR("DNS record is of type A but given data isn't of type IPv4DnsResourceData"); - return false; - } - break; - } - - case DNS_TYPE_AAAA: - { - if (!data->isTypeOf()) - { - PCPP_LOG_ERROR("DNS record is of type AAAA but given data isn't of type IPv6DnsResourceData"); - return false; - } - break; - } - - case DNS_TYPE_NS: - case DNS_TYPE_CNAME: - case DNS_TYPE_DNAM: - case DNS_TYPE_PTR: - { - if (!data->isTypeOf()) - { - PCPP_LOG_ERROR("DNS record is of type NS, CNAME, DNAM or PTR but given data isn't of type StringDnsResourceData"); - return false; - } - break; - } - - case DNS_TYPE_MX: - { - if (!data->isTypeOf()) - { - PCPP_LOG_ERROR("DNS record is of type MX but given data isn't of type MxDnsResourceData"); - return false; - } - break; - } - - default: - { - // do nothing - } - - } - - // convert the IDnsResourceData to byte array - if (!data->toByteArr(dataAsByteArr, dataLength, this)) - { - PCPP_LOG_ERROR("Cannot convert DNS resource data to byte array, data is probably invalid"); - return false; - } - - size_t dataLengthOffset = m_NameLength + (2*sizeof(uint16_t)) + sizeof(uint32_t); - size_t dataOffset = dataLengthOffset + sizeof(uint16_t); - - if (m_DnsLayer != nullptr) - { - size_t curLength = getDataLength(); - if (dataLength > curLength) - { - if (!m_DnsLayer->extendLayer(m_OffsetInLayer + dataOffset, dataLength-curLength, this)) - { - PCPP_LOG_ERROR("Couldn't set data for DNS query, unable to extend layer"); - return false; - } - } - else if (dataLength < curLength) - { - if (!m_DnsLayer->shortenLayer(m_OffsetInLayer + dataOffset, curLength-dataLength, this)) - { - PCPP_LOG_ERROR("Couldn't set data for DNS query, unable to shorten layer"); - return false; - } - } - } - - // write data to resource - memcpy(getRawData() + dataOffset, dataAsByteArr, dataLength); - //update data length in resource - dataLength = htobe16((uint16_t)dataLength); - memcpy(getRawData() + dataLengthOffset, &dataLength, sizeof(uint16_t)); - - return true; +bool DnsResource::setData(IDnsResourceData* data) { + // convert data to byte array according to the DNS type + size_t dataLength = 0; + uint8_t dataAsByteArr[4096]; + + if (data == nullptr) { + PCPP_LOG_ERROR("Given data is NULL"); + return false; + } + + switch (getDnsType()) { + case DNS_TYPE_A: { + if (!data->isTypeOf()) { + PCPP_LOG_ERROR("DNS record is of type A but given data isn't of type " + "IPv4DnsResourceData"); + return false; + } + break; + } + + case DNS_TYPE_AAAA: { + if (!data->isTypeOf()) { + PCPP_LOG_ERROR("DNS record is of type AAAA but given data isn't of type " + "IPv6DnsResourceData"); + return false; + } + break; + } + + case DNS_TYPE_NS: + case DNS_TYPE_CNAME: + case DNS_TYPE_DNAM: + case DNS_TYPE_PTR: { + if (!data->isTypeOf()) { + PCPP_LOG_ERROR("DNS record is of type NS, CNAME, DNAM or PTR but given " + "data isn't of type StringDnsResourceData"); + return false; + } + break; + } + + case DNS_TYPE_MX: { + if (!data->isTypeOf()) { + PCPP_LOG_ERROR("DNS record is of type MX but given data isn't of type " + "MxDnsResourceData"); + return false; + } + break; + } + + default: { + // do nothing + } + } + + // convert the IDnsResourceData to byte array + if (!data->toByteArr(dataAsByteArr, dataLength, this)) { + PCPP_LOG_ERROR("Cannot convert DNS resource data to byte array, data is " + "probably invalid"); + return false; + } + + size_t dataLengthOffset = + m_NameLength + (2 * sizeof(uint16_t)) + sizeof(uint32_t); + size_t dataOffset = dataLengthOffset + sizeof(uint16_t); + + if (m_DnsLayer != nullptr) { + size_t curLength = getDataLength(); + if (dataLength > curLength) { + if (!m_DnsLayer->extendLayer(m_OffsetInLayer + dataOffset, + dataLength - curLength, this)) { + PCPP_LOG_ERROR( + "Couldn't set data for DNS query, unable to extend layer"); + return false; + } + } else if (dataLength < curLength) { + if (!m_DnsLayer->shortenLayer(m_OffsetInLayer + dataOffset, + curLength - dataLength, this)) { + PCPP_LOG_ERROR( + "Couldn't set data for DNS query, unable to shorten layer"); + return false; + } + } + } + + // write data to resource + memcpy(getRawData() + dataOffset, dataAsByteArr, dataLength); + // update data length in resource + dataLength = htobe16((uint16_t)dataLength); + memcpy(getRawData() + dataLengthOffset, &dataLength, sizeof(uint16_t)); + + return true; } -uint16_t DnsResource::getCustomDnsClass() const -{ - uint16_t value = *(uint16_t*)(getRawData() + m_NameLength + sizeof(uint16_t)); - return be16toh(value); +uint16_t DnsResource::getCustomDnsClass() const { + uint16_t value = + *(uint16_t*)(getRawData() + m_NameLength + sizeof(uint16_t)); + return be16toh(value); } -void DnsResource::setCustomDnsClass(uint16_t customValue) -{ - memcpy(getRawData() + m_NameLength + sizeof(uint16_t), &customValue, sizeof(uint16_t)); +void DnsResource::setCustomDnsClass(uint16_t customValue) { + memcpy(getRawData() + m_NameLength + sizeof(uint16_t), &customValue, + sizeof(uint16_t)); } -} +} // namespace pcpp diff --git a/Packet++/src/DnsResourceData.cpp b/Packet++/src/DnsResourceData.cpp index 88b73b373c..b39cf60c38 100644 --- a/Packet++/src/DnsResourceData.cpp +++ b/Packet++/src/DnsResourceData.cpp @@ -1,230 +1,220 @@ #define LOG_MODULE PacketLogModuleDnsLayer #include "DnsResourceData.h" -#include "Logger.h" +#include "EndianPortable.h" #include "GeneralUtils.h" +#include "Logger.h" #include -#include #include -#include "EndianPortable.h" +#include -namespace pcpp -{ - -size_t IDnsResourceData::decodeName(const char* encodedName, char* result, IDnsResource* dnsResource) const -{ - if (dnsResource == nullptr) - { - PCPP_LOG_ERROR("Cannot decode name, DNS resource object is NULL"); - return 0; - } - - return dnsResource->decodeName(encodedName, result); -} +namespace pcpp { -void IDnsResourceData::encodeName(const std::string& decodedName, char* result, size_t& resultLen, IDnsResource* dnsResource) const -{ - if (dnsResource == nullptr) - { - PCPP_LOG_ERROR("Cannot encode name, DNS resource object is NULL"); - return; - } +size_t IDnsResourceData::decodeName(const char* encodedName, char* result, + IDnsResource* dnsResource) const { + if (dnsResource == nullptr) { + PCPP_LOG_ERROR("Cannot decode name, DNS resource object is NULL"); + return 0; + } - dnsResource->encodeName(decodedName, result, resultLen); + return dnsResource->decodeName(encodedName, result); } +void IDnsResourceData::encodeName(const std::string& decodedName, char* result, + size_t& resultLen, + IDnsResource* dnsResource) const { + if (dnsResource == nullptr) { + PCPP_LOG_ERROR("Cannot encode name, DNS resource object is NULL"); + return; + } + + dnsResource->encodeName(decodedName, result, resultLen); +} -StringDnsResourceData::StringDnsResourceData(const uint8_t* dataPtr, size_t dataLen, IDnsResource* dnsResource) -{ - if (dataPtr && dataLen > 0) - { - char tempResult[4096]; - decodeName((const char*)dataPtr, tempResult, dnsResource); - m_Data = tempResult; - } - else - PCPP_LOG_ERROR("Cannot decode name, dataPtr is NULL or length is 0"); +StringDnsResourceData::StringDnsResourceData(const uint8_t* dataPtr, + size_t dataLen, + IDnsResource* dnsResource) { + if (dataPtr && dataLen > 0) { + char tempResult[4096]; + decodeName((const char*)dataPtr, tempResult, dnsResource); + m_Data = tempResult; + } else + PCPP_LOG_ERROR("Cannot decode name, dataPtr is NULL or length is 0"); } -bool StringDnsResourceData::toByteArr(uint8_t* arr, size_t& arrLength, IDnsResource* dnsResource) const -{ - encodeName(m_Data, (char*)arr, arrLength, dnsResource); - return true; +bool StringDnsResourceData::toByteArr(uint8_t* arr, size_t& arrLength, + IDnsResource* dnsResource) const { + encodeName(m_Data, (char*)arr, arrLength, dnsResource); + return true; } -IPv4DnsResourceData::IPv4DnsResourceData(const uint8_t* dataPtr, size_t dataLen) -{ - if (dataLen != 4) - { - PCPP_LOG_ERROR("DNS type is A but resource length is not 4 - malformed data"); - return; - } +IPv4DnsResourceData::IPv4DnsResourceData(const uint8_t* dataPtr, + size_t dataLen) { + if (dataLen != 4) { + PCPP_LOG_ERROR( + "DNS type is A but resource length is not 4 - malformed data"); + return; + } - uint32_t addrAsInt = *(uint32_t*)dataPtr; - m_Data = IPv4Address(addrAsInt); + uint32_t addrAsInt = *(uint32_t*)dataPtr; + m_Data = IPv4Address(addrAsInt); } -bool IPv4DnsResourceData::toByteArr(uint8_t* arr, size_t& arrLength, IDnsResource*) const -{ - if (!m_Data.isValid()) - { - PCPP_LOG_ERROR("Cannot convert IPv4 address to byte array because address is not valid"); - return false; - } - - arrLength = sizeof(uint32_t); - memcpy(arr, m_Data.toBytes(), sizeof(uint32_t)); - return true; +bool IPv4DnsResourceData::toByteArr(uint8_t* arr, size_t& arrLength, + IDnsResource*) const { + if (!m_Data.isValid()) { + PCPP_LOG_ERROR("Cannot convert IPv4 address to byte array because address " + "is not valid"); + return false; + } + + arrLength = sizeof(uint32_t); + memcpy(arr, m_Data.toBytes(), sizeof(uint32_t)); + return true; } -IPv6DnsResourceData::IPv6DnsResourceData(const uint8_t* dataPtr, size_t dataLen) -{ - if (dataLen != 16) - { - PCPP_LOG_ERROR("DNS type is AAAA but resource length is not 16 - malformed data"); - return; - } +IPv6DnsResourceData::IPv6DnsResourceData(const uint8_t* dataPtr, + size_t dataLen) { + if (dataLen != 16) { + PCPP_LOG_ERROR( + "DNS type is AAAA but resource length is not 16 - malformed data"); + return; + } - m_Data = IPv6Address((uint8_t*)dataPtr); + m_Data = IPv6Address((uint8_t*)dataPtr); } -bool IPv6DnsResourceData::toByteArr(uint8_t* arr, size_t& arrLength, IDnsResource*) const -{ - if (!m_Data.isValid()) - { - PCPP_LOG_ERROR("Cannot convert IPv6 address to byte array because address is not valid"); - return false; - } - - arrLength = 16; - m_Data.copyTo(arr); - return true; +bool IPv6DnsResourceData::toByteArr(uint8_t* arr, size_t& arrLength, + IDnsResource*) const { + if (!m_Data.isValid()) { + PCPP_LOG_ERROR("Cannot convert IPv6 address to byte array because address " + "is not valid"); + return false; + } + + arrLength = 16; + m_Data.copyTo(arr); + return true; } -MxDnsResourceData::MxDnsResourceData(uint8_t* dataPtr, size_t dataLen, IDnsResource* dnsResource) -{ - if (dataPtr && dataLen > 0) - { - uint16_t preference = be16toh(*(uint16_t*)dataPtr); - char tempMX[4096]; - decodeName((const char*)(dataPtr + sizeof(preference)), tempMX, dnsResource); - m_Data.preference = preference; - m_Data.mailExchange = tempMX; - } - else - PCPP_LOG_ERROR("Cannot decode name, dataPtr is NULL or length is 0"); +MxDnsResourceData::MxDnsResourceData(uint8_t* dataPtr, size_t dataLen, + IDnsResource* dnsResource) { + if (dataPtr && dataLen > 0) { + uint16_t preference = be16toh(*(uint16_t*)dataPtr); + char tempMX[4096]; + decodeName((const char*)(dataPtr + sizeof(preference)), tempMX, + dnsResource); + m_Data.preference = preference; + m_Data.mailExchange = tempMX; + } else + PCPP_LOG_ERROR("Cannot decode name, dataPtr is NULL or length is 0"); } -MxDnsResourceData::MxDnsResourceData(const uint16_t& preference, const std::string& mailExchange) -{ - m_Data.preference = preference; - m_Data.mailExchange = mailExchange; +MxDnsResourceData::MxDnsResourceData(const uint16_t& preference, + const std::string& mailExchange) { + m_Data.preference = preference; + m_Data.mailExchange = mailExchange; } -bool MxDnsResourceData::operator==(const MxDnsResourceData& other) const -{ - return (m_Data.preference == other.m_Data.preference) && - (m_Data.mailExchange == other.m_Data.mailExchange); +bool MxDnsResourceData::operator==(const MxDnsResourceData& other) const { + return (m_Data.preference == other.m_Data.preference) && + (m_Data.mailExchange == other.m_Data.mailExchange); } -void MxDnsResourceData::setMxData(uint16_t preference, std::string mailExchange) -{ - m_Data.preference = preference; - m_Data.mailExchange = std::move(mailExchange); +void MxDnsResourceData::setMxData(uint16_t preference, + std::string mailExchange) { + m_Data.preference = preference; + m_Data.mailExchange = std::move(mailExchange); } -std::string MxDnsResourceData::toString() const -{ - std::stringstream result; - result << "pref: " << m_Data.preference << "; mx: " << m_Data.mailExchange; - return result.str(); +std::string MxDnsResourceData::toString() const { + std::stringstream result; + result << "pref: " << m_Data.preference << "; mx: " << m_Data.mailExchange; + return result.str(); } -bool MxDnsResourceData::toByteArr(uint8_t* arr, size_t& arrLength, IDnsResource* dnsResource) const -{ - uint16_t netOrderPreference = htobe16(m_Data.preference); - memcpy(arr, &netOrderPreference, sizeof(uint16_t)); - encodeName(m_Data.mailExchange, (char*)(arr + sizeof(uint16_t)), arrLength, dnsResource); - arrLength += sizeof(uint16_t); +bool MxDnsResourceData::toByteArr(uint8_t* arr, size_t& arrLength, + IDnsResource* dnsResource) const { + uint16_t netOrderPreference = htobe16(m_Data.preference); + memcpy(arr, &netOrderPreference, sizeof(uint16_t)); + encodeName(m_Data.mailExchange, (char*)(arr + sizeof(uint16_t)), arrLength, + dnsResource); + arrLength += sizeof(uint16_t); - return true; + return true; } -GenericDnsResourceData::GenericDnsResourceData(const uint8_t* dataPtr, size_t dataLen) -{ - m_Data = nullptr; - m_DataLen = 0; - if (dataLen > 0 && dataPtr != nullptr) - { - m_DataLen = dataLen; - m_Data = new uint8_t[dataLen]; - memcpy(m_Data, dataPtr, dataLen); - } +GenericDnsResourceData::GenericDnsResourceData(const uint8_t* dataPtr, + size_t dataLen) { + m_Data = nullptr; + m_DataLen = 0; + if (dataLen > 0 && dataPtr != nullptr) { + m_DataLen = dataLen; + m_Data = new uint8_t[dataLen]; + memcpy(m_Data, dataPtr, dataLen); + } } -GenericDnsResourceData::GenericDnsResourceData(const std::string& dataAsHexString) -{ - m_Data = nullptr; - uint8_t tempDataArr[2048]; - m_DataLen = hexStringToByteArray(dataAsHexString, tempDataArr, 2048); - if (m_DataLen != 0) - { - m_Data = new uint8_t[m_DataLen]; - memcpy(m_Data, tempDataArr, m_DataLen); - } +GenericDnsResourceData::GenericDnsResourceData( + const std::string& dataAsHexString) { + m_Data = nullptr; + uint8_t tempDataArr[2048]; + m_DataLen = hexStringToByteArray(dataAsHexString, tempDataArr, 2048); + if (m_DataLen != 0) { + m_Data = new uint8_t[m_DataLen]; + memcpy(m_Data, tempDataArr, m_DataLen); + } } -GenericDnsResourceData::GenericDnsResourceData(const GenericDnsResourceData& other) : IDnsResourceData() -{ - m_DataLen = other.m_DataLen; +GenericDnsResourceData::GenericDnsResourceData( + const GenericDnsResourceData& other) + : IDnsResourceData() { + m_DataLen = other.m_DataLen; - if (m_DataLen > 0 && other.m_Data != nullptr) - { - m_Data = new uint8_t[m_DataLen]; - memcpy(m_Data, other.m_Data, m_DataLen); - } + if (m_DataLen > 0 && other.m_Data != nullptr) { + m_Data = new uint8_t[m_DataLen]; + memcpy(m_Data, other.m_Data, m_DataLen); + } } -GenericDnsResourceData& GenericDnsResourceData::operator=(const GenericDnsResourceData& other) -{ - if (m_Data != nullptr) - delete [] m_Data; +GenericDnsResourceData& +GenericDnsResourceData::operator=(const GenericDnsResourceData& other) { + if (m_Data != nullptr) + delete[] m_Data; - m_Data = nullptr; - m_DataLen = other.m_DataLen; - if (m_DataLen > 0 && other.m_Data != nullptr) - { - m_Data = new uint8_t[m_DataLen]; - memcpy(m_Data, other.m_Data, m_DataLen); - } + m_Data = nullptr; + m_DataLen = other.m_DataLen; + if (m_DataLen > 0 && other.m_Data != nullptr) { + m_Data = new uint8_t[m_DataLen]; + memcpy(m_Data, other.m_Data, m_DataLen); + } - return (*this); + return (*this); } -bool GenericDnsResourceData::operator==(const GenericDnsResourceData& other) const -{ - if (m_DataLen != other.m_DataLen) - return false; +bool GenericDnsResourceData::operator==( + const GenericDnsResourceData& other) const { + if (m_DataLen != other.m_DataLen) + return false; - return (memcmp(m_Data, other.m_Data, m_DataLen) == 0); + return (memcmp(m_Data, other.m_Data, m_DataLen) == 0); } -std::string GenericDnsResourceData::toString() const -{ - return byteArrayToHexString(m_Data, m_DataLen); +std::string GenericDnsResourceData::toString() const { + return byteArrayToHexString(m_Data, m_DataLen); } -bool GenericDnsResourceData::toByteArr(uint8_t* arr, size_t& arrLength, IDnsResource*) const -{ - if (m_DataLen == 0 || m_Data == nullptr) - { - PCPP_LOG_ERROR("Input data is null or illegal" << "|m_DataLen:" << m_DataLen); - return false; - } +bool GenericDnsResourceData::toByteArr(uint8_t* arr, size_t& arrLength, + IDnsResource*) const { + if (m_DataLen == 0 || m_Data == nullptr) { + PCPP_LOG_ERROR("Input data is null or illegal" + << "|m_DataLen:" << m_DataLen); + return false; + } - arrLength = m_DataLen; - memcpy(arr, m_Data, m_DataLen); - return true; + arrLength = m_DataLen; + memcpy(arr, m_Data, m_DataLen); + return true; } -} +} // namespace pcpp diff --git a/Packet++/src/EthDot3Layer.cpp b/Packet++/src/EthDot3Layer.cpp index 3f226cba09..378b9eec51 100644 --- a/Packet++/src/EthDot3Layer.cpp +++ b/Packet++/src/EthDot3Layer.cpp @@ -1,62 +1,57 @@ #include "EthDot3Layer.h" #include "EndianPortable.h" -#include "PayloadLayer.h" #include "LLCLayer.h" +#include "PayloadLayer.h" -namespace pcpp -{ - -EthDot3Layer::EthDot3Layer(const MacAddress& sourceMac, const MacAddress& destMac, uint16_t length) : Layer() -{ - const size_t headerLen = sizeof(ether_dot3_header); - m_DataLen = headerLen; - m_Data = new uint8_t[headerLen]; - memset(m_Data, 0, headerLen); - - ether_dot3_header* ethHdr = (ether_dot3_header*)m_Data; - destMac.copyTo(ethHdr->dstMac); - sourceMac.copyTo(ethHdr->srcMac); - ethHdr->length = be16toh(length); - m_Protocol = Ethernet; +namespace pcpp { + +EthDot3Layer::EthDot3Layer(const MacAddress& sourceMac, + const MacAddress& destMac, uint16_t length) + : Layer() { + const size_t headerLen = sizeof(ether_dot3_header); + m_DataLen = headerLen; + m_Data = new uint8_t[headerLen]; + memset(m_Data, 0, headerLen); + + ether_dot3_header* ethHdr = (ether_dot3_header*)m_Data; + destMac.copyTo(ethHdr->dstMac); + sourceMac.copyTo(ethHdr->srcMac); + ethHdr->length = be16toh(length); + m_Protocol = Ethernet; } -void EthDot3Layer::parseNextLayer() -{ - if (m_DataLen <= sizeof(ether_dot3_header)) - return; +void EthDot3Layer::parseNextLayer() { + if (m_DataLen <= sizeof(ether_dot3_header)) + return; - uint8_t* payload = m_Data + sizeof(ether_dot3_header); - size_t payloadLen = m_DataLen - sizeof(ether_dot3_header); + uint8_t* payload = m_Data + sizeof(ether_dot3_header); + size_t payloadLen = m_DataLen - sizeof(ether_dot3_header); - if (LLCLayer::isDataValid(payload, payloadLen)) - m_NextLayer = new LLCLayer(payload, payloadLen, this, m_Packet); - else - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + if (LLCLayer::isDataValid(payload, payloadLen)) + m_NextLayer = new LLCLayer(payload, payloadLen, this, m_Packet); + else + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); } -std::string EthDot3Layer::toString() const -{ - return "IEEE 802.3 Ethernet, Src: " + getSourceMac().toString() + ", Dst: " + getDestMac().toString(); +std::string EthDot3Layer::toString() const { + return "IEEE 802.3 Ethernet, Src: " + getSourceMac().toString() + + ", Dst: " + getDestMac().toString(); } -bool EthDot3Layer::isDataValid(const uint8_t* data, size_t dataLen) -{ - if (dataLen >= sizeof(ether_dot3_header)) - { - /** - * LSAPs: ... Such a length must, when considered as an - * unsigned integer, be less than 0x5DC or it could be mistaken as - * an Ethertype... - * - * From: https://tools.ietf.org/html/rfc5342#section-2.3.2.1 - * More: IEEE Std 802.3 Clause 3.2.6 - */ - return be16toh(*(uint16_t*)(data + 12)) <= (uint16_t)0x05DC; - } - else - { - return false; - } +bool EthDot3Layer::isDataValid(const uint8_t* data, size_t dataLen) { + if (dataLen >= sizeof(ether_dot3_header)) { + /** + * LSAPs: ... Such a length must, when considered as an + * unsigned integer, be less than 0x5DC or it could be mistaken as + * an Ethertype... + * + * From: https://tools.ietf.org/html/rfc5342#section-2.3.2.1 + * More: IEEE Std 802.3 Clause 3.2.6 + */ + return be16toh(*(uint16_t*)(data + 12)) <= (uint16_t)0x05DC; + } else { + return false; + } } } // namespace pcpp diff --git a/Packet++/src/EthLayer.cpp b/Packet++/src/EthLayer.cpp index f0b60e3585..cdf4323cce 100644 --- a/Packet++/src/EthLayer.cpp +++ b/Packet++/src/EthLayer.cpp @@ -1,134 +1,138 @@ #define LOG_MODULE PacketLogModuleEthLayer #include "EthLayer.h" +#include "ArpLayer.h" +#include "EndianPortable.h" #include "IPv4Layer.h" #include "IPv6Layer.h" +#include "MplsLayer.h" +#include "PPPoELayer.h" #include "PayloadLayer.h" -#include "ArpLayer.h" #include "VlanLayer.h" -#include "PPPoELayer.h" -#include "MplsLayer.h" #include "WakeOnLanLayer.h" -#include "EndianPortable.h" #include -namespace pcpp -{ - +namespace pcpp { -EthLayer::EthLayer(const MacAddress& sourceMac, const MacAddress& destMac, uint16_t etherType) : Layer() -{ - const size_t headerLen = sizeof(ether_header); - m_DataLen = headerLen; - m_Data = new uint8_t[headerLen]; - memset(m_Data, 0, headerLen); +EthLayer::EthLayer(const MacAddress& sourceMac, const MacAddress& destMac, + uint16_t etherType) + : Layer() { + const size_t headerLen = sizeof(ether_header); + m_DataLen = headerLen; + m_Data = new uint8_t[headerLen]; + memset(m_Data, 0, headerLen); - ether_header* ethHdr = (ether_header*)m_Data; - destMac.copyTo(ethHdr->dstMac); - sourceMac.copyTo(ethHdr->srcMac); - ethHdr->etherType = htobe16(etherType); - m_Protocol = Ethernet; + ether_header* ethHdr = (ether_header*)m_Data; + destMac.copyTo(ethHdr->dstMac); + sourceMac.copyTo(ethHdr->srcMac); + ethHdr->etherType = htobe16(etherType); + m_Protocol = Ethernet; } -void EthLayer::parseNextLayer() -{ - if (m_DataLen <= sizeof(ether_header)) - return; +void EthLayer::parseNextLayer() { + if (m_DataLen <= sizeof(ether_header)) + return; - ether_header* hdr = getEthHeader(); - uint8_t* payload = m_Data + sizeof(ether_header); - size_t payloadLen = m_DataLen - sizeof(ether_header); + ether_header* hdr = getEthHeader(); + uint8_t* payload = m_Data + sizeof(ether_header); + size_t payloadLen = m_DataLen - sizeof(ether_header); - switch (be16toh(hdr->etherType)) - { - case PCPP_ETHERTYPE_IP: - m_NextLayer = IPv4Layer::isDataValid(payload, payloadLen) - ? static_cast(new IPv4Layer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case PCPP_ETHERTYPE_IPV6: - m_NextLayer = IPv6Layer::isDataValid(payload, payloadLen) - ? static_cast(new IPv6Layer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case PCPP_ETHERTYPE_ARP: - m_NextLayer = new ArpLayer(payload, payloadLen, this, m_Packet); - break; - case PCPP_ETHERTYPE_VLAN: - case PCPP_ETHERTYPE_IEEE_802_1AD: - m_NextLayer = new VlanLayer(payload, payloadLen, this, m_Packet); - break; - case PCPP_ETHERTYPE_PPPOES: - m_NextLayer = PPPoESessionLayer::isDataValid(payload, payloadLen) - ? static_cast(new PPPoESessionLayer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case PCPP_ETHERTYPE_PPPOED: - m_NextLayer = PPPoEDiscoveryLayer::isDataValid(payload, payloadLen) - ? static_cast(new PPPoEDiscoveryLayer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case PCPP_ETHERTYPE_MPLS: - m_NextLayer = new MplsLayer(payload, payloadLen, this, m_Packet); - break; - case PCPP_ETHERTYPE_WAKE_ON_LAN: - m_NextLayer = WakeOnLanLayer::isDataValid(payload, payloadLen) - ? static_cast(new WakeOnLanLayer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - default: - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - } + switch (be16toh(hdr->etherType)) { + case PCPP_ETHERTYPE_IP: + m_NextLayer = IPv4Layer::isDataValid(payload, payloadLen) + ? static_cast( + new IPv4Layer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer( + payload, payloadLen, this, m_Packet)); + break; + case PCPP_ETHERTYPE_IPV6: + m_NextLayer = IPv6Layer::isDataValid(payload, payloadLen) + ? static_cast( + new IPv6Layer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer( + payload, payloadLen, this, m_Packet)); + break; + case PCPP_ETHERTYPE_ARP: + m_NextLayer = new ArpLayer(payload, payloadLen, this, m_Packet); + break; + case PCPP_ETHERTYPE_VLAN: + case PCPP_ETHERTYPE_IEEE_802_1AD: + m_NextLayer = new VlanLayer(payload, payloadLen, this, m_Packet); + break; + case PCPP_ETHERTYPE_PPPOES: + m_NextLayer = + PPPoESessionLayer::isDataValid(payload, payloadLen) + ? static_cast( + new PPPoESessionLayer(payload, payloadLen, this, m_Packet)) + : static_cast( + new PayloadLayer(payload, payloadLen, this, m_Packet)); + break; + case PCPP_ETHERTYPE_PPPOED: + m_NextLayer = + PPPoEDiscoveryLayer::isDataValid(payload, payloadLen) + ? static_cast( + new PPPoEDiscoveryLayer(payload, payloadLen, this, m_Packet)) + : static_cast( + new PayloadLayer(payload, payloadLen, this, m_Packet)); + break; + case PCPP_ETHERTYPE_MPLS: + m_NextLayer = new MplsLayer(payload, payloadLen, this, m_Packet); + break; + case PCPP_ETHERTYPE_WAKE_ON_LAN: + m_NextLayer = + WakeOnLanLayer::isDataValid(payload, payloadLen) + ? static_cast( + new WakeOnLanLayer(payload, payloadLen, this, m_Packet)) + : static_cast( + new PayloadLayer(payload, payloadLen, this, m_Packet)); + break; + default: + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + } } -void EthLayer::computeCalculateFields() -{ - if (m_NextLayer == nullptr) - return; +void EthLayer::computeCalculateFields() { + if (m_NextLayer == nullptr) + return; - switch (m_NextLayer->getProtocol()) - { - case IPv4: - getEthHeader()->etherType = htobe16(PCPP_ETHERTYPE_IP); - break; - case IPv6: - getEthHeader()->etherType = htobe16(PCPP_ETHERTYPE_IPV6); - break; - case ARP: - getEthHeader()->etherType = htobe16(PCPP_ETHERTYPE_ARP); - break; - case VLAN: - getEthHeader()->etherType = htobe16(PCPP_ETHERTYPE_VLAN); - break; - default: - return; - } + switch (m_NextLayer->getProtocol()) { + case IPv4: + getEthHeader()->etherType = htobe16(PCPP_ETHERTYPE_IP); + break; + case IPv6: + getEthHeader()->etherType = htobe16(PCPP_ETHERTYPE_IPV6); + break; + case ARP: + getEthHeader()->etherType = htobe16(PCPP_ETHERTYPE_ARP); + break; + case VLAN: + getEthHeader()->etherType = htobe16(PCPP_ETHERTYPE_VLAN); + break; + default: + return; + } } -std::string EthLayer::toString() const -{ - return "Ethernet II Layer, Src: " + getSourceMac().toString() + ", Dst: " + getDestMac().toString(); +std::string EthLayer::toString() const { + return "Ethernet II Layer, Src: " + getSourceMac().toString() + + ", Dst: " + getDestMac().toString(); } -bool EthLayer::isDataValid(const uint8_t* data, size_t dataLen) -{ - if (dataLen >= sizeof(ether_header)) - { - /** - * Ethertypes: These are 16-bit identifiers appearing as the initial - * two octets after the MAC destination and source (or after a - * tag) which, when considered as an unsigned integer, are equal - * to or larger than 0x0600. - * - * From: https://tools.ietf.org/html/rfc5342#section-2.3.2.1 - * More: IEEE Std 802.3 Clause 3.2.6 - */ - return be16toh(*(uint16_t*)(data + 12)) >= (uint16_t)0x0600; - } - else - { - return false; - } +bool EthLayer::isDataValid(const uint8_t* data, size_t dataLen) { + if (dataLen >= sizeof(ether_header)) { + /** + * Ethertypes: These are 16-bit identifiers appearing as the initial + * two octets after the MAC destination and source (or after a + * tag) which, when considered as an unsigned integer, are equal + * to or larger than 0x0600. + * + * From: https://tools.ietf.org/html/rfc5342#section-2.3.2.1 + * More: IEEE Std 802.3 Clause 3.2.6 + */ + return be16toh(*(uint16_t*)(data + 12)) >= (uint16_t)0x0600; + } else { + return false; + } } } // namespace pcpp diff --git a/Packet++/src/FtpLayer.cpp b/Packet++/src/FtpLayer.cpp index 01e9055889..2a526bd92c 100644 --- a/Packet++/src/FtpLayer.cpp +++ b/Packet++/src/FtpLayer.cpp @@ -2,392 +2,380 @@ #include "FtpLayer.h" -namespace pcpp -{ +namespace pcpp { - // ----------------- Class FtpRequestLayer ----------------- - bool FtpRequestLayer::setCommand(FtpCommand code) - { - return setCommandInternal(getCommandAsString(code)); - } +// ----------------- Class FtpRequestLayer ----------------- +bool FtpRequestLayer::setCommand(FtpCommand code) { + return setCommandInternal(getCommandAsString(code)); +} - FtpRequestLayer::FtpCommand FtpRequestLayer::getCommand() const - { - size_t val = 0; - std::string field = getCommandString(); +FtpRequestLayer::FtpCommand FtpRequestLayer::getCommand() const { + size_t val = 0; + std::string field = getCommandString(); - for (size_t idx = 0; idx < field.size(); ++idx) - val |= (field.c_str()[idx] << (idx * 8)); + for (size_t idx = 0; idx < field.size(); ++idx) + val |= (field.c_str()[idx] << (idx * 8)); - return static_cast(val); - } + return static_cast(val); +} - std::string FtpRequestLayer::getCommandString() const - { - return getCommandInternal(); - } +std::string FtpRequestLayer::getCommandString() const { + return getCommandInternal(); +} - bool FtpRequestLayer::setCommandOption(const std::string &value) - { - return setCommandOptionInternal(value); - } +bool FtpRequestLayer::setCommandOption(const std::string& value) { + return setCommandOptionInternal(value); +} - std::string FtpRequestLayer::getCommandOption(bool removeEscapeCharacters) const - { - if (removeEscapeCharacters) - { - std::stringstream ss; - std::string field = getCommandOptionInternal(); - for (size_t idx = 0; idx < field.size(); ++idx) - { - if (int(field.c_str()[idx]) < 127 && int(field.c_str()[idx]) > 31) // From SPACE to ~ - ss << field.c_str()[idx]; - } - return ss.str(); - } - return getCommandOptionInternal(); - } +std::string +FtpRequestLayer::getCommandOption(bool removeEscapeCharacters) const { + if (removeEscapeCharacters) { + std::stringstream ss; + std::string field = getCommandOptionInternal(); + for (size_t idx = 0; idx < field.size(); ++idx) { + if (int(field.c_str()[idx]) < 127 && + int(field.c_str()[idx]) > 31) // From SPACE to ~ + ss << field.c_str()[idx]; + } + return ss.str(); + } + return getCommandOptionInternal(); +} - std::string FtpRequestLayer::getCommandInfo(FtpCommand code) - { - switch (code) - { - case FtpCommand::ABOR: - return "Abort an active file transfer"; - case FtpCommand::ACCT: - return "Account information"; - case FtpCommand::ADAT: - return "Authentication/Security Data"; - case FtpCommand::ALLO: - return "Allocate sufficient disk space to receive a file"; - case FtpCommand::APPE: - return "Append (with create)"; - case FtpCommand::AUTH: - return "Authentication/Security Mechanism"; - case FtpCommand::AVBL: - return "Get the available space"; - case FtpCommand::CCC: - return "Clear Command Channel"; - case FtpCommand::CDUP: - return "Change to Parent Directory"; - case FtpCommand::CONF: - return "Confidentiality Protection Command"; - case FtpCommand::CSID: - return "Client / Server Identification"; - case FtpCommand::CWD: - return "Change working directory"; - case FtpCommand::DELE: - return "Delete file"; - case FtpCommand::DSIZ: - return "Get the directory size"; - case FtpCommand::ENC: - return "Privacy Protected Channel"; - case FtpCommand::EPRT: - return "Specifies an extended address and port to which the server should connect"; - case FtpCommand::EPSV: - return "Enter extended passive mode"; - case FtpCommand::FEAT: - return "Get the feature list implemented by the server"; - case FtpCommand::HELP: - return "Returns usage documentation on a command if specified, else a general help document is returned"; - case FtpCommand::HOST: - return "Identify desired virtual host on server, by name"; - case FtpCommand::LANG: - return "Language Negotiation"; - case FtpCommand::LIST: - return "Returns information of a file or directory if specified, else information of the current working " - "directory is returned"; - case FtpCommand::LPRT: - return "Specifies a long address and port to which the server should connect"; - case FtpCommand::LPSV: - return "Enter long passive mode"; - case FtpCommand::MDTM: - return "Return the last-modified time of a specified file"; - case FtpCommand::MFCT: - return "Modify the creation time of a file"; - case FtpCommand::MFF: - return "Modify fact (the last modification time, creation time, UNIX group/owner/mode of a file)"; - case FtpCommand::MFMT: - return "Modify the last modification time of a file"; - case FtpCommand::MIC: - return "Integrity Protected Command"; - case FtpCommand::MKD: - return "Make directory"; - case FtpCommand::MLSD: - return "Lists the contents of a directory in a standardized machine-readable format"; - case FtpCommand::MLST: - return "Provides data about exactly the object named on its command line in a standardized " - "machine-readable format"; - case FtpCommand::MODE: - return "Sets the transfer mode (Stream, Block, or Compressed)"; - case FtpCommand::NLST: - return "Returns a list of file names in a specified directory"; - case FtpCommand::NOOP: - return "No operation (dummy packet; used mostly on keepalives)"; - case FtpCommand::OPTS: - return "Select options for a feature (for example OPTS UTF8 ON)"; - case FtpCommand::PASS: - return "Authentication password"; - case FtpCommand::PASV: - return "Enter passive mode"; - case FtpCommand::PBSZ: - return "Protection Buffer Size"; - case FtpCommand::PORT: - return "Specifies an address and port to which the server should connect"; - case FtpCommand::PROT: - return "Data Channel Protection Level"; - case FtpCommand::PWD: - return "Print working directory. Returns the current directory of the host"; - case FtpCommand::QUIT: - return "Disconnect"; - case FtpCommand::REIN: - return "Re initializes the connection"; - case FtpCommand::REST: - return "Restart transfer from the specified point"; - case FtpCommand::RETR: - return "Retrieve a copy of the file"; - case FtpCommand::RMD: - return "Remove a directory"; - case FtpCommand::RMDA: - return "Remove a directory tree"; - case FtpCommand::RNFR: - return "Rename from"; - case FtpCommand::RNTO: - return "Rename to"; - case FtpCommand::SITE: - return "Sends site specific commands to remote server (like SITE IDLE 60 or SITE UMASK 002). Inspect SITE " - "HELP output for complete list of supported commands"; - case FtpCommand::SIZE: - return "Return the size of a file"; - case FtpCommand::SMNT: - return "Mount file structure"; - case FtpCommand::SPSV: - return "Use single port passive mode (only one TCP port number for both control connections and " - "passive-mode data connections)"; - case FtpCommand::STAT: - return "Returns information on the server status, including the status of the current connection"; - case FtpCommand::STOR: - return "Accept the data and to store the data as a file at the server site"; - case FtpCommand::STOU: - return "Store file uniquely"; - case FtpCommand::STRU: - return "Set file transfer structure"; - case FtpCommand::SYST: - return "Return system type"; - case FtpCommand::THMB: - return "Get a thumbnail of a remote image file"; - case FtpCommand::TYPE: - return "Sets the transfer mode (ASCII/Binary)"; - case FtpCommand::USER: - return "Authentication username"; - case FtpCommand::XCUP: - return "Change to the parent of the current working directory"; - case FtpCommand::XMKD: - return "Make a directory"; - case FtpCommand::XPWD: - return "Print the current working directory"; - case FtpCommand::XRCP: - return ""; - case FtpCommand::XRMD: - return "Remove the directory"; - case FtpCommand::XRSQ: - return ""; - case FtpCommand::XSEM: - return "Send, mail if cannot"; - case FtpCommand::XSEN: - return "Send to terminal"; - default: - return "Unknown command"; - } - } +std::string FtpRequestLayer::getCommandInfo(FtpCommand code) { + switch (code) { + case FtpCommand::ABOR: + return "Abort an active file transfer"; + case FtpCommand::ACCT: + return "Account information"; + case FtpCommand::ADAT: + return "Authentication/Security Data"; + case FtpCommand::ALLO: + return "Allocate sufficient disk space to receive a file"; + case FtpCommand::APPE: + return "Append (with create)"; + case FtpCommand::AUTH: + return "Authentication/Security Mechanism"; + case FtpCommand::AVBL: + return "Get the available space"; + case FtpCommand::CCC: + return "Clear Command Channel"; + case FtpCommand::CDUP: + return "Change to Parent Directory"; + case FtpCommand::CONF: + return "Confidentiality Protection Command"; + case FtpCommand::CSID: + return "Client / Server Identification"; + case FtpCommand::CWD: + return "Change working directory"; + case FtpCommand::DELE: + return "Delete file"; + case FtpCommand::DSIZ: + return "Get the directory size"; + case FtpCommand::ENC: + return "Privacy Protected Channel"; + case FtpCommand::EPRT: + return "Specifies an extended address and port to which the server should " + "connect"; + case FtpCommand::EPSV: + return "Enter extended passive mode"; + case FtpCommand::FEAT: + return "Get the feature list implemented by the server"; + case FtpCommand::HELP: + return "Returns usage documentation on a command if specified, else a " + "general help document is returned"; + case FtpCommand::HOST: + return "Identify desired virtual host on server, by name"; + case FtpCommand::LANG: + return "Language Negotiation"; + case FtpCommand::LIST: + return "Returns information of a file or directory if specified, else " + "information of the current working " + "directory is returned"; + case FtpCommand::LPRT: + return "Specifies a long address and port to which the server should " + "connect"; + case FtpCommand::LPSV: + return "Enter long passive mode"; + case FtpCommand::MDTM: + return "Return the last-modified time of a specified file"; + case FtpCommand::MFCT: + return "Modify the creation time of a file"; + case FtpCommand::MFF: + return "Modify fact (the last modification time, creation time, UNIX " + "group/owner/mode of a file)"; + case FtpCommand::MFMT: + return "Modify the last modification time of a file"; + case FtpCommand::MIC: + return "Integrity Protected Command"; + case FtpCommand::MKD: + return "Make directory"; + case FtpCommand::MLSD: + return "Lists the contents of a directory in a standardized " + "machine-readable format"; + case FtpCommand::MLST: + return "Provides data about exactly the object named on its command line " + "in a standardized " + "machine-readable format"; + case FtpCommand::MODE: + return "Sets the transfer mode (Stream, Block, or Compressed)"; + case FtpCommand::NLST: + return "Returns a list of file names in a specified directory"; + case FtpCommand::NOOP: + return "No operation (dummy packet; used mostly on keepalives)"; + case FtpCommand::OPTS: + return "Select options for a feature (for example OPTS UTF8 ON)"; + case FtpCommand::PASS: + return "Authentication password"; + case FtpCommand::PASV: + return "Enter passive mode"; + case FtpCommand::PBSZ: + return "Protection Buffer Size"; + case FtpCommand::PORT: + return "Specifies an address and port to which the server should connect"; + case FtpCommand::PROT: + return "Data Channel Protection Level"; + case FtpCommand::PWD: + return "Print working directory. Returns the current directory of the host"; + case FtpCommand::QUIT: + return "Disconnect"; + case FtpCommand::REIN: + return "Re initializes the connection"; + case FtpCommand::REST: + return "Restart transfer from the specified point"; + case FtpCommand::RETR: + return "Retrieve a copy of the file"; + case FtpCommand::RMD: + return "Remove a directory"; + case FtpCommand::RMDA: + return "Remove a directory tree"; + case FtpCommand::RNFR: + return "Rename from"; + case FtpCommand::RNTO: + return "Rename to"; + case FtpCommand::SITE: + return "Sends site specific commands to remote server (like SITE IDLE 60 " + "or SITE UMASK 002). Inspect SITE " + "HELP output for complete list of supported commands"; + case FtpCommand::SIZE: + return "Return the size of a file"; + case FtpCommand::SMNT: + return "Mount file structure"; + case FtpCommand::SPSV: + return "Use single port passive mode (only one TCP port number for both " + "control connections and " + "passive-mode data connections)"; + case FtpCommand::STAT: + return "Returns information on the server status, including the status of " + "the current connection"; + case FtpCommand::STOR: + return "Accept the data and to store the data as a file at the server site"; + case FtpCommand::STOU: + return "Store file uniquely"; + case FtpCommand::STRU: + return "Set file transfer structure"; + case FtpCommand::SYST: + return "Return system type"; + case FtpCommand::THMB: + return "Get a thumbnail of a remote image file"; + case FtpCommand::TYPE: + return "Sets the transfer mode (ASCII/Binary)"; + case FtpCommand::USER: + return "Authentication username"; + case FtpCommand::XCUP: + return "Change to the parent of the current working directory"; + case FtpCommand::XMKD: + return "Make a directory"; + case FtpCommand::XPWD: + return "Print the current working directory"; + case FtpCommand::XRCP: + return ""; + case FtpCommand::XRMD: + return "Remove the directory"; + case FtpCommand::XRSQ: + return ""; + case FtpCommand::XSEM: + return "Send, mail if cannot"; + case FtpCommand::XSEN: + return "Send to terminal"; + default: + return "Unknown command"; + } +} - std::string FtpRequestLayer::getCommandAsString(FtpCommand code) - { - std::stringstream oss; - for (size_t idx = 0; idx < 4; ++idx) - { - char val = (uint64_t(code) >> (8 * idx)) & UINT8_MAX; - if (val) // Dont push if it is a null character - { - oss << val; - } - } - return oss.str(); - } +std::string FtpRequestLayer::getCommandAsString(FtpCommand code) { + std::stringstream oss; + for (size_t idx = 0; idx < 4; ++idx) { + char val = (uint64_t(code) >> (8 * idx)) & UINT8_MAX; + if (val) // Dont push if it is a null character + { + oss << val; + } + } + return oss.str(); +} - std::string FtpRequestLayer::toString() const - { - return "FTP Request: " + getCommandString(); - } +std::string FtpRequestLayer::toString() const { + return "FTP Request: " + getCommandString(); +} - // ----------------- Class FtpResponseLayer ----------------- - bool FtpResponseLayer::setStatusCode(FtpStatusCode code) - { - std::ostringstream oss; - oss << int(code); - return setCommandInternal(oss.str()); - } +// ----------------- Class FtpResponseLayer ----------------- +bool FtpResponseLayer::setStatusCode(FtpStatusCode code) { + std::ostringstream oss; + oss << int(code); + return setCommandInternal(oss.str()); +} - FtpResponseLayer::FtpStatusCode FtpResponseLayer::getStatusCode() const - { - return static_cast(atoi(getCommandInternal().c_str())); - } +FtpResponseLayer::FtpStatusCode FtpResponseLayer::getStatusCode() const { + return static_cast(atoi(getCommandInternal().c_str())); +} - std::string FtpResponseLayer::getStatusCodeString() const - { - return getCommandInternal(); - } +std::string FtpResponseLayer::getStatusCodeString() const { + return getCommandInternal(); +} - bool FtpResponseLayer::setStatusOption(const std::string &value) - { - return setCommandOptionInternal(value); - } +bool FtpResponseLayer::setStatusOption(const std::string& value) { + return setCommandOptionInternal(value); +} - std::string FtpResponseLayer::getStatusOption(bool removeEscapeCharacters) const - { - if (removeEscapeCharacters) - { - std::stringstream ss; - std::string field = getCommandOptionInternal(); - for (size_t idx = 0; idx < field.size(); ++idx) - { - if (int(field.c_str()[idx]) < 127 && int(field.c_str()[idx]) > 31) // From SPACE to ~ - ss << field.c_str()[idx]; - } - return ss.str(); - } - return getCommandOptionInternal(); - } +std::string +FtpResponseLayer::getStatusOption(bool removeEscapeCharacters) const { + if (removeEscapeCharacters) { + std::stringstream ss; + std::string field = getCommandOptionInternal(); + for (size_t idx = 0; idx < field.size(); ++idx) { + if (int(field.c_str()[idx]) < 127 && + int(field.c_str()[idx]) > 31) // From SPACE to ~ + ss << field.c_str()[idx]; + } + return ss.str(); + } + return getCommandOptionInternal(); +} - std::string FtpResponseLayer::getStatusCodeAsString(FtpStatusCode code) - { - switch (code) - { - case FtpStatusCode::RESTART_MARKER: - return "Restart marker reply"; - case FtpStatusCode::SERVICE_READY_IN_MIN: - return "Service ready in nnn minutes"; - case FtpStatusCode::DATA_ALREADY_OPEN_START_TRANSFER: - return "Data connection already open; transfer starting"; - case FtpStatusCode::FILE_OK: - return "File status okay; about to open data connection"; - case FtpStatusCode::COMMAND_OK: - return "Command okay"; - case FtpStatusCode::COMMAND_NOT_IMPLEMENTED_SUPERFLUOUS: - return "Command not implemented, superfluous at this site"; - case FtpStatusCode::SYSTEM_STATUS: - return "System status, or system help reply"; - case FtpStatusCode::DIR_STATUS: - return "Directory status"; - case FtpStatusCode::FILE_STATUS: - return "File status"; - case FtpStatusCode::HELP_MESSAGE: - return "Help message"; - case FtpStatusCode::NAME_SYSTEM_TYPE: - return "NAME system type"; - case FtpStatusCode::SERVICE_READY_FOR_USER: - return "Service ready for new user"; - case FtpStatusCode::SERVICE_CLOSING_CONTROL: - return "Service closing control connection"; - case FtpStatusCode::DATA_OPEN_NO_TRANSFER: - return "Data connection open; no transfer in progress"; - case FtpStatusCode::CLOSING_DATA: - return "Closing data connection"; - case FtpStatusCode::ENTERING_PASSIVE: - return "Entering Passive Mode"; - case FtpStatusCode::ENTERING_EXTENDED_PASSIVE: - return "Entering Extended Passive Mode"; - case FtpStatusCode::USER_LOG_IN_PROCEED: - return "User logged in, proceed"; - case FtpStatusCode::USER_LOG_IN_AUTHORIZED: - return "User logged in, authorized by security data exchange"; - case FtpStatusCode::SEC_DATA_EXCHANGE_COMPLETE: - return "Security data exchange complete"; - case FtpStatusCode::SEC_DATA_EXCHANGE_COMPLETE_SUCCESS: - return "Security data exchange completed successfully"; - case FtpStatusCode::REQ_FILE_OK_COMPLETE: - return "Requested file action okay, completed"; - case FtpStatusCode::PATHNAME_CREATED: - return "PATHNAME created"; - case FtpStatusCode::USER_OK_NEED_PASSWORD: - return "User name okay, need password"; - case FtpStatusCode::NEED_ACCOUNT: - return "Need account for login"; - case FtpStatusCode::REQ_SEC_MECHANISM_OK: - return "Requested security mechanism is ok"; - case FtpStatusCode::SEC_IS_ACCEPTABLE: - return "Security data is acceptable, more is required"; - case FtpStatusCode::USER_OK_NEED_PASS_CHALLENGE: - return "Username okay, need password. Challenge is ..."; - case FtpStatusCode::FILE_PENDING_ACTION: - return "Requested file action pending further information"; - case FtpStatusCode::SERVICE_NOT_AVAILABLE: - return "Service not available, closing control connection"; - case FtpStatusCode::CANT_OPEN_DATA_CONNECTION: - return "Can't open data connection"; - case FtpStatusCode::CONNECTION_CLOSED: - return "Connection closed; transfer aborted"; - case FtpStatusCode::NEED_UNAVAILABLE_RESOURCE_TO_SEC: - return "Need some unavailable resource to process security"; - case FtpStatusCode::REQ_FILE_ACTION_NOT_TAKEN: - return "Requested file action not taken"; - case FtpStatusCode::REQ_ACTION_ABORTED: - return "Requested action aborted: local error in processing"; - case FtpStatusCode::REQ_ACTION_NOT_TAKEN: - return "Requested action not taken. Insufficient storage space in system"; - case FtpStatusCode::SYNTAX_ERROR_COMMAND_UNRECOGNIZED: - return "Syntax error, command unrecognized"; - case FtpStatusCode::SYNTAX_ERROR_PARAMETER_OR_ARGUMENT: - return "Syntax error in parameters or arguments"; - case FtpStatusCode::COMMAND_NOT_IMPLEMENTED: - return "Command not implemented"; - case FtpStatusCode::BAD_SEQUENCE_COMMANDS: - return "Bad sequence of commands"; - case FtpStatusCode::COMMAND_NOT_IMPLEMENTED_FOR_PARAMETER: - return "Command not implemented for that parameter"; - case FtpStatusCode::NETWORK_PROTOCOL_NOT_SUPPORTED: - return "Network protocol not supported"; - case FtpStatusCode::NOT_LOGGED_IN: - return "Not logged in"; - case FtpStatusCode::NEED_ACCOUNT_FOR_STORE_FILE: - return "Need account for storing files"; - case FtpStatusCode::COMMAND_PROTECTION_DENIED: - return "Command protection level denied for policy reasons"; - case FtpStatusCode::REQUEST_DENIED: - return "Request denied for policy reasons"; - case FtpStatusCode::FAILED_SEC_CHECK: - return "Failed security check (hash, sequence, etc)"; - case FtpStatusCode::REQ_PROT_LEVEL_NOT_SUPPORTED: - return "Requested PROT level not supported by mechanism"; - case FtpStatusCode::COMMAND_PROTECTION_LEVEL_NOT_SUPPORTED: - return "Command protection level not supported by security mechanism"; - case FtpStatusCode::FILE_UNAVAILABLE: - return "Requested action not taken: File unavailable"; - case FtpStatusCode::PAGE_TYPE_UNKNOWN: - return "Requested action aborted: page type unknown"; - case FtpStatusCode::EXCEED_STORAGE_ALLOCATION: - return "Requested file action aborted: Exceeded storage allocation"; - case FtpStatusCode::FILENAME_NOT_ALLOWED: - return "Requested action not taken: File name not allowed"; - case FtpStatusCode::INTEGRITY_PROTECTED: - return "Integrity protected reply"; - case FtpStatusCode::CONFIDENTIALITY_AND_INTEGRITY_PROTECTED: - return "Confidentiality and integrity protected reply"; - case FtpStatusCode::CONFIDENTIALITY_PROTECTED: - return "Confidentiality protected reply"; - default: - return "Unknown Status Code"; - } - } +std::string FtpResponseLayer::getStatusCodeAsString(FtpStatusCode code) { + switch (code) { + case FtpStatusCode::RESTART_MARKER: + return "Restart marker reply"; + case FtpStatusCode::SERVICE_READY_IN_MIN: + return "Service ready in nnn minutes"; + case FtpStatusCode::DATA_ALREADY_OPEN_START_TRANSFER: + return "Data connection already open; transfer starting"; + case FtpStatusCode::FILE_OK: + return "File status okay; about to open data connection"; + case FtpStatusCode::COMMAND_OK: + return "Command okay"; + case FtpStatusCode::COMMAND_NOT_IMPLEMENTED_SUPERFLUOUS: + return "Command not implemented, superfluous at this site"; + case FtpStatusCode::SYSTEM_STATUS: + return "System status, or system help reply"; + case FtpStatusCode::DIR_STATUS: + return "Directory status"; + case FtpStatusCode::FILE_STATUS: + return "File status"; + case FtpStatusCode::HELP_MESSAGE: + return "Help message"; + case FtpStatusCode::NAME_SYSTEM_TYPE: + return "NAME system type"; + case FtpStatusCode::SERVICE_READY_FOR_USER: + return "Service ready for new user"; + case FtpStatusCode::SERVICE_CLOSING_CONTROL: + return "Service closing control connection"; + case FtpStatusCode::DATA_OPEN_NO_TRANSFER: + return "Data connection open; no transfer in progress"; + case FtpStatusCode::CLOSING_DATA: + return "Closing data connection"; + case FtpStatusCode::ENTERING_PASSIVE: + return "Entering Passive Mode"; + case FtpStatusCode::ENTERING_EXTENDED_PASSIVE: + return "Entering Extended Passive Mode"; + case FtpStatusCode::USER_LOG_IN_PROCEED: + return "User logged in, proceed"; + case FtpStatusCode::USER_LOG_IN_AUTHORIZED: + return "User logged in, authorized by security data exchange"; + case FtpStatusCode::SEC_DATA_EXCHANGE_COMPLETE: + return "Security data exchange complete"; + case FtpStatusCode::SEC_DATA_EXCHANGE_COMPLETE_SUCCESS: + return "Security data exchange completed successfully"; + case FtpStatusCode::REQ_FILE_OK_COMPLETE: + return "Requested file action okay, completed"; + case FtpStatusCode::PATHNAME_CREATED: + return "PATHNAME created"; + case FtpStatusCode::USER_OK_NEED_PASSWORD: + return "User name okay, need password"; + case FtpStatusCode::NEED_ACCOUNT: + return "Need account for login"; + case FtpStatusCode::REQ_SEC_MECHANISM_OK: + return "Requested security mechanism is ok"; + case FtpStatusCode::SEC_IS_ACCEPTABLE: + return "Security data is acceptable, more is required"; + case FtpStatusCode::USER_OK_NEED_PASS_CHALLENGE: + return "Username okay, need password. Challenge is ..."; + case FtpStatusCode::FILE_PENDING_ACTION: + return "Requested file action pending further information"; + case FtpStatusCode::SERVICE_NOT_AVAILABLE: + return "Service not available, closing control connection"; + case FtpStatusCode::CANT_OPEN_DATA_CONNECTION: + return "Can't open data connection"; + case FtpStatusCode::CONNECTION_CLOSED: + return "Connection closed; transfer aborted"; + case FtpStatusCode::NEED_UNAVAILABLE_RESOURCE_TO_SEC: + return "Need some unavailable resource to process security"; + case FtpStatusCode::REQ_FILE_ACTION_NOT_TAKEN: + return "Requested file action not taken"; + case FtpStatusCode::REQ_ACTION_ABORTED: + return "Requested action aborted: local error in processing"; + case FtpStatusCode::REQ_ACTION_NOT_TAKEN: + return "Requested action not taken. Insufficient storage space in system"; + case FtpStatusCode::SYNTAX_ERROR_COMMAND_UNRECOGNIZED: + return "Syntax error, command unrecognized"; + case FtpStatusCode::SYNTAX_ERROR_PARAMETER_OR_ARGUMENT: + return "Syntax error in parameters or arguments"; + case FtpStatusCode::COMMAND_NOT_IMPLEMENTED: + return "Command not implemented"; + case FtpStatusCode::BAD_SEQUENCE_COMMANDS: + return "Bad sequence of commands"; + case FtpStatusCode::COMMAND_NOT_IMPLEMENTED_FOR_PARAMETER: + return "Command not implemented for that parameter"; + case FtpStatusCode::NETWORK_PROTOCOL_NOT_SUPPORTED: + return "Network protocol not supported"; + case FtpStatusCode::NOT_LOGGED_IN: + return "Not logged in"; + case FtpStatusCode::NEED_ACCOUNT_FOR_STORE_FILE: + return "Need account for storing files"; + case FtpStatusCode::COMMAND_PROTECTION_DENIED: + return "Command protection level denied for policy reasons"; + case FtpStatusCode::REQUEST_DENIED: + return "Request denied for policy reasons"; + case FtpStatusCode::FAILED_SEC_CHECK: + return "Failed security check (hash, sequence, etc)"; + case FtpStatusCode::REQ_PROT_LEVEL_NOT_SUPPORTED: + return "Requested PROT level not supported by mechanism"; + case FtpStatusCode::COMMAND_PROTECTION_LEVEL_NOT_SUPPORTED: + return "Command protection level not supported by security mechanism"; + case FtpStatusCode::FILE_UNAVAILABLE: + return "Requested action not taken: File unavailable"; + case FtpStatusCode::PAGE_TYPE_UNKNOWN: + return "Requested action aborted: page type unknown"; + case FtpStatusCode::EXCEED_STORAGE_ALLOCATION: + return "Requested file action aborted: Exceeded storage allocation"; + case FtpStatusCode::FILENAME_NOT_ALLOWED: + return "Requested action not taken: File name not allowed"; + case FtpStatusCode::INTEGRITY_PROTECTED: + return "Integrity protected reply"; + case FtpStatusCode::CONFIDENTIALITY_AND_INTEGRITY_PROTECTED: + return "Confidentiality and integrity protected reply"; + case FtpStatusCode::CONFIDENTIALITY_PROTECTED: + return "Confidentiality protected reply"; + default: + return "Unknown Status Code"; + } +} - std::string FtpResponseLayer::toString() const - { - return "FTP Response: " + getStatusCodeString(); - } +std::string FtpResponseLayer::toString() const { + return "FTP Response: " + getStatusCodeString(); +} - std::string FtpDataLayer::toString() const - { - return "FTP Data"; - } +std::string FtpDataLayer::toString() const { return "FTP Data"; } } // namespace pcpp diff --git a/Packet++/src/GreLayer.cpp b/Packet++/src/GreLayer.cpp index bae6aaefaa..5850958bb2 100644 --- a/Packet++/src/GreLayer.cpp +++ b/Packet++/src/GreLayer.cpp @@ -1,618 +1,560 @@ #define LOG_MODULE PacketLogModuleGreLayer #include "GreLayer.h" -#include "EthLayer.h" +#include "EndianPortable.h" #include "EthDot3Layer.h" +#include "EthLayer.h" #include "IPv4Layer.h" #include "IPv6Layer.h" -#include "PPPoELayer.h" -#include "VlanLayer.h" +#include "Logger.h" #include "MplsLayer.h" -#include "PayloadLayer.h" +#include "PPPoELayer.h" #include "PacketUtils.h" -#include "Logger.h" -#include "EndianPortable.h" +#include "PayloadLayer.h" +#include "VlanLayer.h" // ============== // GreLayer class // ============== -namespace pcpp -{ - -ProtocolType GreLayer::getGREVersion(uint8_t* greData, size_t greDataLen) -{ - if (greDataLen < sizeof(gre_basic_header)) - return UnknownProtocol; - - uint8_t version = *(greData+1); - version &= 0x07; - if (version == 0) - return GREv0; - else if (version == 1) - return GREv1; - else - return UnknownProtocol; +namespace pcpp { + +ProtocolType GreLayer::getGREVersion(uint8_t* greData, size_t greDataLen) { + if (greDataLen < sizeof(gre_basic_header)) + return UnknownProtocol; + + uint8_t version = *(greData + 1); + version &= 0x07; + if (version == 0) + return GREv0; + else if (version == 1) + return GREv1; + else + return UnknownProtocol; } -uint8_t* GreLayer::getFieldValue(GreField field, bool returnOffsetEvenIfFieldMissing) const -{ - uint8_t* ptr = m_Data + sizeof(gre_basic_header); - - gre_basic_header* header = (gre_basic_header*)m_Data; - - for (int curFieldAsInt = static_cast(GreChecksumOrRouting); curFieldAsInt < 4 /* this value is out of scope of GreField enum values */; ++curFieldAsInt) - { - const GreField curField = static_cast(curFieldAsInt); - bool curFieldExists = false; - - uint8_t* origPtr = ptr; - - switch (curField) - { - case GreChecksumOrRouting: - if (header->checksumBit == 1 || header->routingBit == 1) - { - curFieldExists = true; - ptr += sizeof(uint32_t); - } - break; - case GreKey: - if (header->keyBit == 1) - { - curFieldExists = true; - ptr += sizeof(uint32_t); - } - break; - case GreSeq: - if (header->sequenceNumBit == 1) - { - curFieldExists = true; - ptr += sizeof(uint32_t); - } - break; - case GreAck: - if (header->ackSequenceNumBit == 1) - { - curFieldExists = true; - ptr += sizeof(uint32_t); - } - break; - default: // shouldn't get there - return nullptr; - } - - if (field == curField) - { - if (curFieldExists || returnOffsetEvenIfFieldMissing) - return origPtr; - - return nullptr; - } - } // for - - return nullptr; +uint8_t* GreLayer::getFieldValue(GreField field, + bool returnOffsetEvenIfFieldMissing) const { + uint8_t* ptr = m_Data + sizeof(gre_basic_header); + + gre_basic_header* header = (gre_basic_header*)m_Data; + + for (int curFieldAsInt = static_cast(GreChecksumOrRouting); + curFieldAsInt < + 4 /* this value is out of scope of GreField enum values */; + ++curFieldAsInt) { + const GreField curField = static_cast(curFieldAsInt); + bool curFieldExists = false; + + uint8_t* origPtr = ptr; + + switch (curField) { + case GreChecksumOrRouting: + if (header->checksumBit == 1 || header->routingBit == 1) { + curFieldExists = true; + ptr += sizeof(uint32_t); + } + break; + case GreKey: + if (header->keyBit == 1) { + curFieldExists = true; + ptr += sizeof(uint32_t); + } + break; + case GreSeq: + if (header->sequenceNumBit == 1) { + curFieldExists = true; + ptr += sizeof(uint32_t); + } + break; + case GreAck: + if (header->ackSequenceNumBit == 1) { + curFieldExists = true; + ptr += sizeof(uint32_t); + } + break; + default: // shouldn't get there + return nullptr; + } + + if (field == curField) { + if (curFieldExists || returnOffsetEvenIfFieldMissing) + return origPtr; + + return nullptr; + } + } // for + + return nullptr; } -void GreLayer::computeCalculateFieldsInner() -{ - gre_basic_header* header = (gre_basic_header*)m_Data; - if (m_NextLayer != nullptr) - { - switch (m_NextLayer->getProtocol()) - { - case IPv4: - header->protocol = htobe16(PCPP_ETHERTYPE_IP); - break; - case IPv6: - header->protocol = htobe16(PCPP_ETHERTYPE_IPV6); - break; - case VLAN: - header->protocol = htobe16(PCPP_ETHERTYPE_VLAN); - break; - case MPLS: - header->protocol = htobe16(PCPP_ETHERTYPE_MPLS); - break; - case PPP_PPTP: - header->protocol = htobe16(PCPP_ETHERTYPE_PPP); - break; - case Ethernet: - header->protocol = htobe16(PCPP_ETHERTYPE_ETHBRIDGE); - break; - default: - break; - } - } +void GreLayer::computeCalculateFieldsInner() { + gre_basic_header* header = (gre_basic_header*)m_Data; + if (m_NextLayer != nullptr) { + switch (m_NextLayer->getProtocol()) { + case IPv4: + header->protocol = htobe16(PCPP_ETHERTYPE_IP); + break; + case IPv6: + header->protocol = htobe16(PCPP_ETHERTYPE_IPV6); + break; + case VLAN: + header->protocol = htobe16(PCPP_ETHERTYPE_VLAN); + break; + case MPLS: + header->protocol = htobe16(PCPP_ETHERTYPE_MPLS); + break; + case PPP_PPTP: + header->protocol = htobe16(PCPP_ETHERTYPE_PPP); + break; + case Ethernet: + header->protocol = htobe16(PCPP_ETHERTYPE_ETHBRIDGE); + break; + default: + break; + } + } } -bool GreLayer::getSequenceNumber(uint32_t& seqNumber) const -{ - gre_basic_header* header = (gre_basic_header*)m_Data; +bool GreLayer::getSequenceNumber(uint32_t& seqNumber) const { + gre_basic_header* header = (gre_basic_header*)m_Data; - if (header->sequenceNumBit == 0) - return false; + if (header->sequenceNumBit == 0) + return false; - uint32_t* val = (uint32_t*)getFieldValue(GreSeq, false); - if (val == nullptr) - return false; + uint32_t* val = (uint32_t*)getFieldValue(GreSeq, false); + if (val == nullptr) + return false; - seqNumber = be32toh(*val); - return true; + seqNumber = be32toh(*val); + return true; } -bool GreLayer::setSequenceNumber(uint32_t seqNumber) -{ - gre_basic_header* header = (gre_basic_header*)m_Data; +bool GreLayer::setSequenceNumber(uint32_t seqNumber) { + gre_basic_header* header = (gre_basic_header*)m_Data; - bool needToExtendLayer = false; + bool needToExtendLayer = false; - if (header->sequenceNumBit == 0) - needToExtendLayer = true; + if (header->sequenceNumBit == 0) + needToExtendLayer = true; - uint8_t* offsetPtr = getFieldValue(GreSeq, true); + uint8_t* offsetPtr = getFieldValue(GreSeq, true); - int offset = offsetPtr - m_Data; - if (needToExtendLayer && !extendLayer(offset, sizeof(uint32_t))) - { - header->sequenceNumBit = 0; - PCPP_LOG_ERROR("Couldn't extend layer to set sequence number"); - return false; - } + int offset = offsetPtr - m_Data; + if (needToExtendLayer && !extendLayer(offset, sizeof(uint32_t))) { + header->sequenceNumBit = 0; + PCPP_LOG_ERROR("Couldn't extend layer to set sequence number"); + return false; + } - header = (gre_basic_header*)m_Data; - header->sequenceNumBit = 1; - uint32_t* seqPtr = (uint32_t*)(m_Data + offset); - *seqPtr = htobe32(seqNumber); + header = (gre_basic_header*)m_Data; + header->sequenceNumBit = 1; + uint32_t* seqPtr = (uint32_t*)(m_Data + offset); + *seqPtr = htobe32(seqNumber); - return true; + return true; } -bool GreLayer::unsetSequenceNumber() -{ - gre_basic_header* header = (gre_basic_header*)m_Data; +bool GreLayer::unsetSequenceNumber() { + gre_basic_header* header = (gre_basic_header*)m_Data; - if (header->sequenceNumBit == 0) - { - PCPP_LOG_ERROR("Couldn't unset sequence number as it's already unset"); - return false; - } + if (header->sequenceNumBit == 0) { + PCPP_LOG_ERROR("Couldn't unset sequence number as it's already unset"); + return false; + } - uint8_t* offsetPtr = getFieldValue(GreSeq, true); + uint8_t* offsetPtr = getFieldValue(GreSeq, true); - int offset = offsetPtr - m_Data; - if (!shortenLayer(offset, sizeof(uint32_t))) - { - PCPP_LOG_ERROR("Couldn't shorted layer to unset sequence number"); - return false; - } + int offset = offsetPtr - m_Data; + if (!shortenLayer(offset, sizeof(uint32_t))) { + PCPP_LOG_ERROR("Couldn't shorted layer to unset sequence number"); + return false; + } - header = (gre_basic_header*)m_Data; - header->sequenceNumBit = 0; - return true; + header = (gre_basic_header*)m_Data; + header->sequenceNumBit = 0; + return true; } -void GreLayer::parseNextLayer() -{ - size_t headerLen = getHeaderLen(); - if (m_DataLen <= headerLen) - return; - - gre_basic_header* header = (gre_basic_header*)m_Data; - uint8_t* payload = m_Data + headerLen; - size_t payloadLen = m_DataLen - headerLen; - - switch (be16toh(header->protocol)) - { - case PCPP_ETHERTYPE_IP: - m_NextLayer = IPv4Layer::isDataValid(payload, payloadLen) - ? static_cast(new IPv4Layer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case PCPP_ETHERTYPE_IPV6: - m_NextLayer = IPv6Layer::isDataValid(payload, payloadLen) - ? static_cast(new IPv6Layer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case PCPP_ETHERTYPE_VLAN: - m_NextLayer = new VlanLayer(payload, payloadLen, this, m_Packet); - break; - case PCPP_ETHERTYPE_MPLS: - m_NextLayer = new MplsLayer(payload, payloadLen, this, m_Packet); - break; - case PCPP_ETHERTYPE_PPP: - m_NextLayer = new PPP_PPTPLayer(payload, payloadLen, this, m_Packet); - break; - case PCPP_ETHERTYPE_ETHBRIDGE: - if (EthLayer::isDataValid(payload, payloadLen)) - { - m_NextLayer = new EthLayer(payload, payloadLen, this, m_Packet); - } - else if (EthDot3Layer::isDataValid(payload, payloadLen)) - { - m_NextLayer = new EthDot3Layer(payload, payloadLen, this, m_Packet); - } - else - { - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - } - break; - default: - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - } +void GreLayer::parseNextLayer() { + size_t headerLen = getHeaderLen(); + if (m_DataLen <= headerLen) + return; + + gre_basic_header* header = (gre_basic_header*)m_Data; + uint8_t* payload = m_Data + headerLen; + size_t payloadLen = m_DataLen - headerLen; + + switch (be16toh(header->protocol)) { + case PCPP_ETHERTYPE_IP: + m_NextLayer = IPv4Layer::isDataValid(payload, payloadLen) + ? static_cast( + new IPv4Layer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer( + payload, payloadLen, this, m_Packet)); + break; + case PCPP_ETHERTYPE_IPV6: + m_NextLayer = IPv6Layer::isDataValid(payload, payloadLen) + ? static_cast( + new IPv6Layer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer( + payload, payloadLen, this, m_Packet)); + break; + case PCPP_ETHERTYPE_VLAN: + m_NextLayer = new VlanLayer(payload, payloadLen, this, m_Packet); + break; + case PCPP_ETHERTYPE_MPLS: + m_NextLayer = new MplsLayer(payload, payloadLen, this, m_Packet); + break; + case PCPP_ETHERTYPE_PPP: + m_NextLayer = new PPP_PPTPLayer(payload, payloadLen, this, m_Packet); + break; + case PCPP_ETHERTYPE_ETHBRIDGE: + if (EthLayer::isDataValid(payload, payloadLen)) { + m_NextLayer = new EthLayer(payload, payloadLen, this, m_Packet); + } else if (EthDot3Layer::isDataValid(payload, payloadLen)) { + m_NextLayer = new EthDot3Layer(payload, payloadLen, this, m_Packet); + } else { + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + } + break; + default: + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + } } -size_t GreLayer::getHeaderLen() const -{ - size_t result = sizeof(gre_basic_header); +size_t GreLayer::getHeaderLen() const { + size_t result = sizeof(gre_basic_header); - gre_basic_header* header = (gre_basic_header*)m_Data; + gre_basic_header* header = (gre_basic_header*)m_Data; - if (header->checksumBit == 1 || header->routingBit == 1 ) - result += 4; - if (header->keyBit == 1) - result += 4; - if (header->sequenceNumBit == 1) - result += 4; - if (header->ackSequenceNumBit == 1) - result += 4; + if (header->checksumBit == 1 || header->routingBit == 1) + result += 4; + if (header->keyBit == 1) + result += 4; + if (header->sequenceNumBit == 1) + result += 4; + if (header->ackSequenceNumBit == 1) + result += 4; - return result; + return result; } - - // ================ // GREv0Layer class // ================ - -GREv0Layer::GREv0Layer() -{ - const size_t headerLen = sizeof(gre_basic_header); - m_DataLen = headerLen; - m_Data = new uint8_t[headerLen]; - memset(m_Data, 0, headerLen); - m_Protocol = GREv0; +GREv0Layer::GREv0Layer() { + const size_t headerLen = sizeof(gre_basic_header); + m_DataLen = headerLen; + m_Data = new uint8_t[headerLen]; + memset(m_Data, 0, headerLen); + m_Protocol = GREv0; } -bool GREv0Layer::getChecksum(uint16_t& checksum) -{ - if (getGreHeader()->checksumBit == 0) - return false; +bool GREv0Layer::getChecksum(uint16_t& checksum) { + if (getGreHeader()->checksumBit == 0) + return false; - uint16_t* val = (uint16_t*)getFieldValue(GreChecksumOrRouting, false); - if (val == nullptr) - return false; + uint16_t* val = (uint16_t*)getFieldValue(GreChecksumOrRouting, false); + if (val == nullptr) + return false; - checksum = be16toh(*val); - return true; + checksum = be16toh(*val); + return true; } -bool GREv0Layer::setChecksum(uint16_t checksum) -{ - gre_basic_header* header = getGreHeader(); +bool GREv0Layer::setChecksum(uint16_t checksum) { + gre_basic_header* header = getGreHeader(); - bool needToExtendLayer = false; + bool needToExtendLayer = false; - if (header->routingBit == 0 && header->checksumBit == 0) - needToExtendLayer = true; + if (header->routingBit == 0 && header->checksumBit == 0) + needToExtendLayer = true; - uint8_t* offsetPtr = getFieldValue(GreChecksumOrRouting, true); - int offset = offsetPtr - m_Data; - // extend layer in 4 bytes to keep 4-byte alignment - if (needToExtendLayer && !extendLayer(offset, sizeof(uint32_t))) - { - PCPP_LOG_ERROR("Couldn't extend layer to set checksum"); - return false; - } + uint8_t* offsetPtr = getFieldValue(GreChecksumOrRouting, true); + int offset = offsetPtr - m_Data; + // extend layer in 4 bytes to keep 4-byte alignment + if (needToExtendLayer && !extendLayer(offset, sizeof(uint32_t))) { + PCPP_LOG_ERROR("Couldn't extend layer to set checksum"); + return false; + } - uint16_t* checksumPtr = (uint16_t*)(m_Data + offset); - *checksumPtr = htobe16(checksum); + uint16_t* checksumPtr = (uint16_t*)(m_Data + offset); + *checksumPtr = htobe16(checksum); - // if layer was extended in 4 bytes, make sure the offset field stays 0 - if (needToExtendLayer) - { - checksumPtr++; - *checksumPtr = 0; - } + // if layer was extended in 4 bytes, make sure the offset field stays 0 + if (needToExtendLayer) { + checksumPtr++; + *checksumPtr = 0; + } - header = getGreHeader(); - header->checksumBit = 1; + header = getGreHeader(); + header->checksumBit = 1; - return true; + return true; } -bool GREv0Layer::unsetChecksum() -{ - gre_basic_header* header = getGreHeader(); - - if (header->checksumBit == 0) - { - PCPP_LOG_ERROR("Couldn't unset checksum as it's already unset"); - return false; - } - - // if both routing and checksum are unset we need to shorted the layer - bool needToShortenLayer = (header->routingBit == 0); - - uint8_t* offsetPtr = getFieldValue(GreChecksumOrRouting, true); - int offset = offsetPtr - m_Data; - if (needToShortenLayer && !shortenLayer(offset, sizeof(uint32_t))) - { - PCPP_LOG_ERROR("Couldn't extend layer to unset checksum"); - return false; - } - - if (!needToShortenLayer) // meaning routing bit is set - only zero the checksum field - { - uint16_t* checksumPtr = (uint16_t*)(m_Data + offset); - *checksumPtr = 0; - } - - header = getGreHeader(); - header->checksumBit = 0; - - return true; +bool GREv0Layer::unsetChecksum() { + gre_basic_header* header = getGreHeader(); + + if (header->checksumBit == 0) { + PCPP_LOG_ERROR("Couldn't unset checksum as it's already unset"); + return false; + } + + // if both routing and checksum are unset we need to shorted the layer + bool needToShortenLayer = (header->routingBit == 0); + + uint8_t* offsetPtr = getFieldValue(GreChecksumOrRouting, true); + int offset = offsetPtr - m_Data; + if (needToShortenLayer && !shortenLayer(offset, sizeof(uint32_t))) { + PCPP_LOG_ERROR("Couldn't extend layer to unset checksum"); + return false; + } + + if (!needToShortenLayer) // meaning routing bit is set - only zero the + // checksum field + { + uint16_t* checksumPtr = (uint16_t*)(m_Data + offset); + *checksumPtr = 0; + } + + header = getGreHeader(); + header->checksumBit = 0; + + return true; } -bool GREv0Layer::getOffset(uint16_t& offset) const -{ - if (getGreHeader()->routingBit == 0) - return false; +bool GREv0Layer::getOffset(uint16_t& offset) const { + if (getGreHeader()->routingBit == 0) + return false; - uint8_t* val = (uint8_t*)getFieldValue(GreChecksumOrRouting, false); - if (val == nullptr) - return false; + uint8_t* val = (uint8_t*)getFieldValue(GreChecksumOrRouting, false); + if (val == nullptr) + return false; - offset = be16toh(*(val+2)); - return true; + offset = be16toh(*(val + 2)); + return true; } -bool GREv0Layer::getKey(uint32_t& key) const -{ - if (getGreHeader()->keyBit == 0) - return false; +bool GREv0Layer::getKey(uint32_t& key) const { + if (getGreHeader()->keyBit == 0) + return false; - uint32_t* val = (uint32_t*)getFieldValue(GreKey, false); - if (val == nullptr) - return false; + uint32_t* val = (uint32_t*)getFieldValue(GreKey, false); + if (val == nullptr) + return false; - key = be32toh(*val); - return true; + key = be32toh(*val); + return true; } -bool GREv0Layer::setKey(uint32_t key) -{ - gre_basic_header* header = getGreHeader(); +bool GREv0Layer::setKey(uint32_t key) { + gre_basic_header* header = getGreHeader(); - bool needToExtendLayer = false; + bool needToExtendLayer = false; - if (header->keyBit == 0) - needToExtendLayer = true; + if (header->keyBit == 0) + needToExtendLayer = true; - uint8_t* offsetPtr = getFieldValue(GreKey, true); + uint8_t* offsetPtr = getFieldValue(GreKey, true); - int offset = offsetPtr - m_Data; - if (needToExtendLayer && !extendLayer(offset, sizeof(uint32_t))) - { - header->keyBit = 0; - PCPP_LOG_ERROR("Couldn't extend layer to set key"); - return false; - } + int offset = offsetPtr - m_Data; + if (needToExtendLayer && !extendLayer(offset, sizeof(uint32_t))) { + header->keyBit = 0; + PCPP_LOG_ERROR("Couldn't extend layer to set key"); + return false; + } - header = getGreHeader(); - header->keyBit = 1; - uint32_t* keyPtr = (uint32_t*)(m_Data + offset); - *keyPtr = htobe32(key); + header = getGreHeader(); + header->keyBit = 1; + uint32_t* keyPtr = (uint32_t*)(m_Data + offset); + *keyPtr = htobe32(key); - return true; + return true; } -bool GREv0Layer::unsetKey() -{ - gre_basic_header* header = getGreHeader(); +bool GREv0Layer::unsetKey() { + gre_basic_header* header = getGreHeader(); - if (header->keyBit == 0) - { - PCPP_LOG_ERROR("Couldn't unset key as it's already unset"); - return false; - } + if (header->keyBit == 0) { + PCPP_LOG_ERROR("Couldn't unset key as it's already unset"); + return false; + } - uint8_t* offsetPtr = getFieldValue(GreKey, true); + uint8_t* offsetPtr = getFieldValue(GreKey, true); - int offset = offsetPtr - m_Data; - if (!shortenLayer(offset, sizeof(uint32_t))) - { - PCPP_LOG_ERROR("Couldn't shorted layer to unset key"); - return false; - } + int offset = offsetPtr - m_Data; + if (!shortenLayer(offset, sizeof(uint32_t))) { + PCPP_LOG_ERROR("Couldn't shorted layer to unset key"); + return false; + } - header = (gre_basic_header*)m_Data; - header->keyBit = 0; - return true; + header = (gre_basic_header*)m_Data; + header->keyBit = 0; + return true; } -void GREv0Layer::computeCalculateFields() -{ - computeCalculateFieldsInner(); +void GREv0Layer::computeCalculateFields() { + computeCalculateFieldsInner(); - if (getGreHeader()->checksumBit == 0) - return; + if (getGreHeader()->checksumBit == 0) + return; - // calculate checksum - setChecksum(0); + // calculate checksum + setChecksum(0); - ScalarBuffer buffer; - buffer.buffer = (uint16_t*)m_Data; - buffer.len = m_DataLen; - size_t checksum = computeChecksum(&buffer, 1); + ScalarBuffer buffer; + buffer.buffer = (uint16_t*)m_Data; + buffer.len = m_DataLen; + size_t checksum = computeChecksum(&buffer, 1); - setChecksum(checksum); -} - -std::string GREv0Layer::toString() const -{ - return "GRE Layer, version 0"; + setChecksum(checksum); } +std::string GREv0Layer::toString() const { return "GRE Layer, version 0"; } // ================ // GREv1Layer class // ================ -GREv1Layer::GREv1Layer(uint16_t callID) -{ - const size_t headerLen = sizeof(gre1_header); - m_DataLen = headerLen; - m_Data = new uint8_t[headerLen]; - memset(m_Data, 0, headerLen); - m_Protocol = GREv1; - - gre1_header* header = getGreHeader(); - header->keyBit = 1; - header->version = 1; - header->callID = htobe16(callID); +GREv1Layer::GREv1Layer(uint16_t callID) { + const size_t headerLen = sizeof(gre1_header); + m_DataLen = headerLen; + m_Data = new uint8_t[headerLen]; + memset(m_Data, 0, headerLen); + m_Protocol = GREv1; + + gre1_header* header = getGreHeader(); + header->keyBit = 1; + header->version = 1; + header->callID = htobe16(callID); } -bool GREv1Layer::getAcknowledgmentNum(uint32_t& ackNum) const -{ - if (getGreHeader()->ackSequenceNumBit == 0) - return false; +bool GREv1Layer::getAcknowledgmentNum(uint32_t& ackNum) const { + if (getGreHeader()->ackSequenceNumBit == 0) + return false; - uint32_t* val = (uint32_t*)getFieldValue(GreAck, false); - if (val == nullptr) - return false; + uint32_t* val = (uint32_t*)getFieldValue(GreAck, false); + if (val == nullptr) + return false; - ackNum = be32toh(*val); - return true; + ackNum = be32toh(*val); + return true; } -bool GREv1Layer::setAcknowledgmentNum(uint32_t ackNum) -{ - bool needToExtendLayer = false; +bool GREv1Layer::setAcknowledgmentNum(uint32_t ackNum) { + bool needToExtendLayer = false; - gre1_header* header = getGreHeader(); + gre1_header* header = getGreHeader(); - if (header->ackSequenceNumBit == 0) - needToExtendLayer = true; + if (header->ackSequenceNumBit == 0) + needToExtendLayer = true; - uint8_t* offsetPtr = getFieldValue(GreAck, true); - int offset = offsetPtr - m_Data; - if (needToExtendLayer && !extendLayer(offset, sizeof(uint32_t))) - { - PCPP_LOG_ERROR("Couldn't extend layer to set ack number"); - return false; - } + uint8_t* offsetPtr = getFieldValue(GreAck, true); + int offset = offsetPtr - m_Data; + if (needToExtendLayer && !extendLayer(offset, sizeof(uint32_t))) { + PCPP_LOG_ERROR("Couldn't extend layer to set ack number"); + return false; + } - header = getGreHeader(); - header->ackSequenceNumBit = 1; - uint32_t* ackPtr = (uint32_t*)(m_Data + offset); - *ackPtr = htobe32(ackNum); - return true; + header = getGreHeader(); + header->ackSequenceNumBit = 1; + uint32_t* ackPtr = (uint32_t*)(m_Data + offset); + *ackPtr = htobe32(ackNum); + return true; } -bool GREv1Layer::unsetAcknowledgmentNum() -{ - gre1_header* header = getGreHeader(); +bool GREv1Layer::unsetAcknowledgmentNum() { + gre1_header* header = getGreHeader(); - if (header->ackSequenceNumBit == 0) - { - PCPP_LOG_ERROR("Couldn't unset ack number as it's already unset"); - return false; - } + if (header->ackSequenceNumBit == 0) { + PCPP_LOG_ERROR("Couldn't unset ack number as it's already unset"); + return false; + } - uint8_t* offsetPtr = getFieldValue(GreAck, true); + uint8_t* offsetPtr = getFieldValue(GreAck, true); - int offset = offsetPtr - m_Data; - if (!shortenLayer(offset, sizeof(uint32_t))) - { - PCPP_LOG_ERROR("Couldn't shorted layer to unset ack number"); - return false; - } + int offset = offsetPtr - m_Data; + if (!shortenLayer(offset, sizeof(uint32_t))) { + PCPP_LOG_ERROR("Couldn't shorted layer to unset ack number"); + return false; + } - header = getGreHeader(); - header->ackSequenceNumBit = 0; - return true; + header = getGreHeader(); + header->ackSequenceNumBit = 0; + return true; } -void GREv1Layer::computeCalculateFields() -{ - computeCalculateFieldsInner(); - - getGreHeader()->payloadLength = htobe16(m_DataLen - getHeaderLen()); -} +void GREv1Layer::computeCalculateFields() { + computeCalculateFieldsInner(); -std::string GREv1Layer::toString() const -{ - return "GRE Layer, version 1"; + getGreHeader()->payloadLength = htobe16(m_DataLen - getHeaderLen()); } - +std::string GREv1Layer::toString() const { return "GRE Layer, version 1"; } // =================== // PPP_PPTPLayer class // =================== -PPP_PPTPLayer::PPP_PPTPLayer(uint8_t address, uint8_t control) -{ - const size_t headerLen = sizeof(ppp_pptp_header); - m_DataLen = headerLen; - m_Data = new uint8_t[headerLen]; - memset(m_Data, 0, headerLen); - m_Protocol = PPP_PPTP; - - ppp_pptp_header* header = getPPP_PPTPHeader(); - header->address = address; - header->control = control; -} +PPP_PPTPLayer::PPP_PPTPLayer(uint8_t address, uint8_t control) { + const size_t headerLen = sizeof(ppp_pptp_header); + m_DataLen = headerLen; + m_Data = new uint8_t[headerLen]; + memset(m_Data, 0, headerLen); + m_Protocol = PPP_PPTP; + ppp_pptp_header* header = getPPP_PPTPHeader(); + header->address = address; + header->control = control; +} -void PPP_PPTPLayer::parseNextLayer() -{ - size_t headerLen = getHeaderLen(); - if (m_DataLen <= headerLen) - return; - - uint8_t* payload = m_Data + headerLen; - size_t payloadLen = m_DataLen - headerLen; - - switch (be16toh(getPPP_PPTPHeader()->protocol)) - { - case PCPP_PPP_IP: - m_NextLayer = IPv4Layer::isDataValid(payload, payloadLen) - ? static_cast(new IPv4Layer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case PCPP_PPP_IPV6: - m_NextLayer = IPv6Layer::isDataValid(payload, payloadLen) - ? static_cast(new IPv6Layer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - default: - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - break; - } +void PPP_PPTPLayer::parseNextLayer() { + size_t headerLen = getHeaderLen(); + if (m_DataLen <= headerLen) + return; + + uint8_t* payload = m_Data + headerLen; + size_t payloadLen = m_DataLen - headerLen; + + switch (be16toh(getPPP_PPTPHeader()->protocol)) { + case PCPP_PPP_IP: + m_NextLayer = IPv4Layer::isDataValid(payload, payloadLen) + ? static_cast( + new IPv4Layer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer( + payload, payloadLen, this, m_Packet)); + break; + case PCPP_PPP_IPV6: + m_NextLayer = IPv6Layer::isDataValid(payload, payloadLen) + ? static_cast( + new IPv6Layer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer( + payload, payloadLen, this, m_Packet)); + break; + default: + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + break; + } } -void PPP_PPTPLayer::computeCalculateFields() -{ - ppp_pptp_header* header = getPPP_PPTPHeader(); - if (m_NextLayer != nullptr) - { - switch (m_NextLayer->getProtocol()) - { - case IPv4: - header->protocol = htobe16(PCPP_PPP_IP); - break; - case IPv6: - header->protocol = htobe16(PCPP_PPP_IPV6); - break; - default: - break; - } - } - else - header->protocol = 0; +void PPP_PPTPLayer::computeCalculateFields() { + ppp_pptp_header* header = getPPP_PPTPHeader(); + if (m_NextLayer != nullptr) { + switch (m_NextLayer->getProtocol()) { + case IPv4: + header->protocol = htobe16(PCPP_PPP_IP); + break; + case IPv6: + header->protocol = htobe16(PCPP_PPP_IPV6); + break; + default: + break; + } + } else + header->protocol = 0; } } // namespace pcpp diff --git a/Packet++/src/GtpLayer.cpp b/Packet++/src/GtpLayer.cpp index acf099d5d1..806815e6df 100644 --- a/Packet++/src/GtpLayer.cpp +++ b/Packet++/src/GtpLayer.cpp @@ -1,16 +1,15 @@ #define LOG_MODULE PacketLogModuleGtpLayer -#include -#include -#include "Logger.h" #include "GtpLayer.h" +#include "EndianPortable.h" #include "IPv4Layer.h" #include "IPv6Layer.h" +#include "Logger.h" #include "PayloadLayer.h" -#include "EndianPortable.h" +#include +#include -namespace pcpp -{ +namespace pcpp { #define PCPP_GTP_V1_GPDU_MESSAGE_TYPE 0xff @@ -18,665 +17,588 @@ namespace pcpp /// GtpExtension class /// ================== - -GtpV1Layer::GtpExtension::GtpExtension() -{ - m_Data = nullptr; - m_DataLen = 0; - m_ExtType = 0; +GtpV1Layer::GtpExtension::GtpExtension() { + m_Data = nullptr; + m_DataLen = 0; + m_ExtType = 0; } -GtpV1Layer::GtpExtension::GtpExtension(uint8_t* data, size_t dataLen, uint8_t type) -{ - m_Data = data; - m_DataLen = dataLen; - m_ExtType = type; +GtpV1Layer::GtpExtension::GtpExtension(uint8_t* data, size_t dataLen, + uint8_t type) { + m_Data = data; + m_DataLen = dataLen; + m_ExtType = type; } -GtpV1Layer::GtpExtension::GtpExtension(const GtpExtension& other) -{ - m_Data = other.m_Data; - m_DataLen = other.m_DataLen; - m_ExtType = other.m_ExtType; +GtpV1Layer::GtpExtension::GtpExtension(const GtpExtension& other) { + m_Data = other.m_Data; + m_DataLen = other.m_DataLen; + m_ExtType = other.m_ExtType; } -GtpV1Layer::GtpExtension& GtpV1Layer::GtpExtension::operator=(const GtpV1Layer::GtpExtension& other) -{ - m_Data = other.m_Data; - m_DataLen = other.m_DataLen; - m_ExtType = other.m_ExtType; - return *this; +GtpV1Layer::GtpExtension& +GtpV1Layer::GtpExtension::operator=(const GtpV1Layer::GtpExtension& other) { + m_Data = other.m_Data; + m_DataLen = other.m_DataLen; + m_ExtType = other.m_ExtType; + return *this; } -bool GtpV1Layer::GtpExtension::isNull() const -{ - return m_Data == nullptr; -} +bool GtpV1Layer::GtpExtension::isNull() const { return m_Data == nullptr; } -uint8_t GtpV1Layer::GtpExtension::getExtensionType() const -{ - return m_ExtType; -} +uint8_t GtpV1Layer::GtpExtension::getExtensionType() const { return m_ExtType; } -size_t GtpV1Layer::GtpExtension::getTotalLength() const -{ - if (m_Data == nullptr) - { - return 0; - } +size_t GtpV1Layer::GtpExtension::getTotalLength() const { + if (m_Data == nullptr) { + return 0; + } - size_t len = (size_t)(m_Data[0]*4); - if (len <= m_DataLen) - { - return len; - } + size_t len = (size_t)(m_Data[0] * 4); + if (len <= m_DataLen) { + return len; + } - return m_DataLen; + return m_DataLen; } -size_t GtpV1Layer::GtpExtension::getContentLength() const -{ - size_t res = getTotalLength(); +size_t GtpV1Layer::GtpExtension::getContentLength() const { + size_t res = getTotalLength(); - if (res >= 2*sizeof(uint8_t)) - { - return (size_t)(res - 2*sizeof(uint8_t)); - } + if (res >= 2 * sizeof(uint8_t)) { + return (size_t)(res - 2 * sizeof(uint8_t)); + } - return 0; + return 0; } -uint8_t* GtpV1Layer::GtpExtension::getContent() const -{ - if (m_Data == nullptr || getContentLength() == 0) - { - return nullptr; - } +uint8_t* GtpV1Layer::GtpExtension::getContent() const { + if (m_Data == nullptr || getContentLength() == 0) { + return nullptr; + } - return m_Data + sizeof(uint8_t); + return m_Data + sizeof(uint8_t); } -uint8_t GtpV1Layer::GtpExtension::getNextExtensionHeaderType() const -{ - if (m_Data == nullptr || getTotalLength() < 4) - { - return 0; - } +uint8_t GtpV1Layer::GtpExtension::getNextExtensionHeaderType() const { + if (m_Data == nullptr || getTotalLength() < 4) { + return 0; + } - uint8_t res = *(uint8_t*)(m_Data + sizeof(uint8_t) + getContentLength()); + uint8_t res = *(uint8_t*)(m_Data + sizeof(uint8_t) + getContentLength()); - return res; + return res; } -GtpV1Layer::GtpExtension GtpV1Layer::GtpExtension::getNextExtension() const -{ - size_t totalLength = getTotalLength(); - uint8_t nextExtType = getNextExtensionHeaderType(); - if (nextExtType > 0 && m_DataLen > totalLength + sizeof(uint8_t)) - { - return GtpV1Layer::GtpExtension(m_Data + totalLength, m_DataLen - totalLength, nextExtType); - } - else - { - return GtpV1Layer::GtpExtension(); - } +GtpV1Layer::GtpExtension GtpV1Layer::GtpExtension::getNextExtension() const { + size_t totalLength = getTotalLength(); + uint8_t nextExtType = getNextExtensionHeaderType(); + if (nextExtType > 0 && m_DataLen > totalLength + sizeof(uint8_t)) { + return GtpV1Layer::GtpExtension(m_Data + totalLength, + m_DataLen - totalLength, nextExtType); + } else { + return GtpV1Layer::GtpExtension(); + } } -void GtpV1Layer::GtpExtension::setNextHeaderType(uint8_t nextHeaderType) -{ - if (m_Data != nullptr && m_DataLen > 1) - { - m_Data[getTotalLength() - 1] = nextHeaderType; - } +void GtpV1Layer::GtpExtension::setNextHeaderType(uint8_t nextHeaderType) { + if (m_Data != nullptr && m_DataLen > 1) { + m_Data[getTotalLength() - 1] = nextHeaderType; + } } -GtpV1Layer::GtpExtension GtpV1Layer::GtpExtension::createGtpExtension(uint8_t* data, size_t dataLen, uint8_t extType, uint16_t content) -{ - if (dataLen < 4*sizeof(uint8_t)) - { - return GtpExtension(); - } +GtpV1Layer::GtpExtension GtpV1Layer::GtpExtension::createGtpExtension( + uint8_t* data, size_t dataLen, uint8_t extType, uint16_t content) { + if (dataLen < 4 * sizeof(uint8_t)) { + return GtpExtension(); + } - data[0] = 1; - data[1] = (content >> 8); - data[2] = content & 0xff; - data[3] = 0; + data[0] = 1; + data[1] = (content >> 8); + data[2] = content & 0xff; + data[3] = 0; - return GtpV1Layer::GtpExtension(data, dataLen, extType); + return GtpV1Layer::GtpExtension(data, dataLen, extType); } - - - /// ================ /// GtpV1Layer class /// ================ +GtpV1Layer::GtpV1Layer(GtpV1MessageType messageType, uint32_t teid) { + init(messageType, teid, false, 0, false, 0); +} + +GtpV1Layer::GtpV1Layer(GtpV1MessageType messageType, uint32_t teid, + bool setSeqNum, uint16_t seqNum, bool setNpduNum, + uint8_t npduNum) { + init(messageType, teid, setSeqNum, seqNum, setNpduNum, npduNum); +} -GtpV1Layer::GtpV1Layer(GtpV1MessageType messageType, uint32_t teid) -{ - init(messageType, teid, false, 0, false, 0); -} - -GtpV1Layer::GtpV1Layer(GtpV1MessageType messageType, uint32_t teid, bool setSeqNum, uint16_t seqNum, bool setNpduNum, uint8_t npduNum) -{ - init(messageType, teid, setSeqNum, seqNum, setNpduNum, npduNum); -} - -void GtpV1Layer::init(GtpV1MessageType messageType, uint32_t teid, bool setSeqNum, uint16_t seqNum, bool setNpduNum, uint8_t npduNum) -{ - size_t dataLen = sizeof(gtpv1_header); - if (setSeqNum || setNpduNum) - { - dataLen += sizeof(gtpv1_header_extra); - } - - m_DataLen = dataLen; - m_Data = new uint8_t[dataLen]; - memset(m_Data, 0, dataLen); - m_Protocol = GTPv1; - - gtpv1_header* hdr = getHeader(); - hdr->version = 1; - hdr->protocolType = 1; - hdr->messageType = (uint8_t)messageType; - hdr->teid = htobe32(teid); - - if (setSeqNum || setNpduNum) - { - hdr->messageLength = htobe16(sizeof(gtpv1_header_extra)); - gtpv1_header_extra* extraHdr = getHeaderExtra(); - if (setSeqNum) - { - hdr->sequenceNumberFlag = 1; - extraHdr->sequenceNumber = htobe16(seqNum); - } - - if (setNpduNum) - { - hdr->npduNumberFlag = 1; - extraHdr->npduNumber = npduNum; - } - } -} - - -bool GtpV1Layer::isGTPv1(const uint8_t* data, size_t dataSize) -{ - if(data != nullptr && dataSize >= sizeof(gtpv1_header) && (data[0] & 0xE0) == 0x20) - { - return true; - } - - return false; -} - -GtpV1Layer::gtpv1_header_extra* GtpV1Layer::getHeaderExtra() const -{ - if (m_Data != nullptr && m_DataLen >= sizeof(gtpv1_header) + sizeof(gtpv1_header_extra)) - { - return (gtpv1_header_extra*)(m_Data + sizeof(gtpv1_header)); - } - - return nullptr; -} - -bool GtpV1Layer::getSequenceNumber(uint16_t& seqNumber) const -{ - gtpv1_header* header = getHeader(); - gtpv1_header_extra* headerExtra = getHeaderExtra(); - if (header != nullptr && headerExtra != nullptr && header->sequenceNumberFlag == 1) - { - seqNumber = be16toh(headerExtra->sequenceNumber); - return true; - } - - return false; -} - -bool GtpV1Layer::setSequenceNumber(const uint16_t seqNumber) -{ - // get GTP header - gtpv1_header* header = getHeader(); - if (header == nullptr) - { - PCPP_LOG_ERROR("Set sequence failed: GTP header is NULL"); - return false; - } - - // if all flags are unset then create the GTP extra header - if (header->npduNumberFlag == 0 && header->sequenceNumberFlag == 0 && header->extensionHeaderFlag == 0) - { - if (!extendLayer(sizeof(gtpv1_header), sizeof(gtpv1_header_extra))) - { - PCPP_LOG_ERROR("Set sequence failed: cannot extend layer"); - return false; - } - header = getHeader(); - } - - // get the extra header - gtpv1_header_extra* headerExtra = getHeaderExtra(); - if (headerExtra == nullptr) - { - PCPP_LOG_ERROR("Set sequence failed: extra header is NULL"); - return false; - } - - // set seq number - header->sequenceNumberFlag = 1; - headerExtra->sequenceNumber = htobe16(seqNumber); - - // extend GTP length - header->messageLength = htobe16(be16toh(header->messageLength) + sizeof(gtpv1_header_extra)); - - return true; -} - -bool GtpV1Layer::getNpduNumber(uint8_t& npduNum) const -{ - gtpv1_header* header = getHeader(); - gtpv1_header_extra* headerExtra = getHeaderExtra(); - if (header != nullptr && headerExtra != nullptr && header->npduNumberFlag == 1) - { - npduNum = headerExtra->npduNumber; - return true; - } - - return false; -} - -bool GtpV1Layer::setNpduNumber(const uint8_t npduNum) -{ - // get GTP header - gtpv1_header* header = getHeader(); - if (header == nullptr) - { - PCPP_LOG_ERROR("Set N-PDU failed: GTP header is NULL"); - return false; - } - - // if all flags are unset then create the GTP extra header - if (header->npduNumberFlag == 0 && header->sequenceNumberFlag == 0 && header->extensionHeaderFlag == 0) - { - if (!extendLayer(sizeof(gtpv1_header), sizeof(gtpv1_header_extra))) - { - PCPP_LOG_ERROR("Set N-PDU failed: cannot extend layer"); - return false; - } - header = getHeader(); - } - - // get the extra header - gtpv1_header_extra* headerExtra = getHeaderExtra(); - if (headerExtra == nullptr) - { - PCPP_LOG_ERROR("Set N-PDU failed: extra header is NULL"); - return false; - } - - // set N-PDU value - header->npduNumberFlag = 1; - headerExtra->npduNumber = npduNum; - - // extend GTP length - header->messageLength = htobe16(be16toh(header->messageLength) + sizeof(gtpv1_header_extra)); - - return true; -} - -bool GtpV1Layer::getNextExtensionHeaderType(uint8_t& nextExtType) const -{ - gtpv1_header* header = getHeader(); - gtpv1_header_extra* headerExtra = getHeaderExtra(); - if (header != nullptr && headerExtra != nullptr && header->extensionHeaderFlag == 1) - { - nextExtType = headerExtra->nextExtensionHeader; - return true; - } - - return false; -} - -GtpV1Layer::GtpExtension GtpV1Layer::getNextExtension() const -{ - uint8_t nextExtType = 0; - bool nextExtExists = getNextExtensionHeaderType(nextExtType); - if (!nextExtExists || nextExtType == 0 || m_DataLen <= sizeof(gtpv1_header) + sizeof(gtpv1_header_extra)) - { - return GtpV1Layer::GtpExtension(); - } - - return GtpV1Layer::GtpExtension(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) -{ - // get GTP header - gtpv1_header* header = getHeader(); - if (header == nullptr) - { - PCPP_LOG_ERROR("Add extension failed: GTP header is NULL"); - return GtpExtension(); - } - - size_t offsetForNewExtension = sizeof(gtpv1_header); - - // if all flags are unset then create the GTP extra header - if (header->npduNumberFlag == 0 && header->sequenceNumberFlag == 0 && header->extensionHeaderFlag == 0) - { - if (!extendLayer(offsetForNewExtension, sizeof(gtpv1_header_extra))) - { - PCPP_LOG_ERROR("Add extension failed: cannot extend layer"); - return GtpExtension(); - } - header = getHeader(); - } - - // get the extra header - gtpv1_header_extra* headerExtra = getHeaderExtra(); - if (headerExtra == nullptr) - { - PCPP_LOG_ERROR("Add extension failed: extra header is NULL"); - return GtpExtension(); - } - - offsetForNewExtension += sizeof(gtpv1_header_extra); - - // find the last GTP header extension - GtpV1Layer::GtpExtension lastExt = getNextExtension(); - - // go over the GTP header extensions - while (!lastExt.getNextExtension().isNull()) - { - // add ext total length to offset - offsetForNewExtension += lastExt.getTotalLength(); - lastExt = lastExt.getNextExtension(); - } - - // lastExt != null means layer contains 1 or more extensions - if (!lastExt.isNull()) - { - // add ext total length to offset - offsetForNewExtension += lastExt.getTotalLength(); - } - - // allocate extension space in layer (assuming extension length can only be 4 bytes) - if (!extendLayer(offsetForNewExtension, 4*sizeof(uint8_t))) - { - PCPP_LOG_ERROR("Add extension failed: cannot extend layer"); - return GtpExtension(); - } - - // lastExt != null means layer contains 1 or more extensions - if (!lastExt.isNull()) - { - // set the next header type in the last extension - lastExt.setNextHeaderType(extensionType); - } - else - { - // mark extension flags in the layer - header->extensionHeaderFlag = 1; - headerExtra->nextExtensionHeader = extensionType; - } - - // create the extension data and return the extension object to the user - return GtpV1Layer::GtpExtension::createGtpExtension( - m_Data + offsetForNewExtension, - m_DataLen - offsetForNewExtension, - extensionType, - extensionContent); -} - -GtpV1MessageType GtpV1Layer::getMessageType() const -{ - gtpv1_header* header = getHeader(); - - if (header == nullptr) - { - return GtpV1_MessageTypeUnknown; - } - - return (GtpV1MessageType)header->messageType; -} - -std::map createGtpV1MessageTypeToStringMap() -{ - std::map tempMap; - - tempMap[0] = "GTPv1 Message Type Unknown"; - tempMap[1] = "Echo Request"; - tempMap[2] = "Echo Response"; - tempMap[3] = "Version Not Supported"; - tempMap[4] = "Node Alive Request"; - tempMap[5] = "Node Alive Response"; - tempMap[6] = "Redirection Request"; - tempMap[7] = "Create PDP Context Request"; - tempMap[16] = "Create PDP Context Response"; - tempMap[17] = "Update PDP Context Request"; - tempMap[18] = "Update PDP Context Response"; - tempMap[19] = "Delete PDP Context Request"; - tempMap[20] = "Delete PDP Context Response"; - tempMap[22] = "Initiate PDP Context Activation Request"; - tempMap[23] = "Initiate PDP Context Activation Response"; - tempMap[26] = "Error Indication"; - tempMap[27] = "PDU Notification Request"; - tempMap[28] = "PDU Notification Response"; - tempMap[29] = "PDU Notification Reject Request"; - tempMap[30] = "PDU Notification Reject Response"; - tempMap[31] = "Supported Extensions Header Notification"; - tempMap[32] = "Send Routing for GPRS Request"; - tempMap[33] = "Send Routing for GPRS Response"; - tempMap[34] = "Failure Report Request"; - tempMap[35] = "Failure Report Response"; - tempMap[36] = "Note MS Present Request"; - tempMap[37] = "Note MS Present Response"; - tempMap[38] = "Identification Request"; - tempMap[39] = "Identification Response"; - tempMap[50] = "SGSN Context Request"; - tempMap[51] = "SGSN Context Response"; - tempMap[52] = "SGSN Context Acknowledge"; - tempMap[53] = "Forward Relocation Request"; - tempMap[54] = "Forward Relocation Response"; - tempMap[55] = "Forward Relocation Complete"; - tempMap[56] = "Relocation Cancel Request"; - tempMap[57] = "Relocation Cancel Response"; - tempMap[58] = "Forward SRNS Context"; - tempMap[59] = "Forward Relocation Complete Acknowledge"; - tempMap[60] = "Forward SRNS Context Acknowledge"; - tempMap[61] = "UE Registration Request"; - tempMap[62] = "UE Registration Response"; - tempMap[70] = "RAN Information Relay"; - tempMap[96] = "MBMS Notification Request"; - tempMap[97] = "MBMS Notification Response"; - tempMap[98] = "MBMS Notification Reject Request"; - tempMap[99] = "MBMS Notification Reject Response"; - tempMap[100] = "Create MBMS Notification Request"; - tempMap[101] = "Create MBMS Notification Response"; - tempMap[102] = "Update MBMS Notification Request"; - tempMap[103] = "Update MBMS Notification Response"; - tempMap[104] = "Delete MBMS Notification Request"; - tempMap[105] = "Delete MBMS Notification Response"; - tempMap[112] = "MBMS Registration Request"; - tempMap[113] = "MBMS Registration Response"; - tempMap[114] = "MBMS De-Registration Request"; - tempMap[115] = "MBMS De-Registration Response"; - tempMap[116] = "MBMS Session Start Request"; - tempMap[117] = "MBMS Session Start Response"; - tempMap[118] = "MBMS Session Stop Request"; - tempMap[119] = "MBMS Session Stop Response"; - tempMap[120] = "MBMS Session Update Request"; - tempMap[121] = "MBMS Session Update Response"; - tempMap[128] = "MS Info Change Request"; - tempMap[129] = "MS Info Change Response"; - tempMap[240] = "Data Record Transfer Request"; - tempMap[241] = "Data Record Transfer Response"; - tempMap[254] = "End Marker"; - tempMap[255] = "G-PDU"; - - return tempMap; -} - -const std::map GTPv1MsgTypeToStringMap = createGtpV1MessageTypeToStringMap(); - -std::string GtpV1Layer::getMessageTypeAsString() const -{ - gtpv1_header* header = getHeader(); - - if (header == nullptr) - { - return GTPv1MsgTypeToStringMap.find(0)->second; - } - - std::map::const_iterator iter = GTPv1MsgTypeToStringMap.find(header->messageType); - if (iter != GTPv1MsgTypeToStringMap.end()) - { - return iter->second; - } - else - { - return GTPv1MsgTypeToStringMap.find(0)->second; - } -} - -bool GtpV1Layer::isGTPUMessage() const -{ - gtpv1_header* header = getHeader(); - if (header == nullptr) - { - return false; - } - - return header->messageType == PCPP_GTP_V1_GPDU_MESSAGE_TYPE; -} - -bool GtpV1Layer::isGTPCMessage() const -{ - gtpv1_header* header = getHeader(); - if (header == nullptr) - { - return false; - } - - return header->messageType != PCPP_GTP_V1_GPDU_MESSAGE_TYPE; -} - - -void GtpV1Layer::parseNextLayer() -{ - size_t headerLen = getHeaderLen(); - if (headerLen < sizeof(gtpv1_header)) - { - // do nothing - return; - } - - gtpv1_header* header = getHeader(); - if (header->messageType != PCPP_GTP_V1_GPDU_MESSAGE_TYPE) - { - // this is a GTP-C message, hence it is the last layer - return; - } - - if (m_DataLen <= headerLen) - { - // no data beyond headerLen, nothing to parse further - return; - } - - // GTP-U message, try to parse the next layer - - uint8_t* payload = (uint8_t*)(m_Data + headerLen); - size_t payloadLen = m_DataLen - headerLen; - - uint8_t subProto = *payload; - if (subProto >= 0x45 && subProto <= 0x4e) - { - m_NextLayer = IPv4Layer::isDataValid(payload, payloadLen) - ? static_cast(new IPv4Layer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - } - else if ((subProto & 0xf0) == 0x60) - { - m_NextLayer = IPv6Layer::isDataValid(payload, payloadLen) - ? static_cast(new IPv6Layer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - } - else - { - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - } -} - -size_t GtpV1Layer::getHeaderLen() const -{ - gtpv1_header* header = getHeader(); - if (header == nullptr) - { - return 0; - } - - size_t res = sizeof(gtpv1_header); - - if (header->messageType != PCPP_GTP_V1_GPDU_MESSAGE_TYPE) - { - size_t msgLen = be16toh(header->messageLength); - res += (msgLen > m_DataLen - sizeof(gtpv1_header) ? m_DataLen - sizeof(gtpv1_header) : msgLen); - } - else - { - gtpv1_header_extra* headerExtra = getHeaderExtra(); - if (headerExtra != nullptr && (header->extensionHeaderFlag == 1 || header->sequenceNumberFlag == 1 || header->npduNumberFlag == 1)) - { - res += sizeof(gtpv1_header_extra); - GtpExtension nextExt = getNextExtension(); - while (!nextExt.isNull()) - { - res += nextExt.getTotalLength(); - nextExt = nextExt.getNextExtension(); - } - } - } - - return res; -} - -std::string GtpV1Layer::toString() const -{ - std::string res = "GTP v1 Layer"; - - gtpv1_header* header = getHeader(); - if (header != nullptr) - { - std::stringstream teidStream; - teidStream << be32toh(header->teid); - - std::string gtpu_gtpc; - if (header->messageType == PCPP_GTP_V1_GPDU_MESSAGE_TYPE) - { - gtpu_gtpc = "GTP-U message"; - } - else - { - gtpu_gtpc = "GTP-C message: " + getMessageTypeAsString(); - } - - res += ", " + gtpu_gtpc + ", TEID: " + teidStream.str(); - } - - return res; -} - -void GtpV1Layer::computeCalculateFields() -{ - gtpv1_header* hdr = getHeader(); - if (hdr == nullptr) - { - return; - } - - hdr->messageLength = htobe16(m_DataLen - sizeof(gtpv1_header)); -} - -} +void GtpV1Layer::init(GtpV1MessageType messageType, uint32_t teid, + bool setSeqNum, uint16_t seqNum, bool setNpduNum, + uint8_t npduNum) { + size_t dataLen = sizeof(gtpv1_header); + if (setSeqNum || setNpduNum) { + dataLen += sizeof(gtpv1_header_extra); + } + + m_DataLen = dataLen; + m_Data = new uint8_t[dataLen]; + memset(m_Data, 0, dataLen); + m_Protocol = GTPv1; + + gtpv1_header* hdr = getHeader(); + hdr->version = 1; + hdr->protocolType = 1; + hdr->messageType = (uint8_t)messageType; + hdr->teid = htobe32(teid); + + if (setSeqNum || setNpduNum) { + hdr->messageLength = htobe16(sizeof(gtpv1_header_extra)); + gtpv1_header_extra* extraHdr = getHeaderExtra(); + if (setSeqNum) { + hdr->sequenceNumberFlag = 1; + extraHdr->sequenceNumber = htobe16(seqNum); + } + + if (setNpduNum) { + hdr->npduNumberFlag = 1; + extraHdr->npduNumber = npduNum; + } + } +} + +bool GtpV1Layer::isGTPv1(const uint8_t* data, size_t dataSize) { + if (data != nullptr && dataSize >= sizeof(gtpv1_header) && + (data[0] & 0xE0) == 0x20) { + return true; + } + + return false; +} + +GtpV1Layer::gtpv1_header_extra* GtpV1Layer::getHeaderExtra() const { + if (m_Data != nullptr && + m_DataLen >= sizeof(gtpv1_header) + sizeof(gtpv1_header_extra)) { + return (gtpv1_header_extra*)(m_Data + sizeof(gtpv1_header)); + } + + return nullptr; +} + +bool GtpV1Layer::getSequenceNumber(uint16_t& seqNumber) const { + gtpv1_header* header = getHeader(); + gtpv1_header_extra* headerExtra = getHeaderExtra(); + if (header != nullptr && headerExtra != nullptr && + header->sequenceNumberFlag == 1) { + seqNumber = be16toh(headerExtra->sequenceNumber); + return true; + } + + return false; +} + +bool GtpV1Layer::setSequenceNumber(const uint16_t seqNumber) { + // get GTP header + gtpv1_header* header = getHeader(); + if (header == nullptr) { + PCPP_LOG_ERROR("Set sequence failed: GTP header is NULL"); + return false; + } + + // if all flags are unset then create the GTP extra header + if (header->npduNumberFlag == 0 && header->sequenceNumberFlag == 0 && + header->extensionHeaderFlag == 0) { + if (!extendLayer(sizeof(gtpv1_header), sizeof(gtpv1_header_extra))) { + PCPP_LOG_ERROR("Set sequence failed: cannot extend layer"); + return false; + } + header = getHeader(); + } + + // get the extra header + gtpv1_header_extra* headerExtra = getHeaderExtra(); + if (headerExtra == nullptr) { + PCPP_LOG_ERROR("Set sequence failed: extra header is NULL"); + return false; + } + + // set seq number + header->sequenceNumberFlag = 1; + headerExtra->sequenceNumber = htobe16(seqNumber); + + // extend GTP length + header->messageLength = + htobe16(be16toh(header->messageLength) + sizeof(gtpv1_header_extra)); + + return true; +} + +bool GtpV1Layer::getNpduNumber(uint8_t& npduNum) const { + gtpv1_header* header = getHeader(); + gtpv1_header_extra* headerExtra = getHeaderExtra(); + if (header != nullptr && headerExtra != nullptr && + header->npduNumberFlag == 1) { + npduNum = headerExtra->npduNumber; + return true; + } + + return false; +} + +bool GtpV1Layer::setNpduNumber(const uint8_t npduNum) { + // get GTP header + gtpv1_header* header = getHeader(); + if (header == nullptr) { + PCPP_LOG_ERROR("Set N-PDU failed: GTP header is NULL"); + return false; + } + + // if all flags are unset then create the GTP extra header + if (header->npduNumberFlag == 0 && header->sequenceNumberFlag == 0 && + header->extensionHeaderFlag == 0) { + if (!extendLayer(sizeof(gtpv1_header), sizeof(gtpv1_header_extra))) { + PCPP_LOG_ERROR("Set N-PDU failed: cannot extend layer"); + return false; + } + header = getHeader(); + } + + // get the extra header + gtpv1_header_extra* headerExtra = getHeaderExtra(); + if (headerExtra == nullptr) { + PCPP_LOG_ERROR("Set N-PDU failed: extra header is NULL"); + return false; + } + + // set N-PDU value + header->npduNumberFlag = 1; + headerExtra->npduNumber = npduNum; + + // extend GTP length + header->messageLength = + htobe16(be16toh(header->messageLength) + sizeof(gtpv1_header_extra)); + + return true; +} + +bool GtpV1Layer::getNextExtensionHeaderType(uint8_t& nextExtType) const { + gtpv1_header* header = getHeader(); + gtpv1_header_extra* headerExtra = getHeaderExtra(); + if (header != nullptr && headerExtra != nullptr && + header->extensionHeaderFlag == 1) { + nextExtType = headerExtra->nextExtensionHeader; + return true; + } + + return false; +} + +GtpV1Layer::GtpExtension GtpV1Layer::getNextExtension() const { + uint8_t nextExtType = 0; + bool nextExtExists = getNextExtensionHeaderType(nextExtType); + if (!nextExtExists || nextExtType == 0 || + m_DataLen <= sizeof(gtpv1_header) + sizeof(gtpv1_header_extra)) { + return GtpV1Layer::GtpExtension(); + } + + return GtpV1Layer::GtpExtension( + 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) { + // get GTP header + gtpv1_header* header = getHeader(); + if (header == nullptr) { + PCPP_LOG_ERROR("Add extension failed: GTP header is NULL"); + return GtpExtension(); + } + + size_t offsetForNewExtension = sizeof(gtpv1_header); + + // if all flags are unset then create the GTP extra header + if (header->npduNumberFlag == 0 && header->sequenceNumberFlag == 0 && + header->extensionHeaderFlag == 0) { + if (!extendLayer(offsetForNewExtension, sizeof(gtpv1_header_extra))) { + PCPP_LOG_ERROR("Add extension failed: cannot extend layer"); + return GtpExtension(); + } + header = getHeader(); + } + + // get the extra header + gtpv1_header_extra* headerExtra = getHeaderExtra(); + if (headerExtra == nullptr) { + PCPP_LOG_ERROR("Add extension failed: extra header is NULL"); + return GtpExtension(); + } + + offsetForNewExtension += sizeof(gtpv1_header_extra); + + // find the last GTP header extension + GtpV1Layer::GtpExtension lastExt = getNextExtension(); + + // go over the GTP header extensions + while (!lastExt.getNextExtension().isNull()) { + // add ext total length to offset + offsetForNewExtension += lastExt.getTotalLength(); + lastExt = lastExt.getNextExtension(); + } + + // lastExt != null means layer contains 1 or more extensions + if (!lastExt.isNull()) { + // add ext total length to offset + offsetForNewExtension += lastExt.getTotalLength(); + } + + // allocate extension space in layer (assuming extension length can only be 4 + // bytes) + if (!extendLayer(offsetForNewExtension, 4 * sizeof(uint8_t))) { + PCPP_LOG_ERROR("Add extension failed: cannot extend layer"); + return GtpExtension(); + } + + // lastExt != null means layer contains 1 or more extensions + if (!lastExt.isNull()) { + // set the next header type in the last extension + lastExt.setNextHeaderType(extensionType); + } else { + // mark extension flags in the layer + header->extensionHeaderFlag = 1; + headerExtra->nextExtensionHeader = extensionType; + } + + // create the extension data and return the extension object to the user + return GtpV1Layer::GtpExtension::createGtpExtension( + m_Data + offsetForNewExtension, m_DataLen - offsetForNewExtension, + extensionType, extensionContent); +} + +GtpV1MessageType GtpV1Layer::getMessageType() const { + gtpv1_header* header = getHeader(); + + if (header == nullptr) { + return GtpV1_MessageTypeUnknown; + } + + return (GtpV1MessageType)header->messageType; +} + +std::map createGtpV1MessageTypeToStringMap() { + std::map tempMap; + + tempMap[0] = "GTPv1 Message Type Unknown"; + tempMap[1] = "Echo Request"; + tempMap[2] = "Echo Response"; + tempMap[3] = "Version Not Supported"; + tempMap[4] = "Node Alive Request"; + tempMap[5] = "Node Alive Response"; + tempMap[6] = "Redirection Request"; + tempMap[7] = "Create PDP Context Request"; + tempMap[16] = "Create PDP Context Response"; + tempMap[17] = "Update PDP Context Request"; + tempMap[18] = "Update PDP Context Response"; + tempMap[19] = "Delete PDP Context Request"; + tempMap[20] = "Delete PDP Context Response"; + tempMap[22] = "Initiate PDP Context Activation Request"; + tempMap[23] = "Initiate PDP Context Activation Response"; + tempMap[26] = "Error Indication"; + tempMap[27] = "PDU Notification Request"; + tempMap[28] = "PDU Notification Response"; + tempMap[29] = "PDU Notification Reject Request"; + tempMap[30] = "PDU Notification Reject Response"; + tempMap[31] = "Supported Extensions Header Notification"; + tempMap[32] = "Send Routing for GPRS Request"; + tempMap[33] = "Send Routing for GPRS Response"; + tempMap[34] = "Failure Report Request"; + tempMap[35] = "Failure Report Response"; + tempMap[36] = "Note MS Present Request"; + tempMap[37] = "Note MS Present Response"; + tempMap[38] = "Identification Request"; + tempMap[39] = "Identification Response"; + tempMap[50] = "SGSN Context Request"; + tempMap[51] = "SGSN Context Response"; + tempMap[52] = "SGSN Context Acknowledge"; + tempMap[53] = "Forward Relocation Request"; + tempMap[54] = "Forward Relocation Response"; + tempMap[55] = "Forward Relocation Complete"; + tempMap[56] = "Relocation Cancel Request"; + tempMap[57] = "Relocation Cancel Response"; + tempMap[58] = "Forward SRNS Context"; + tempMap[59] = "Forward Relocation Complete Acknowledge"; + tempMap[60] = "Forward SRNS Context Acknowledge"; + tempMap[61] = "UE Registration Request"; + tempMap[62] = "UE Registration Response"; + tempMap[70] = "RAN Information Relay"; + tempMap[96] = "MBMS Notification Request"; + tempMap[97] = "MBMS Notification Response"; + tempMap[98] = "MBMS Notification Reject Request"; + tempMap[99] = "MBMS Notification Reject Response"; + tempMap[100] = "Create MBMS Notification Request"; + tempMap[101] = "Create MBMS Notification Response"; + tempMap[102] = "Update MBMS Notification Request"; + tempMap[103] = "Update MBMS Notification Response"; + tempMap[104] = "Delete MBMS Notification Request"; + tempMap[105] = "Delete MBMS Notification Response"; + tempMap[112] = "MBMS Registration Request"; + tempMap[113] = "MBMS Registration Response"; + tempMap[114] = "MBMS De-Registration Request"; + tempMap[115] = "MBMS De-Registration Response"; + tempMap[116] = "MBMS Session Start Request"; + tempMap[117] = "MBMS Session Start Response"; + tempMap[118] = "MBMS Session Stop Request"; + tempMap[119] = "MBMS Session Stop Response"; + tempMap[120] = "MBMS Session Update Request"; + tempMap[121] = "MBMS Session Update Response"; + tempMap[128] = "MS Info Change Request"; + tempMap[129] = "MS Info Change Response"; + tempMap[240] = "Data Record Transfer Request"; + tempMap[241] = "Data Record Transfer Response"; + tempMap[254] = "End Marker"; + tempMap[255] = "G-PDU"; + + return tempMap; +} + +const std::map GTPv1MsgTypeToStringMap = + createGtpV1MessageTypeToStringMap(); + +std::string GtpV1Layer::getMessageTypeAsString() const { + gtpv1_header* header = getHeader(); + + if (header == nullptr) { + return GTPv1MsgTypeToStringMap.find(0)->second; + } + + std::map::const_iterator iter = + GTPv1MsgTypeToStringMap.find(header->messageType); + if (iter != GTPv1MsgTypeToStringMap.end()) { + return iter->second; + } else { + return GTPv1MsgTypeToStringMap.find(0)->second; + } +} + +bool GtpV1Layer::isGTPUMessage() const { + gtpv1_header* header = getHeader(); + if (header == nullptr) { + return false; + } + + return header->messageType == PCPP_GTP_V1_GPDU_MESSAGE_TYPE; +} + +bool GtpV1Layer::isGTPCMessage() const { + gtpv1_header* header = getHeader(); + if (header == nullptr) { + return false; + } + + return header->messageType != PCPP_GTP_V1_GPDU_MESSAGE_TYPE; +} + +void GtpV1Layer::parseNextLayer() { + size_t headerLen = getHeaderLen(); + if (headerLen < sizeof(gtpv1_header)) { + // do nothing + return; + } + + gtpv1_header* header = getHeader(); + if (header->messageType != PCPP_GTP_V1_GPDU_MESSAGE_TYPE) { + // this is a GTP-C message, hence it is the last layer + return; + } + + if (m_DataLen <= headerLen) { + // no data beyond headerLen, nothing to parse further + return; + } + + // GTP-U message, try to parse the next layer + + uint8_t* payload = (uint8_t*)(m_Data + headerLen); + size_t payloadLen = m_DataLen - headerLen; + + uint8_t subProto = *payload; + if (subProto >= 0x45 && subProto <= 0x4e) { + m_NextLayer = IPv4Layer::isDataValid(payload, payloadLen) + ? static_cast( + new IPv4Layer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer( + payload, payloadLen, this, m_Packet)); + } else if ((subProto & 0xf0) == 0x60) { + m_NextLayer = IPv6Layer::isDataValid(payload, payloadLen) + ? static_cast( + new IPv6Layer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer( + payload, payloadLen, this, m_Packet)); + } else { + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + } +} + +size_t GtpV1Layer::getHeaderLen() const { + gtpv1_header* header = getHeader(); + if (header == nullptr) { + return 0; + } + + size_t res = sizeof(gtpv1_header); + + if (header->messageType != PCPP_GTP_V1_GPDU_MESSAGE_TYPE) { + size_t msgLen = be16toh(header->messageLength); + res += (msgLen > m_DataLen - sizeof(gtpv1_header) + ? m_DataLen - sizeof(gtpv1_header) + : msgLen); + } else { + gtpv1_header_extra* headerExtra = getHeaderExtra(); + if (headerExtra != nullptr && + (header->extensionHeaderFlag == 1 || header->sequenceNumberFlag == 1 || + header->npduNumberFlag == 1)) { + res += sizeof(gtpv1_header_extra); + GtpExtension nextExt = getNextExtension(); + while (!nextExt.isNull()) { + res += nextExt.getTotalLength(); + nextExt = nextExt.getNextExtension(); + } + } + } + + return res; +} + +std::string GtpV1Layer::toString() const { + std::string res = "GTP v1 Layer"; + + gtpv1_header* header = getHeader(); + if (header != nullptr) { + std::stringstream teidStream; + teidStream << be32toh(header->teid); + + std::string gtpu_gtpc; + if (header->messageType == PCPP_GTP_V1_GPDU_MESSAGE_TYPE) { + gtpu_gtpc = "GTP-U message"; + } else { + gtpu_gtpc = "GTP-C message: " + getMessageTypeAsString(); + } + + res += ", " + gtpu_gtpc + ", TEID: " + teidStream.str(); + } + + return res; +} + +void GtpV1Layer::computeCalculateFields() { + gtpv1_header* hdr = getHeader(); + if (hdr == nullptr) { + return; + } + + hdr->messageLength = htobe16(m_DataLen - sizeof(gtpv1_header)); +} + +} // namespace pcpp diff --git a/Packet++/src/HttpLayer.cpp b/Packet++/src/HttpLayer.cpp index b7fc02721a..d3514cd4c5 100644 --- a/Packet++/src/HttpLayer.cpp +++ b/Packet++/src/HttpLayer.cpp @@ -1,1011 +1,959 @@ #define LOG_MODULE PacketLogModuleHttpLayer -#include "Logger.h" -#include "GeneralUtils.h" #include "HttpLayer.h" -#include +#include "GeneralUtils.h" +#include "Logger.h" #include -#include #include -#include +#include +#include #include +#include -namespace pcpp -{ - +namespace pcpp { // -------- Class HttpMessage ----------------- +HeaderField* HttpMessage::addField(const std::string& fieldName, + const std::string& fieldValue) { + if (getFieldByName(fieldName) != nullptr) { + PCPP_LOG_ERROR("Field '" << fieldName << "' already exists!"); + return nullptr; + } -HeaderField* HttpMessage::addField(const std::string& fieldName, const std::string& fieldValue) -{ - if (getFieldByName(fieldName) != nullptr) - { - PCPP_LOG_ERROR("Field '" << fieldName << "' already exists!"); - return nullptr; - } - - return TextBasedProtocolMessage::addField(fieldName, fieldValue); + return TextBasedProtocolMessage::addField(fieldName, fieldValue); } -HeaderField* HttpMessage::addField(const HeaderField& newField) -{ - if (getFieldByName(newField.getFieldName()) != nullptr) - { - PCPP_LOG_ERROR("Field '" << newField.getFieldName() << "' already exists!"); - return nullptr; - } +HeaderField* HttpMessage::addField(const HeaderField& newField) { + if (getFieldByName(newField.getFieldName()) != nullptr) { + PCPP_LOG_ERROR("Field '" << newField.getFieldName() << "' already exists!"); + return nullptr; + } - return TextBasedProtocolMessage::addField(newField); + return TextBasedProtocolMessage::addField(newField); } -HeaderField* HttpMessage::insertField(HeaderField* prevField, const std::string& fieldName, const std::string& fieldValue) -{ - if (getFieldByName(fieldName) != nullptr) - { - PCPP_LOG_ERROR("Field '" << fieldName << "' already exists!"); - return nullptr; - } +HeaderField* HttpMessage::insertField(HeaderField* prevField, + const std::string& fieldName, + const std::string& fieldValue) { + if (getFieldByName(fieldName) != nullptr) { + PCPP_LOG_ERROR("Field '" << fieldName << "' already exists!"); + return nullptr; + } - return TextBasedProtocolMessage::insertField(prevField, fieldName, fieldValue); + return TextBasedProtocolMessage::insertField(prevField, fieldName, + fieldValue); } -HeaderField* HttpMessage::insertField(HeaderField* prevField, const HeaderField& newField) -{ - if (getFieldByName(newField.getFieldName()) != nullptr) - { - PCPP_LOG_ERROR("Field '" << newField.getFieldName() << "' already exists!"); - return nullptr; - } +HeaderField* HttpMessage::insertField(HeaderField* prevField, + const HeaderField& newField) { + if (getFieldByName(newField.getFieldName()) != nullptr) { + PCPP_LOG_ERROR("Field '" << newField.getFieldName() << "' already exists!"); + return nullptr; + } - return TextBasedProtocolMessage::insertField(prevField, newField); + return TextBasedProtocolMessage::insertField(prevField, newField); } - - // -------- Class HttpRequestLayer ----------------- -HttpRequestLayer::HttpRequestLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : HttpMessage(data, dataLen, prevLayer, packet) -{ - m_Protocol = HTTPRequest; - m_FirstLine = new HttpRequestFirstLine(this); - m_FieldsOffset = m_FirstLine->getSize(); - parseFields(); +HttpRequestLayer::HttpRequestLayer(uint8_t* data, size_t dataLen, + Layer* prevLayer, Packet* packet) + : HttpMessage(data, dataLen, prevLayer, packet) { + m_Protocol = HTTPRequest; + m_FirstLine = new HttpRequestFirstLine(this); + m_FieldsOffset = m_FirstLine->getSize(); + parseFields(); } -HttpRequestLayer::HttpRequestLayer(HttpMethod method, const std::string& uri, HttpVersion version) -{ - m_Protocol = HTTPRequest; - m_FirstLine = new HttpRequestFirstLine(this, method, version, uri); - m_FieldsOffset = m_FirstLine->getSize(); +HttpRequestLayer::HttpRequestLayer(HttpMethod method, const std::string& uri, + HttpVersion version) { + m_Protocol = HTTPRequest; + m_FirstLine = new HttpRequestFirstLine(this, method, version, uri); + m_FieldsOffset = m_FirstLine->getSize(); } -HttpRequestLayer::HttpRequestLayer(const HttpRequestLayer& other) : HttpMessage(other) -{ - m_FirstLine = new HttpRequestFirstLine(this); +HttpRequestLayer::HttpRequestLayer(const HttpRequestLayer& other) + : HttpMessage(other) { + m_FirstLine = new HttpRequestFirstLine(this); } -HttpRequestLayer& HttpRequestLayer::operator=(const HttpRequestLayer& other) -{ - HttpMessage::operator=(other); +HttpRequestLayer& HttpRequestLayer::operator=(const HttpRequestLayer& other) { + HttpMessage::operator=(other); - if (m_FirstLine != nullptr) - delete m_FirstLine; + if (m_FirstLine != nullptr) + delete m_FirstLine; - m_FirstLine = new HttpRequestFirstLine(this); + m_FirstLine = new HttpRequestFirstLine(this); - return *this; + return *this; } +std::string HttpRequestLayer::getUrl() const { + HeaderField* hostField = getFieldByName(PCPP_HTTP_HOST_FIELD); + if (hostField == nullptr) + return m_FirstLine->getUri(); -std::string HttpRequestLayer::getUrl() const -{ - HeaderField* hostField = getFieldByName(PCPP_HTTP_HOST_FIELD); - if (hostField == nullptr) - return m_FirstLine->getUri(); - - return hostField->getFieldValue() + m_FirstLine->getUri(); + return hostField->getFieldValue() + m_FirstLine->getUri(); } -HttpRequestLayer::~HttpRequestLayer() -{ - delete m_FirstLine; -} +HttpRequestLayer::~HttpRequestLayer() { delete m_FirstLine; } -std::string HttpRequestLayer::toString() const -{ - static const int maxLengthToPrint = 120; - std::string result = "HTTP request, "; - int size = m_FirstLine->getSize() - 2; // the -2 is to remove \r\n at the end of the first line - if (size <= 0) - { - result += std::string("CORRUPT DATA"); - return result; - } - if (size <= maxLengthToPrint) - { - char* firstLine = new char[size+1]; - strncpy(firstLine, (char*)m_Data, size); - firstLine[size] = 0; - result += std::string(firstLine); - delete[] firstLine; - } - else - { - char firstLine[maxLengthToPrint+1]; - strncpy(firstLine, (char*)m_Data, maxLengthToPrint-3); - firstLine[maxLengthToPrint-3] = '.'; - firstLine[maxLengthToPrint-2] = '.'; - firstLine[maxLengthToPrint-1] = '.'; - firstLine[maxLengthToPrint] = 0; - result += std::string(firstLine); - } +std::string HttpRequestLayer::toString() const { + static const int maxLengthToPrint = 120; + std::string result = "HTTP request, "; + int size = m_FirstLine->getSize() - + 2; // the -2 is to remove \r\n at the end of the first line + if (size <= 0) { + result += std::string("CORRUPT DATA"); + return result; + } + if (size <= maxLengthToPrint) { + char* firstLine = new char[size + 1]; + strncpy(firstLine, (char*)m_Data, size); + firstLine[size] = 0; + result += std::string(firstLine); + delete[] firstLine; + } else { + char firstLine[maxLengthToPrint + 1]; + strncpy(firstLine, (char*)m_Data, maxLengthToPrint - 3); + firstLine[maxLengthToPrint - 3] = '.'; + firstLine[maxLengthToPrint - 2] = '.'; + firstLine[maxLengthToPrint - 1] = '.'; + firstLine[maxLengthToPrint] = 0; + result += std::string(firstLine); + } - return result; + return result; } - - - - - - // -------- Class HttpRequestFirstLine ----------------- - -const std::string MethodEnumToString[9] = { - "GET", - "HEAD", - "POST", - "PUT", - "DELETE", - "TRACE", - "OPTIONS", - "CONNECT", - "PATCH" -}; - -const std::unordered_map HttpMethodStringToEnum { - {"GET", HttpRequestLayer::HttpMethod::HttpGET }, - {"HEAD", HttpRequestLayer::HttpMethod::HttpHEAD }, - {"POST", HttpRequestLayer::HttpMethod::HttpPOST }, - {"PUT", HttpRequestLayer::HttpMethod::HttpPUT }, - {"DELETE", HttpRequestLayer::HttpMethod::HttpDELETE }, - {"TRACE", HttpRequestLayer::HttpMethod::HttpTRACE }, - {"OPTIONS", HttpRequestLayer::HttpMethod::HttpOPTIONS }, - {"CONNECT", HttpRequestLayer::HttpMethod::HttpCONNECT }, - {"PATCH", HttpRequestLayer::HttpMethod::HttpPATCH } -}; - -const std::string VersionEnumToString[3] = { - "0.9", - "1.0", - "1.1" -}; - -const std::unordered_map HttpVersionStringToEnum { - { "0.9", HttpVersion::ZeroDotNine }, - { "1.0", HttpVersion::OneDotZero }, - { "1.1", HttpVersion::OneDotOne } -}; - - -HttpRequestFirstLine::HttpRequestFirstLine(HttpRequestLayer* httpRequest) : m_HttpRequest(httpRequest) -{ - m_Method = parseMethod((char*)m_HttpRequest->m_Data, m_HttpRequest->getDataLen()); - if (m_Method == HttpRequestLayer::HttpMethodUnknown) - { - m_UriOffset = -1; - PCPP_LOG_DEBUG("Couldn't resolve HTTP request method"); - m_IsComplete = false; - m_Version = HttpVersionUnknown; - m_VersionOffset = -1; - m_FirstLineEndOffset = m_HttpRequest->getDataLen(); - return; - } - else - m_UriOffset = MethodEnumToString[m_Method].length() + 1; - - parseVersion(); - if(m_VersionOffset < 0) - { - m_IsComplete = false; - m_FirstLineEndOffset = m_HttpRequest->getDataLen(); - return; - } - - char* endOfFirstLine; - if ((endOfFirstLine = (char*)memchr((char*)(m_HttpRequest->m_Data + m_VersionOffset), '\n', m_HttpRequest->m_DataLen-(size_t)m_VersionOffset)) != nullptr) - { - m_FirstLineEndOffset = endOfFirstLine - (char*)m_HttpRequest->m_Data + 1; - m_IsComplete = true; - } - else - { - m_FirstLineEndOffset = m_HttpRequest->getDataLen(); - m_IsComplete = false; - } - - if (Logger::getInstance().isDebugEnabled(PacketLogModuleHttpLayer)) - { - std::string method = m_Method == HttpRequestLayer::HttpMethodUnknown? "Unknown" : MethodEnumToString[m_Method]; - PCPP_LOG_DEBUG( - "Method='" << method << "'; " - << "HTTP version='" << VersionEnumToString[m_Version] << "'; " - << "URI='" << getUri() << "'"); - } -} - -HttpRequestFirstLine::HttpRequestFirstLine(HttpRequestLayer* httpRequest, HttpRequestLayer::HttpMethod method, HttpVersion version, const std::string &uri) -{ - try // throw(HttpRequestFirstLineException) - { - if (method == HttpRequestLayer::HttpMethodUnknown) - { - m_Exception.setMessage("Method supplied was HttpMethodUnknown"); - throw m_Exception; - } - - if (version == HttpVersionUnknown) - { - m_Exception.setMessage("Version supplied was HttpVersionUnknown"); - throw m_Exception; - } - - m_HttpRequest = httpRequest; - - m_Method = method; - m_Version = version; - - std::string firstLine = MethodEnumToString[m_Method] + " " + uri + " " + "HTTP/" + VersionEnumToString[m_Version] + "\r\n"; - - m_UriOffset = MethodEnumToString[m_Method].length() + 1; - m_FirstLineEndOffset = firstLine.length(); - m_VersionOffset = m_UriOffset + uri.length() + 6; - - m_HttpRequest->m_DataLen = firstLine.length(); - m_HttpRequest->m_Data = new uint8_t[m_HttpRequest->m_DataLen]; - memcpy(m_HttpRequest->m_Data, firstLine.c_str(), m_HttpRequest->m_DataLen); - - m_IsComplete = true; - } - catch(const HttpRequestFirstLineException&) - { - throw; - } - catch(...) - { - std::terminate(); - } -} - -HttpRequestLayer::HttpMethod HttpRequestFirstLine::parseMethod(const char* data, size_t dataLen) -{ - if (!data || dataLen < 4) - { - return HttpRequestLayer::HttpMethodUnknown; - } - - size_t spaceIndex = 0; - while (spaceIndex < dataLen && data[spaceIndex] != ' ' ) - { - spaceIndex++; - } - - if (spaceIndex == 0 || spaceIndex == dataLen) - { - return HttpRequestLayer::HttpMethodUnknown; - } - - auto methodAdEnum = HttpMethodStringToEnum.find(std::string(data, data + spaceIndex)); - if (methodAdEnum == HttpMethodStringToEnum.end()) - { - return HttpRequestLayer::HttpMethodUnknown; - } - return methodAdEnum->second; -} - -void HttpRequestFirstLine::parseVersion() -{ - char* data = (char*)(m_HttpRequest->m_Data + m_UriOffset); - char* verPos = cross_platform_memmem(data, m_HttpRequest->getDataLen() - m_UriOffset, " HTTP/", 6); - if (verPos == nullptr) - { - m_Version = HttpVersionUnknown; - m_VersionOffset = -1; - return; - } - - // verify packet doesn't end before the version, meaning still left place for " HTTP/x.y" (9 chars) - std::ptrdiff_t actualLen = verPos + 9 - (char*)m_HttpRequest->m_Data; - if (static_cast(actualLen) > m_HttpRequest->getDataLen()) - { - m_Version = HttpVersionUnknown; - m_VersionOffset = -1; - return; - } - - //skip " HTTP/" (6 chars) - verPos += 6; - auto versionAsEnum = HttpVersionStringToEnum.find(std::string(verPos, verPos + 3)); - if (versionAsEnum == HttpVersionStringToEnum.end()) - { - m_Version = HttpVersionUnknown; - } - else - { - m_Version = versionAsEnum->second; - } - - m_VersionOffset = verPos - (char*)m_HttpRequest->m_Data; -} - -bool HttpRequestFirstLine::setMethod(HttpRequestLayer::HttpMethod newMethod) -{ - if (newMethod == HttpRequestLayer::HttpMethodUnknown) - { - PCPP_LOG_ERROR("Requested method is HttpMethodUnknown"); - return false; - } - - //extend or shorten layer - int lengthDifference = MethodEnumToString[newMethod].length() - MethodEnumToString[m_Method].length(); - if (lengthDifference > 0) - { - if (!m_HttpRequest->extendLayer(0, lengthDifference)) - { - PCPP_LOG_ERROR("Cannot change layer size"); - return false; - } - } - else if (lengthDifference < 0) - { - if (!m_HttpRequest->shortenLayer(0, 0-lengthDifference)) - { - PCPP_LOG_ERROR("Cannot change layer size"); - return false; - - } - } - - if (lengthDifference != 0) - m_HttpRequest->shiftFieldsOffset(m_HttpRequest->getFirstField(), lengthDifference); - - memcpy(m_HttpRequest->m_Data, MethodEnumToString[newMethod].c_str(), MethodEnumToString[newMethod].length()); - - m_Method = newMethod; - m_UriOffset += lengthDifference; - m_VersionOffset += lengthDifference; - - return true; -} - -std::string HttpRequestFirstLine::getUri() const -{ - std::string result; - if (m_UriOffset != -1 && m_VersionOffset != -1) - result.assign((const char*)m_HttpRequest->m_Data + m_UriOffset, m_VersionOffset - 6 - m_UriOffset); - - //else first line is illegal, return empty string - - return result; -} - -bool HttpRequestFirstLine::setUri(std::string newUri) -{ - // make sure the new URI begins with "/" - if (newUri.compare(0, 1, "/") != 0) - newUri = "/" + newUri; - - //extend or shorten layer - std::string currentUri = getUri(); - int lengthDifference = newUri.length() - currentUri.length(); - if (lengthDifference > 0) - { - if (!m_HttpRequest->extendLayer(m_UriOffset, lengthDifference)) - { - PCPP_LOG_ERROR("Cannot change layer size"); - return false; - } - } - else if (lengthDifference < 0) - { - if (!m_HttpRequest->shortenLayer(m_UriOffset, 0-lengthDifference)) - { - PCPP_LOG_ERROR("Cannot change layer size"); - return false; - } - } - - if (lengthDifference != 0) - m_HttpRequest->shiftFieldsOffset(m_HttpRequest->getFirstField(), lengthDifference); - - memcpy(m_HttpRequest->m_Data + m_UriOffset, newUri.c_str(), newUri.length()); - - m_VersionOffset += lengthDifference; - - return true; +const std::string MethodEnumToString[9] = {"GET", "HEAD", "POST", + "PUT", "DELETE", "TRACE", + "OPTIONS", "CONNECT", "PATCH"}; + +const std::unordered_map + HttpMethodStringToEnum{ + {"GET", HttpRequestLayer::HttpMethod::HttpGET}, + {"HEAD", HttpRequestLayer::HttpMethod::HttpHEAD}, + {"POST", HttpRequestLayer::HttpMethod::HttpPOST}, + {"PUT", HttpRequestLayer::HttpMethod::HttpPUT}, + {"DELETE", HttpRequestLayer::HttpMethod::HttpDELETE}, + {"TRACE", HttpRequestLayer::HttpMethod::HttpTRACE}, + {"OPTIONS", HttpRequestLayer::HttpMethod::HttpOPTIONS}, + {"CONNECT", HttpRequestLayer::HttpMethod::HttpCONNECT}, + {"PATCH", HttpRequestLayer::HttpMethod::HttpPATCH}}; + +const std::string VersionEnumToString[3] = {"0.9", "1.0", "1.1"}; + +const std::unordered_map HttpVersionStringToEnum{ + {"0.9", HttpVersion::ZeroDotNine}, + {"1.0", HttpVersion::OneDotZero}, + {"1.1", HttpVersion::OneDotOne}}; + +HttpRequestFirstLine::HttpRequestFirstLine(HttpRequestLayer* httpRequest) + : m_HttpRequest(httpRequest) { + m_Method = + parseMethod((char*)m_HttpRequest->m_Data, m_HttpRequest->getDataLen()); + if (m_Method == HttpRequestLayer::HttpMethodUnknown) { + m_UriOffset = -1; + PCPP_LOG_DEBUG("Couldn't resolve HTTP request method"); + m_IsComplete = false; + m_Version = HttpVersionUnknown; + m_VersionOffset = -1; + m_FirstLineEndOffset = m_HttpRequest->getDataLen(); + return; + } else + m_UriOffset = MethodEnumToString[m_Method].length() + 1; + + parseVersion(); + if (m_VersionOffset < 0) { + m_IsComplete = false; + m_FirstLineEndOffset = m_HttpRequest->getDataLen(); + return; + } + + char* endOfFirstLine; + if ((endOfFirstLine = (char*)memchr( + (char*)(m_HttpRequest->m_Data + m_VersionOffset), '\n', + m_HttpRequest->m_DataLen - (size_t)m_VersionOffset)) != nullptr) { + m_FirstLineEndOffset = endOfFirstLine - (char*)m_HttpRequest->m_Data + 1; + m_IsComplete = true; + } else { + m_FirstLineEndOffset = m_HttpRequest->getDataLen(); + m_IsComplete = false; + } + + if (Logger::getInstance().isDebugEnabled(PacketLogModuleHttpLayer)) { + std::string method = m_Method == HttpRequestLayer::HttpMethodUnknown + ? "Unknown" + : MethodEnumToString[m_Method]; + PCPP_LOG_DEBUG("Method='" << method << "'; " + << "HTTP version='" + << VersionEnumToString[m_Version] << "'; " + << "URI='" << getUri() << "'"); + } +} + +HttpRequestFirstLine::HttpRequestFirstLine(HttpRequestLayer* httpRequest, + HttpRequestLayer::HttpMethod method, + HttpVersion version, + const std::string& uri) { + try // throw(HttpRequestFirstLineException) + { + if (method == HttpRequestLayer::HttpMethodUnknown) { + m_Exception.setMessage("Method supplied was HttpMethodUnknown"); + throw m_Exception; + } + + if (version == HttpVersionUnknown) { + m_Exception.setMessage("Version supplied was HttpVersionUnknown"); + throw m_Exception; + } + + m_HttpRequest = httpRequest; + + m_Method = method; + m_Version = version; + + std::string firstLine = MethodEnumToString[m_Method] + " " + uri + " " + + "HTTP/" + VersionEnumToString[m_Version] + "\r\n"; + + m_UriOffset = MethodEnumToString[m_Method].length() + 1; + m_FirstLineEndOffset = firstLine.length(); + m_VersionOffset = m_UriOffset + uri.length() + 6; + + m_HttpRequest->m_DataLen = firstLine.length(); + m_HttpRequest->m_Data = new uint8_t[m_HttpRequest->m_DataLen]; + memcpy(m_HttpRequest->m_Data, firstLine.c_str(), m_HttpRequest->m_DataLen); + + m_IsComplete = true; + } + catch (const HttpRequestFirstLineException&) { + throw; + } + catch (...) { + std::terminate(); + } +} + +HttpRequestLayer::HttpMethod HttpRequestFirstLine::parseMethod(const char* data, + size_t dataLen) { + if (!data || dataLen < 4) { + return HttpRequestLayer::HttpMethodUnknown; + } + + size_t spaceIndex = 0; + while (spaceIndex < dataLen && data[spaceIndex] != ' ') { + spaceIndex++; + } + + if (spaceIndex == 0 || spaceIndex == dataLen) { + return HttpRequestLayer::HttpMethodUnknown; + } + + auto methodAdEnum = + HttpMethodStringToEnum.find(std::string(data, data + spaceIndex)); + if (methodAdEnum == HttpMethodStringToEnum.end()) { + return HttpRequestLayer::HttpMethodUnknown; + } + return methodAdEnum->second; +} + +void HttpRequestFirstLine::parseVersion() { + char* data = (char*)(m_HttpRequest->m_Data + m_UriOffset); + char* verPos = cross_platform_memmem( + data, m_HttpRequest->getDataLen() - m_UriOffset, " HTTP/", 6); + if (verPos == nullptr) { + m_Version = HttpVersionUnknown; + m_VersionOffset = -1; + return; + } + + // verify packet doesn't end before the version, meaning still left place for + // " HTTP/x.y" (9 chars) + std::ptrdiff_t actualLen = verPos + 9 - (char*)m_HttpRequest->m_Data; + if (static_cast(actualLen) > m_HttpRequest->getDataLen()) { + m_Version = HttpVersionUnknown; + m_VersionOffset = -1; + return; + } + + // skip " HTTP/" (6 chars) + verPos += 6; + auto versionAsEnum = + HttpVersionStringToEnum.find(std::string(verPos, verPos + 3)); + if (versionAsEnum == HttpVersionStringToEnum.end()) { + m_Version = HttpVersionUnknown; + } else { + m_Version = versionAsEnum->second; + } + + m_VersionOffset = verPos - (char*)m_HttpRequest->m_Data; +} + +bool HttpRequestFirstLine::setMethod(HttpRequestLayer::HttpMethod newMethod) { + if (newMethod == HttpRequestLayer::HttpMethodUnknown) { + PCPP_LOG_ERROR("Requested method is HttpMethodUnknown"); + return false; + } + + // extend or shorten layer + int lengthDifference = MethodEnumToString[newMethod].length() - + MethodEnumToString[m_Method].length(); + if (lengthDifference > 0) { + if (!m_HttpRequest->extendLayer(0, lengthDifference)) { + PCPP_LOG_ERROR("Cannot change layer size"); + return false; + } + } else if (lengthDifference < 0) { + if (!m_HttpRequest->shortenLayer(0, 0 - lengthDifference)) { + PCPP_LOG_ERROR("Cannot change layer size"); + return false; + } + } + + if (lengthDifference != 0) + m_HttpRequest->shiftFieldsOffset(m_HttpRequest->getFirstField(), + lengthDifference); + + memcpy(m_HttpRequest->m_Data, MethodEnumToString[newMethod].c_str(), + MethodEnumToString[newMethod].length()); + + m_Method = newMethod; + m_UriOffset += lengthDifference; + m_VersionOffset += lengthDifference; + + return true; +} + +std::string HttpRequestFirstLine::getUri() const { + std::string result; + if (m_UriOffset != -1 && m_VersionOffset != -1) + result.assign((const char*)m_HttpRequest->m_Data + m_UriOffset, + m_VersionOffset - 6 - m_UriOffset); + + // else first line is illegal, return empty string + + return result; +} + +bool HttpRequestFirstLine::setUri(std::string newUri) { + // make sure the new URI begins with "/" + if (newUri.compare(0, 1, "/") != 0) + newUri = "/" + newUri; + + // extend or shorten layer + std::string currentUri = getUri(); + int lengthDifference = newUri.length() - currentUri.length(); + if (lengthDifference > 0) { + if (!m_HttpRequest->extendLayer(m_UriOffset, lengthDifference)) { + PCPP_LOG_ERROR("Cannot change layer size"); + return false; + } + } else if (lengthDifference < 0) { + if (!m_HttpRequest->shortenLayer(m_UriOffset, 0 - lengthDifference)) { + PCPP_LOG_ERROR("Cannot change layer size"); + return false; + } + } + + if (lengthDifference != 0) + m_HttpRequest->shiftFieldsOffset(m_HttpRequest->getFirstField(), + lengthDifference); + + memcpy(m_HttpRequest->m_Data + m_UriOffset, newUri.c_str(), newUri.length()); + + m_VersionOffset += lengthDifference; + + return true; } -void HttpRequestFirstLine::setVersion(HttpVersion newVersion) -{ - if (m_VersionOffset == -1) - return; +void HttpRequestFirstLine::setVersion(HttpVersion newVersion) { + if (m_VersionOffset == -1) + return; - if (newVersion == HttpVersionUnknown) - return; + if (newVersion == HttpVersionUnknown) + return; - char* verPos = (char*)(m_HttpRequest->m_Data + m_VersionOffset); - memcpy(verPos, VersionEnumToString[newVersion].c_str(), 3); + char* verPos = (char*)(m_HttpRequest->m_Data + m_VersionOffset); + memcpy(verPos, VersionEnumToString[newVersion].c_str(), 3); - m_Version = newVersion; + m_Version = newVersion; } - - - - - // -------- Class HttpResponseLayer ----------------- -static const std::unordered_map intStatusCodeMap = { - {100, HttpResponseStatusCode::Http100Continue}, - {101, HttpResponseStatusCode::Http101SwitchingProtocols}, - {102, HttpResponseStatusCode::Http102Processing}, - {103, HttpResponseStatusCode::Http103EarlyHints}, - {200, HttpResponseStatusCode::Http200OK}, - {201, HttpResponseStatusCode::Http201Created}, - {202, HttpResponseStatusCode::Http202Accepted}, - {203, HttpResponseStatusCode::Http203NonAuthoritativeInformation}, - {204, HttpResponseStatusCode::Http204NoContent}, - {205, HttpResponseStatusCode::Http205ResetContent}, - {206, HttpResponseStatusCode::Http206PartialContent}, - {207, HttpResponseStatusCode::Http207MultiStatus}, - {208, HttpResponseStatusCode::Http208AlreadyReported}, - {226, HttpResponseStatusCode::Http226IMUsed}, - {300, HttpResponseStatusCode::Http300MultipleChoices}, - {301, HttpResponseStatusCode::Http301MovedPermanently}, - {302, HttpResponseStatusCode::Http302}, - {303, HttpResponseStatusCode::Http303SeeOther}, - {304, HttpResponseStatusCode::Http304NotModified}, - {305, HttpResponseStatusCode::Http305UseProxy}, - {306, HttpResponseStatusCode::Http306SwitchProxy}, - {307, HttpResponseStatusCode::Http307TemporaryRedirect}, - {308, HttpResponseStatusCode::Http308PermanentRedirect}, - {400, HttpResponseStatusCode::Http400BadRequest}, - {401, HttpResponseStatusCode::Http401Unauthorized}, - {402, HttpResponseStatusCode::Http402PaymentRequired}, - {403, HttpResponseStatusCode::Http403Forbidden}, - {404, HttpResponseStatusCode::Http404NotFound}, - {405, HttpResponseStatusCode::Http405MethodNotAllowed}, - {406, HttpResponseStatusCode::Http406NotAcceptable}, - {407, HttpResponseStatusCode::Http407ProxyAuthenticationRequired}, - {408, HttpResponseStatusCode::Http408RequestTimeout}, - {409, HttpResponseStatusCode::Http409Conflict}, - {410, HttpResponseStatusCode::Http410Gone}, - {411, HttpResponseStatusCode::Http411LengthRequired}, - {412, HttpResponseStatusCode::Http412PreconditionFailed}, - {413, HttpResponseStatusCode::Http413RequestEntityTooLarge}, - {414, HttpResponseStatusCode::Http414RequestURITooLong}, - {415, HttpResponseStatusCode::Http415UnsupportedMediaType}, - {416, HttpResponseStatusCode::Http416RequestedRangeNotSatisfiable}, - {417, HttpResponseStatusCode::Http417ExpectationFailed}, - {418, HttpResponseStatusCode::Http418ImATeapot}, - {419, HttpResponseStatusCode::Http419AuthenticationTimeout}, - {420, HttpResponseStatusCode::Http420}, - {421, HttpResponseStatusCode::Http421MisdirectedRequest}, - {422, HttpResponseStatusCode::Http422UnprocessableEntity}, - {423, HttpResponseStatusCode::Http423Locked}, - {424, HttpResponseStatusCode::Http424FailedDependency}, - {425, HttpResponseStatusCode::Http425TooEarly}, - {426, HttpResponseStatusCode::Http426UpgradeRequired}, - {428, HttpResponseStatusCode::Http428PreconditionRequired}, - {429, HttpResponseStatusCode::Http429TooManyRequests}, - {431, HttpResponseStatusCode::Http431RequestHeaderFieldsTooLarge}, - {440, HttpResponseStatusCode::Http440LoginTimeout}, - {444, HttpResponseStatusCode::Http444NoResponse}, - {449, HttpResponseStatusCode::Http449RetryWith}, - {450, HttpResponseStatusCode::Http450BlockedByWindowsParentalControls}, - {451, HttpResponseStatusCode::Http451}, - {494, HttpResponseStatusCode::Http494RequestHeaderTooLarge}, - {495, HttpResponseStatusCode::Http495CertError}, - {496, HttpResponseStatusCode::Http496NoCert}, - {497, HttpResponseStatusCode::Http497HTTPtoHTTPS}, - {498, HttpResponseStatusCode::Http498TokenExpiredInvalid}, - {499, HttpResponseStatusCode::Http499}, - {500, HttpResponseStatusCode::Http500InternalServerError}, - {501, HttpResponseStatusCode::Http501NotImplemented}, - {502, HttpResponseStatusCode::Http502BadGateway}, - {503, HttpResponseStatusCode::Http503ServiceUnavailable}, - {504, HttpResponseStatusCode::Http504GatewayTimeout}, - {505, HttpResponseStatusCode::Http505HTTPVersionNotSupported}, - {506, HttpResponseStatusCode::Http506VariantAlsoNegotiates}, - {507, HttpResponseStatusCode::Http507InsufficientStorage}, - {508, HttpResponseStatusCode::Http508LoopDetected}, - {509, HttpResponseStatusCode::Http509BandwidthLimitExceeded}, - {510, HttpResponseStatusCode::Http510NotExtended}, - {511, HttpResponseStatusCode::Http511NetworkAuthenticationRequired}, - {520, HttpResponseStatusCode::Http520OriginError}, - {521, HttpResponseStatusCode::Http521WebServerIsDown}, - {522, HttpResponseStatusCode::Http522ConnectionTimedOut}, - {523, HttpResponseStatusCode::Http523ProxyDeclinedRequest}, - {524, HttpResponseStatusCode::Http524aTimeoutOccurred}, - {598, HttpResponseStatusCode::Http598NetworkReadTimeoutError}, - {599, HttpResponseStatusCode::Http599NetworkConnectTimeoutError}, +static const std::unordered_map intStatusCodeMap = + { + {100, HttpResponseStatusCode::Http100Continue}, + {101, HttpResponseStatusCode::Http101SwitchingProtocols}, + {102, HttpResponseStatusCode::Http102Processing}, + {103, HttpResponseStatusCode::Http103EarlyHints}, + {200, HttpResponseStatusCode::Http200OK}, + {201, HttpResponseStatusCode::Http201Created}, + {202, HttpResponseStatusCode::Http202Accepted}, + {203, HttpResponseStatusCode::Http203NonAuthoritativeInformation}, + {204, HttpResponseStatusCode::Http204NoContent}, + {205, HttpResponseStatusCode::Http205ResetContent}, + {206, HttpResponseStatusCode::Http206PartialContent}, + {207, HttpResponseStatusCode::Http207MultiStatus}, + {208, HttpResponseStatusCode::Http208AlreadyReported}, + {226, HttpResponseStatusCode::Http226IMUsed}, + {300, HttpResponseStatusCode::Http300MultipleChoices}, + {301, HttpResponseStatusCode::Http301MovedPermanently}, + {302, HttpResponseStatusCode::Http302}, + {303, HttpResponseStatusCode::Http303SeeOther}, + {304, HttpResponseStatusCode::Http304NotModified}, + {305, HttpResponseStatusCode::Http305UseProxy}, + {306, HttpResponseStatusCode::Http306SwitchProxy}, + {307, HttpResponseStatusCode::Http307TemporaryRedirect}, + {308, HttpResponseStatusCode::Http308PermanentRedirect}, + {400, HttpResponseStatusCode::Http400BadRequest}, + {401, HttpResponseStatusCode::Http401Unauthorized}, + {402, HttpResponseStatusCode::Http402PaymentRequired}, + {403, HttpResponseStatusCode::Http403Forbidden}, + {404, HttpResponseStatusCode::Http404NotFound}, + {405, HttpResponseStatusCode::Http405MethodNotAllowed}, + {406, HttpResponseStatusCode::Http406NotAcceptable}, + {407, HttpResponseStatusCode::Http407ProxyAuthenticationRequired}, + {408, HttpResponseStatusCode::Http408RequestTimeout}, + {409, HttpResponseStatusCode::Http409Conflict}, + {410, HttpResponseStatusCode::Http410Gone}, + {411, HttpResponseStatusCode::Http411LengthRequired}, + {412, HttpResponseStatusCode::Http412PreconditionFailed}, + {413, HttpResponseStatusCode::Http413RequestEntityTooLarge}, + {414, HttpResponseStatusCode::Http414RequestURITooLong}, + {415, HttpResponseStatusCode::Http415UnsupportedMediaType}, + {416, HttpResponseStatusCode::Http416RequestedRangeNotSatisfiable}, + {417, HttpResponseStatusCode::Http417ExpectationFailed}, + {418, HttpResponseStatusCode::Http418ImATeapot}, + {419, HttpResponseStatusCode::Http419AuthenticationTimeout}, + {420, HttpResponseStatusCode::Http420}, + {421, HttpResponseStatusCode::Http421MisdirectedRequest}, + {422, HttpResponseStatusCode::Http422UnprocessableEntity}, + {423, HttpResponseStatusCode::Http423Locked}, + {424, HttpResponseStatusCode::Http424FailedDependency}, + {425, HttpResponseStatusCode::Http425TooEarly}, + {426, HttpResponseStatusCode::Http426UpgradeRequired}, + {428, HttpResponseStatusCode::Http428PreconditionRequired}, + {429, HttpResponseStatusCode::Http429TooManyRequests}, + {431, HttpResponseStatusCode::Http431RequestHeaderFieldsTooLarge}, + {440, HttpResponseStatusCode::Http440LoginTimeout}, + {444, HttpResponseStatusCode::Http444NoResponse}, + {449, HttpResponseStatusCode::Http449RetryWith}, + {450, HttpResponseStatusCode::Http450BlockedByWindowsParentalControls}, + {451, HttpResponseStatusCode::Http451}, + {494, HttpResponseStatusCode::Http494RequestHeaderTooLarge}, + {495, HttpResponseStatusCode::Http495CertError}, + {496, HttpResponseStatusCode::Http496NoCert}, + {497, HttpResponseStatusCode::Http497HTTPtoHTTPS}, + {498, HttpResponseStatusCode::Http498TokenExpiredInvalid}, + {499, HttpResponseStatusCode::Http499}, + {500, HttpResponseStatusCode::Http500InternalServerError}, + {501, HttpResponseStatusCode::Http501NotImplemented}, + {502, HttpResponseStatusCode::Http502BadGateway}, + {503, HttpResponseStatusCode::Http503ServiceUnavailable}, + {504, HttpResponseStatusCode::Http504GatewayTimeout}, + {505, HttpResponseStatusCode::Http505HTTPVersionNotSupported}, + {506, HttpResponseStatusCode::Http506VariantAlsoNegotiates}, + {507, HttpResponseStatusCode::Http507InsufficientStorage}, + {508, HttpResponseStatusCode::Http508LoopDetected}, + {509, HttpResponseStatusCode::Http509BandwidthLimitExceeded}, + {510, HttpResponseStatusCode::Http510NotExtended}, + {511, HttpResponseStatusCode::Http511NetworkAuthenticationRequired}, + {520, HttpResponseStatusCode::Http520OriginError}, + {521, HttpResponseStatusCode::Http521WebServerIsDown}, + {522, HttpResponseStatusCode::Http522ConnectionTimedOut}, + {523, HttpResponseStatusCode::Http523ProxyDeclinedRequest}, + {524, HttpResponseStatusCode::Http524aTimeoutOccurred}, + {598, HttpResponseStatusCode::Http598NetworkReadTimeoutError}, + {599, HttpResponseStatusCode::Http599NetworkConnectTimeoutError}, }; -HttpResponseStatusCode::HttpResponseStatusCode(const int &statusCodeNumber, const std::string& statusMessage) -{ - if(statusMessage != "") - { - m_CustomizedMessage = statusMessage; - } - - if(intStatusCodeMap.find(statusCodeNumber) != intStatusCodeMap.end()) - { - m_Value = intStatusCodeMap.at(statusCodeNumber); - return; - } - - if(statusCodeNumber >= 100 && statusCodeNumber <= 199) - { - m_Value = HttpResponseStatusCode::HttpStatus1xxCodeUnknown; - } - else if (statusCodeNumber >= 200 && statusCodeNumber <= 299) - { - m_Value = HttpResponseStatusCode::HttpStatus2xxCodeUnknown; - } - else if (statusCodeNumber >= 300 && statusCodeNumber <= 399) - { - m_Value = HttpResponseStatusCode::HttpStatus3xxCodeUnknown; - } - else if (statusCodeNumber >= 400 && statusCodeNumber <= 499) - { - m_Value = HttpResponseStatusCode::HttpStatus4xxCodeUnknown; - } - else if (statusCodeNumber >= 500 && statusCodeNumber <= 599) - { - m_Value = HttpResponseStatusCode::HttpStatus5xxCodeUnknown; - } +HttpResponseStatusCode::HttpResponseStatusCode( + const int& statusCodeNumber, const std::string& statusMessage) { + if (statusMessage != "") { + m_CustomizedMessage = statusMessage; + } + + if (intStatusCodeMap.find(statusCodeNumber) != intStatusCodeMap.end()) { + m_Value = intStatusCodeMap.at(statusCodeNumber); + return; + } + + if (statusCodeNumber >= 100 && statusCodeNumber <= 199) { + m_Value = HttpResponseStatusCode::HttpStatus1xxCodeUnknown; + } else if (statusCodeNumber >= 200 && statusCodeNumber <= 299) { + m_Value = HttpResponseStatusCode::HttpStatus2xxCodeUnknown; + } else if (statusCodeNumber >= 300 && statusCodeNumber <= 399) { + m_Value = HttpResponseStatusCode::HttpStatus3xxCodeUnknown; + } else if (statusCodeNumber >= 400 && statusCodeNumber <= 499) { + m_Value = HttpResponseStatusCode::HttpStatus4xxCodeUnknown; + } else if (statusCodeNumber >= 500 && statusCodeNumber <= 599) { + m_Value = HttpResponseStatusCode::HttpStatus5xxCodeUnknown; + } } /** * @struct HttpResponseStatusCodeHash - * @brief The helper structure for hash HttpResponseStatusCode while using std::unordered_map + * @brief The helper structure for hash HttpResponseStatusCode while using + * std::unordered_map */ -struct HttpResponseStatusCodeHash -{ - size_t operator()(const HttpResponseStatusCode& status) const - { - return static_cast(status); - } +struct HttpResponseStatusCodeHash { + size_t operator()(const HttpResponseStatusCode& status) const { + return static_cast(status); + } }; -static const std::unordered_map statusCodeExplanationStringMap = { - {HttpResponseStatusCode::Http100Continue, "Continue"}, - {HttpResponseStatusCode::Http101SwitchingProtocols, "Switching Protocols"}, - {HttpResponseStatusCode::Http102Processing, "Processing"}, - {HttpResponseStatusCode::Http103EarlyHints, "Early Hints"}, - {HttpResponseStatusCode::Http200OK, "OK"}, - {HttpResponseStatusCode::Http201Created, "Created"}, - {HttpResponseStatusCode::Http202Accepted, "Accepted"}, - {HttpResponseStatusCode::Http203NonAuthoritativeInformation, "Non-Authoritative Information"}, - {HttpResponseStatusCode::Http204NoContent, "No Content"}, - {HttpResponseStatusCode::Http205ResetContent, "Reset Content"}, - {HttpResponseStatusCode::Http206PartialContent, "Partial Content"}, - {HttpResponseStatusCode::Http207MultiStatus, "Multi-Status"}, - {HttpResponseStatusCode::Http208AlreadyReported, "Already Reported"}, - {HttpResponseStatusCode::Http226IMUsed, "IM Used"}, - {HttpResponseStatusCode::Http300MultipleChoices, "Multiple Choices"}, - {HttpResponseStatusCode::Http301MovedPermanently, "Moved Permanently"}, - {HttpResponseStatusCode::Http302, "(various messages)"}, - {HttpResponseStatusCode::Http303SeeOther, "See Other"}, - {HttpResponseStatusCode::Http304NotModified, "Not Modified"}, - {HttpResponseStatusCode::Http305UseProxy, "Use Proxy"}, - {HttpResponseStatusCode::Http306SwitchProxy, "Switch Proxy"}, - {HttpResponseStatusCode::Http307TemporaryRedirect, "Temporary Redirect"}, - {HttpResponseStatusCode::Http308PermanentRedirect, "Permanent Redirect"}, - {HttpResponseStatusCode::Http400BadRequest, "Bad Request"}, - {HttpResponseStatusCode::Http401Unauthorized, "Unauthorized"}, - {HttpResponseStatusCode::Http402PaymentRequired, "Payment Required"}, - {HttpResponseStatusCode::Http403Forbidden, "Forbidden"}, - {HttpResponseStatusCode::Http404NotFound, "Not Found"}, - {HttpResponseStatusCode::Http405MethodNotAllowed, "Method Not Allowed"}, - {HttpResponseStatusCode::Http406NotAcceptable, "Not Acceptable"}, - {HttpResponseStatusCode::Http407ProxyAuthenticationRequired, "Proxy Authentication Required"}, - {HttpResponseStatusCode::Http408RequestTimeout, "Request Timeout"}, - {HttpResponseStatusCode::Http409Conflict, "Conflict"}, - {HttpResponseStatusCode::Http410Gone, "Gone"}, - {HttpResponseStatusCode::Http411LengthRequired, "Length Required"}, - {HttpResponseStatusCode::Http412PreconditionFailed, "Precondition Failed"}, - {HttpResponseStatusCode::Http413RequestEntityTooLarge, "Request Entity Too Large"}, - {HttpResponseStatusCode::Http414RequestURITooLong, "Request-URI Too Long"}, - {HttpResponseStatusCode::Http415UnsupportedMediaType, "Unsupported Media Type"}, - {HttpResponseStatusCode::Http416RequestedRangeNotSatisfiable, "Requested Range Not Satisfiable"}, - {HttpResponseStatusCode::Http417ExpectationFailed, "Expectation Failed"}, - {HttpResponseStatusCode::Http418ImATeapot, "I'm a teapot"}, - {HttpResponseStatusCode::Http419AuthenticationTimeout, "Authentication Timeout"}, - {HttpResponseStatusCode::Http420, "(various messages)"}, - {HttpResponseStatusCode::Http421MisdirectedRequest, "Misdirected Request"}, - {HttpResponseStatusCode::Http422UnprocessableEntity, "Unprocessable Entity"}, - {HttpResponseStatusCode::Http423Locked, "Locked"}, - {HttpResponseStatusCode::Http424FailedDependency, "Failed Dependency"}, - {HttpResponseStatusCode::Http425TooEarly, "Too Early"}, - {HttpResponseStatusCode::Http426UpgradeRequired, "Upgrade Required"}, - {HttpResponseStatusCode::Http428PreconditionRequired, "Precondition Required"}, - {HttpResponseStatusCode::Http429TooManyRequests, "Too Many Requests"}, - {HttpResponseStatusCode::Http431RequestHeaderFieldsTooLarge, "Request Header Fields Too Large"}, - {HttpResponseStatusCode::Http440LoginTimeout, "Login Timeout"}, - {HttpResponseStatusCode::Http444NoResponse, "No Response"}, - {HttpResponseStatusCode::Http449RetryWith, "Retry With"}, - {HttpResponseStatusCode::Http450BlockedByWindowsParentalControls, "Blocked by Windows Parental Controls"}, - {HttpResponseStatusCode::Http451, "(various messages)"}, - {HttpResponseStatusCode::Http494RequestHeaderTooLarge, "Request Header Too Large"}, - {HttpResponseStatusCode::Http495CertError, "Cert Error"}, - {HttpResponseStatusCode::Http496NoCert, "No Cert"}, - {HttpResponseStatusCode::Http497HTTPtoHTTPS, "HTTP to HTTPS"}, - {HttpResponseStatusCode::Http498TokenExpiredInvalid, "Token expired/invalid"}, - {HttpResponseStatusCode::Http499, "(various messages)"}, - {HttpResponseStatusCode::Http500InternalServerError, "Internal Server Error"}, - {HttpResponseStatusCode::Http501NotImplemented, "Not Implemented"}, - {HttpResponseStatusCode::Http502BadGateway, "Bad Gateway"}, - {HttpResponseStatusCode::Http503ServiceUnavailable, "Service Unavailable"}, - {HttpResponseStatusCode::Http504GatewayTimeout, "Gateway Timeout"}, - {HttpResponseStatusCode::Http505HTTPVersionNotSupported, "HTTP Version Not Supported"}, - {HttpResponseStatusCode::Http506VariantAlsoNegotiates, "Variant Also Negotiates"}, - {HttpResponseStatusCode::Http507InsufficientStorage, "Insufficient Storage"}, - {HttpResponseStatusCode::Http508LoopDetected, "Loop Detected"}, - {HttpResponseStatusCode::Http509BandwidthLimitExceeded, "Bandwidth Limit Exceeded"}, - {HttpResponseStatusCode::Http510NotExtended, "Not Extended"}, - {HttpResponseStatusCode::Http511NetworkAuthenticationRequired, "Network Authentication Required"}, - {HttpResponseStatusCode::Http520OriginError, "Origin Error"}, - {HttpResponseStatusCode::Http521WebServerIsDown, "Web server is down"}, - {HttpResponseStatusCode::Http522ConnectionTimedOut, "Connection timed out"}, - {HttpResponseStatusCode::Http523ProxyDeclinedRequest, "Proxy Declined Request"}, - {HttpResponseStatusCode::Http524aTimeoutOccurred, "A timeout occurred"}, - {HttpResponseStatusCode::Http598NetworkReadTimeoutError, "Network read timeout error"}, - {HttpResponseStatusCode::Http599NetworkConnectTimeoutError, "Network connect timeout error"}, - {HttpResponseStatusCode::HttpStatus1xxCodeUnknown, "1XX Status Code Unknown"}, - {HttpResponseStatusCode::HttpStatus2xxCodeUnknown, "2XX Status Code Unknown"}, - {HttpResponseStatusCode::HttpStatus3xxCodeUnknown, "3XX Status Code Unknown"}, - {HttpResponseStatusCode::HttpStatus4xxCodeUnknown, "4XX Status Code Unknown"}, - {HttpResponseStatusCode::HttpStatus5xxCodeUnknown, "5XX Status Code Unknown"}, - {HttpResponseStatusCode::HttpStatusCodeUnknown, "Status Code Unknown"}, +static const std::unordered_map + statusCodeExplanationStringMap = { + {HttpResponseStatusCode::Http100Continue, "Continue"}, + {HttpResponseStatusCode::Http101SwitchingProtocols, + "Switching Protocols"}, + {HttpResponseStatusCode::Http102Processing, "Processing"}, + {HttpResponseStatusCode::Http103EarlyHints, "Early Hints"}, + {HttpResponseStatusCode::Http200OK, "OK"}, + {HttpResponseStatusCode::Http201Created, "Created"}, + {HttpResponseStatusCode::Http202Accepted, "Accepted"}, + {HttpResponseStatusCode::Http203NonAuthoritativeInformation, + "Non-Authoritative Information"}, + {HttpResponseStatusCode::Http204NoContent, "No Content"}, + {HttpResponseStatusCode::Http205ResetContent, "Reset Content"}, + {HttpResponseStatusCode::Http206PartialContent, "Partial Content"}, + {HttpResponseStatusCode::Http207MultiStatus, "Multi-Status"}, + {HttpResponseStatusCode::Http208AlreadyReported, "Already Reported"}, + {HttpResponseStatusCode::Http226IMUsed, "IM Used"}, + {HttpResponseStatusCode::Http300MultipleChoices, "Multiple Choices"}, + {HttpResponseStatusCode::Http301MovedPermanently, "Moved Permanently"}, + {HttpResponseStatusCode::Http302, "(various messages)"}, + {HttpResponseStatusCode::Http303SeeOther, "See Other"}, + {HttpResponseStatusCode::Http304NotModified, "Not Modified"}, + {HttpResponseStatusCode::Http305UseProxy, "Use Proxy"}, + {HttpResponseStatusCode::Http306SwitchProxy, "Switch Proxy"}, + {HttpResponseStatusCode::Http307TemporaryRedirect, + "Temporary Redirect"}, + {HttpResponseStatusCode::Http308PermanentRedirect, + "Permanent Redirect"}, + {HttpResponseStatusCode::Http400BadRequest, "Bad Request"}, + {HttpResponseStatusCode::Http401Unauthorized, "Unauthorized"}, + {HttpResponseStatusCode::Http402PaymentRequired, "Payment Required"}, + {HttpResponseStatusCode::Http403Forbidden, "Forbidden"}, + {HttpResponseStatusCode::Http404NotFound, "Not Found"}, + {HttpResponseStatusCode::Http405MethodNotAllowed, "Method Not Allowed"}, + {HttpResponseStatusCode::Http406NotAcceptable, "Not Acceptable"}, + {HttpResponseStatusCode::Http407ProxyAuthenticationRequired, + "Proxy Authentication Required"}, + {HttpResponseStatusCode::Http408RequestTimeout, "Request Timeout"}, + {HttpResponseStatusCode::Http409Conflict, "Conflict"}, + {HttpResponseStatusCode::Http410Gone, "Gone"}, + {HttpResponseStatusCode::Http411LengthRequired, "Length Required"}, + {HttpResponseStatusCode::Http412PreconditionFailed, + "Precondition Failed"}, + {HttpResponseStatusCode::Http413RequestEntityTooLarge, + "Request Entity Too Large"}, + {HttpResponseStatusCode::Http414RequestURITooLong, + "Request-URI Too Long"}, + {HttpResponseStatusCode::Http415UnsupportedMediaType, + "Unsupported Media Type"}, + {HttpResponseStatusCode::Http416RequestedRangeNotSatisfiable, + "Requested Range Not Satisfiable"}, + {HttpResponseStatusCode::Http417ExpectationFailed, + "Expectation Failed"}, + {HttpResponseStatusCode::Http418ImATeapot, "I'm a teapot"}, + {HttpResponseStatusCode::Http419AuthenticationTimeout, + "Authentication Timeout"}, + {HttpResponseStatusCode::Http420, "(various messages)"}, + {HttpResponseStatusCode::Http421MisdirectedRequest, + "Misdirected Request"}, + {HttpResponseStatusCode::Http422UnprocessableEntity, + "Unprocessable Entity"}, + {HttpResponseStatusCode::Http423Locked, "Locked"}, + {HttpResponseStatusCode::Http424FailedDependency, "Failed Dependency"}, + {HttpResponseStatusCode::Http425TooEarly, "Too Early"}, + {HttpResponseStatusCode::Http426UpgradeRequired, "Upgrade Required"}, + {HttpResponseStatusCode::Http428PreconditionRequired, + "Precondition Required"}, + {HttpResponseStatusCode::Http429TooManyRequests, "Too Many Requests"}, + {HttpResponseStatusCode::Http431RequestHeaderFieldsTooLarge, + "Request Header Fields Too Large"}, + {HttpResponseStatusCode::Http440LoginTimeout, "Login Timeout"}, + {HttpResponseStatusCode::Http444NoResponse, "No Response"}, + {HttpResponseStatusCode::Http449RetryWith, "Retry With"}, + {HttpResponseStatusCode::Http450BlockedByWindowsParentalControls, + "Blocked by Windows Parental Controls"}, + {HttpResponseStatusCode::Http451, "(various messages)"}, + {HttpResponseStatusCode::Http494RequestHeaderTooLarge, + "Request Header Too Large"}, + {HttpResponseStatusCode::Http495CertError, "Cert Error"}, + {HttpResponseStatusCode::Http496NoCert, "No Cert"}, + {HttpResponseStatusCode::Http497HTTPtoHTTPS, "HTTP to HTTPS"}, + {HttpResponseStatusCode::Http498TokenExpiredInvalid, + "Token expired/invalid"}, + {HttpResponseStatusCode::Http499, "(various messages)"}, + {HttpResponseStatusCode::Http500InternalServerError, + "Internal Server Error"}, + {HttpResponseStatusCode::Http501NotImplemented, "Not Implemented"}, + {HttpResponseStatusCode::Http502BadGateway, "Bad Gateway"}, + {HttpResponseStatusCode::Http503ServiceUnavailable, + "Service Unavailable"}, + {HttpResponseStatusCode::Http504GatewayTimeout, "Gateway Timeout"}, + {HttpResponseStatusCode::Http505HTTPVersionNotSupported, + "HTTP Version Not Supported"}, + {HttpResponseStatusCode::Http506VariantAlsoNegotiates, + "Variant Also Negotiates"}, + {HttpResponseStatusCode::Http507InsufficientStorage, + "Insufficient Storage"}, + {HttpResponseStatusCode::Http508LoopDetected, "Loop Detected"}, + {HttpResponseStatusCode::Http509BandwidthLimitExceeded, + "Bandwidth Limit Exceeded"}, + {HttpResponseStatusCode::Http510NotExtended, "Not Extended"}, + {HttpResponseStatusCode::Http511NetworkAuthenticationRequired, + "Network Authentication Required"}, + {HttpResponseStatusCode::Http520OriginError, "Origin Error"}, + {HttpResponseStatusCode::Http521WebServerIsDown, "Web server is down"}, + {HttpResponseStatusCode::Http522ConnectionTimedOut, + "Connection timed out"}, + {HttpResponseStatusCode::Http523ProxyDeclinedRequest, + "Proxy Declined Request"}, + {HttpResponseStatusCode::Http524aTimeoutOccurred, "A timeout occurred"}, + {HttpResponseStatusCode::Http598NetworkReadTimeoutError, + "Network read timeout error"}, + {HttpResponseStatusCode::Http599NetworkConnectTimeoutError, + "Network connect timeout error"}, + {HttpResponseStatusCode::HttpStatus1xxCodeUnknown, + "1XX Status Code Unknown"}, + {HttpResponseStatusCode::HttpStatus2xxCodeUnknown, + "2XX Status Code Unknown"}, + {HttpResponseStatusCode::HttpStatus3xxCodeUnknown, + "3XX Status Code Unknown"}, + {HttpResponseStatusCode::HttpStatus4xxCodeUnknown, + "4XX Status Code Unknown"}, + {HttpResponseStatusCode::HttpStatus5xxCodeUnknown, + "5XX Status Code Unknown"}, + {HttpResponseStatusCode::HttpStatusCodeUnknown, "Status Code Unknown"}, }; -HttpResponseStatusCode::HttpResponseStatusCode(const Value& statusCode, const std::string& statusMessage) : m_Value(statusCode) -{ - if(statusMessage != "") - { - m_CustomizedMessage = statusMessage; - } +HttpResponseStatusCode::HttpResponseStatusCode(const Value& statusCode, + const std::string& statusMessage) + : m_Value(statusCode) { + if (statusMessage != "") { + m_CustomizedMessage = statusMessage; + } +} + +std::string HttpResponseStatusCode::getMessage() const { + if (m_CustomizedMessage != "") { + return m_CustomizedMessage; + } + return statusCodeExplanationStringMap.at(m_Value); } -std::string HttpResponseStatusCode::getMessage() const -{ - if(m_CustomizedMessage != "") - { - return m_CustomizedMessage; - } - return statusCodeExplanationStringMap.at(m_Value); +HttpResponseLayer::HttpResponseLayer(uint8_t* data, size_t dataLen, + Layer* prevLayer, Packet* packet) + : HttpMessage(data, dataLen, prevLayer, packet) { + m_Protocol = HTTPResponse; + m_FirstLine = new HttpResponseFirstLine(this); + m_FieldsOffset = m_FirstLine->getSize(); + parseFields(); } -HttpResponseLayer::HttpResponseLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : HttpMessage(data, dataLen, prevLayer, packet) -{ - m_Protocol = HTTPResponse; - m_FirstLine = new HttpResponseFirstLine(this); - m_FieldsOffset = m_FirstLine->getSize(); - parseFields(); +HttpResponseLayer::HttpResponseLayer(HttpVersion version, + const HttpResponseStatusCode& statusCode, + const std::string& statusCodeString) { + m_Protocol = HTTPResponse; + m_FirstLine = new HttpResponseFirstLine( + this, version, HttpResponseStatusCode(statusCode, statusCodeString)); + m_FieldsOffset = m_FirstLine->getSize(); } -HttpResponseLayer::HttpResponseLayer(HttpVersion version, const HttpResponseStatusCode& statusCode, const std::string& statusCodeString) -{ - m_Protocol = HTTPResponse; - m_FirstLine = new HttpResponseFirstLine(this, version, HttpResponseStatusCode(statusCode, statusCodeString)); - m_FieldsOffset = m_FirstLine->getSize(); +HttpResponseLayer::HttpResponseLayer(HttpVersion version, + const HttpResponseStatusCode& statusCode) { + m_Protocol = HTTPResponse; + m_FirstLine = new HttpResponseFirstLine(this, version, statusCode); + m_FieldsOffset = m_FirstLine->getSize(); } -HttpResponseLayer::HttpResponseLayer(HttpVersion version, const HttpResponseStatusCode& statusCode) -{ - m_Protocol = HTTPResponse; - m_FirstLine = new HttpResponseFirstLine(this, version, statusCode); - m_FieldsOffset = m_FirstLine->getSize(); -} +HttpResponseLayer::~HttpResponseLayer() { delete m_FirstLine; } -HttpResponseLayer::~HttpResponseLayer() -{ - delete m_FirstLine; +HttpResponseLayer::HttpResponseLayer(const HttpResponseLayer& other) + : HttpMessage(other) { + m_FirstLine = new HttpResponseFirstLine(this); } +HttpResponseLayer& +HttpResponseLayer::operator=(const HttpResponseLayer& other) { + HttpMessage::operator=(other); -HttpResponseLayer::HttpResponseLayer(const HttpResponseLayer& other) : HttpMessage(other) -{ - m_FirstLine = new HttpResponseFirstLine(this); -} - -HttpResponseLayer& HttpResponseLayer::operator=(const HttpResponseLayer& other) -{ - HttpMessage::operator=(other); + if (m_FirstLine != nullptr) + delete m_FirstLine; - if (m_FirstLine != nullptr) - delete m_FirstLine; + m_FirstLine = new HttpResponseFirstLine(this); - m_FirstLine = new HttpResponseFirstLine(this); - - return *this; + return *this; } +HeaderField* +HttpResponseLayer::setContentLength(int contentLength, + const std::string& prevFieldName) { + std::ostringstream contentLengthAsString; + contentLengthAsString << contentLength; + std::string contentLengthFieldName(PCPP_HTTP_CONTENT_LENGTH_FIELD); + HeaderField* contentLengthField = getFieldByName(contentLengthFieldName); + if (contentLengthField == nullptr) { + HeaderField* prevField = getFieldByName(prevFieldName); + contentLengthField = insertField(prevField, PCPP_HTTP_CONTENT_LENGTH_FIELD, + contentLengthAsString.str()); + } else + contentLengthField->setFieldValue(contentLengthAsString.str()); -HeaderField* HttpResponseLayer::setContentLength(int contentLength, const std::string &prevFieldName) -{ - std::ostringstream contentLengthAsString; - contentLengthAsString << contentLength; - std::string contentLengthFieldName(PCPP_HTTP_CONTENT_LENGTH_FIELD); - HeaderField* contentLengthField = getFieldByName(contentLengthFieldName); - if (contentLengthField == nullptr) - { - HeaderField* prevField = getFieldByName(prevFieldName); - contentLengthField = insertField(prevField, PCPP_HTTP_CONTENT_LENGTH_FIELD, contentLengthAsString.str()); - } - else - contentLengthField->setFieldValue(contentLengthAsString.str()); - - return contentLengthField; + return contentLengthField; } -int HttpResponseLayer::getContentLength() const -{ - std::string contentLengthFieldName(PCPP_HTTP_CONTENT_LENGTH_FIELD); - std::transform(contentLengthFieldName.begin(), contentLengthFieldName.end(), contentLengthFieldName.begin(), ::tolower); - HeaderField* contentLengthField = getFieldByName(contentLengthFieldName); - if (contentLengthField != nullptr) - return atoi(contentLengthField->getFieldValue().c_str()); - return 0; +int HttpResponseLayer::getContentLength() const { + std::string contentLengthFieldName(PCPP_HTTP_CONTENT_LENGTH_FIELD); + std::transform(contentLengthFieldName.begin(), contentLengthFieldName.end(), + contentLengthFieldName.begin(), ::tolower); + HeaderField* contentLengthField = getFieldByName(contentLengthFieldName); + if (contentLengthField != nullptr) + return atoi(contentLengthField->getFieldValue().c_str()); + return 0; } -std::string HttpResponseLayer::toString() const -{ - static const int maxLengthToPrint = 120; - std::string result = "HTTP response, "; - int size = m_FirstLine->getSize() - 2; // the -2 is to remove \r\n at the end of the first line - if (size <= maxLengthToPrint) - { - char* firstLine = new char[size+1]; - strncpy(firstLine, (char*)m_Data, size); - firstLine[size] = 0; - result += std::string(firstLine); - delete[] firstLine; - } - else - { - char firstLine[maxLengthToPrint+1]; - strncpy(firstLine, (char*)m_Data, maxLengthToPrint-3); - firstLine[maxLengthToPrint-3] = '.'; - firstLine[maxLengthToPrint-2] = '.'; - firstLine[maxLengthToPrint-1] = '.'; - firstLine[maxLengthToPrint] = 0; - result += std::string(firstLine); - } +std::string HttpResponseLayer::toString() const { + static const int maxLengthToPrint = 120; + std::string result = "HTTP response, "; + int size = m_FirstLine->getSize() - + 2; // the -2 is to remove \r\n at the end of the first line + if (size <= maxLengthToPrint) { + char* firstLine = new char[size + 1]; + strncpy(firstLine, (char*)m_Data, size); + firstLine[size] = 0; + result += std::string(firstLine); + delete[] firstLine; + } else { + char firstLine[maxLengthToPrint + 1]; + strncpy(firstLine, (char*)m_Data, maxLengthToPrint - 3); + firstLine[maxLengthToPrint - 3] = '.'; + firstLine[maxLengthToPrint - 2] = '.'; + firstLine[maxLengthToPrint - 1] = '.'; + firstLine[maxLengthToPrint] = 0; + result += std::string(firstLine); + } - return result; + return result; } - // -------- Class HttpResponseFirstLine ----------------- - -int HttpResponseFirstLine::getStatusCodeAsInt() const -{ - return m_StatusCode.toInt(); +int HttpResponseFirstLine::getStatusCodeAsInt() const { + return m_StatusCode.toInt(); } -std::string HttpResponseFirstLine::getStatusCodeString() const -{ - if (!m_StatusCode.isUnsupportedCode()) - { - return m_StatusCode.getMessage(); - } +std::string HttpResponseFirstLine::getStatusCodeString() const { + if (!m_StatusCode.isUnsupportedCode()) { + return m_StatusCode.getMessage(); + } - //else first line is illegal, return empty string - return ""; + // else first line is illegal, return empty string + return ""; } -bool HttpResponseFirstLine::setStatusCode(const HttpResponseStatusCode& newStatusCode, const std::string& statusCodeString) -{ - return setStatusCode(HttpResponseStatusCode(newStatusCode, statusCodeString)); +bool HttpResponseFirstLine::setStatusCode( + const HttpResponseStatusCode& newStatusCode, + const std::string& statusCodeString) { + return setStatusCode(HttpResponseStatusCode(newStatusCode, statusCodeString)); } -bool HttpResponseFirstLine::setStatusCode(const HttpResponseStatusCode& newStatusCode) -{ - if (newStatusCode.isUnsupportedCode()) - { - PCPP_LOG_ERROR("Requested status code is " << newStatusCode.toString() << ":" << statusCodeExplanationStringMap.at(newStatusCode)); - return false; - } - - //extend or shorten layer - - size_t statusStringOffset = 13; - auto newStatusCodeMessage = newStatusCode.getMessage(); - - int lengthDifference = newStatusCodeMessage.length() - getStatusCodeString().length(); - if (lengthDifference > 0) - { - if (!m_HttpResponse->extendLayer(statusStringOffset, lengthDifference)) - { - PCPP_LOG_ERROR("Cannot change layer size"); - return false; - } - } - else if (lengthDifference < 0) - { - if (!m_HttpResponse->shortenLayer(statusStringOffset, 0-lengthDifference)) - { - PCPP_LOG_ERROR("Cannot change layer size"); - return false; - - } - } - - if (lengthDifference != 0) - m_HttpResponse->shiftFieldsOffset(m_HttpResponse->getFirstField(), lengthDifference); - - // copy status string - memcpy(m_HttpResponse->m_Data+statusStringOffset, newStatusCodeMessage.c_str(), newStatusCodeMessage.length()); - - // change status code - memcpy(m_HttpResponse->m_Data+9, newStatusCode.toString().c_str(), 3); - - m_StatusCode = newStatusCode; - - m_FirstLineEndOffset += lengthDifference; - - return true; - -} - -void HttpResponseFirstLine::setVersion(HttpVersion newVersion) -{ - if (newVersion == HttpVersionUnknown) - return; - - char* verPos = (char*)(m_HttpResponse->m_Data + 5); - memcpy(verPos, VersionEnumToString[newVersion].c_str(), 3); - - m_Version = newVersion; -} - -HttpResponseStatusCode HttpResponseFirstLine::parseStatusCode(const char* data, size_t dataLen) -{ - // minimum data should be 12B long: "HTTP/x.y XXX" - if (!data || dataLen < 12) - { - return HttpResponseStatusCode::HttpStatusCodeUnknown; - } - - const std::string codeString = std::string(data + 9, 3); - - if(codeString.empty() || (std::find_if(codeString.begin(), codeString.end(), [](unsigned char c){ return !std::isdigit(c); }) != codeString.end())) - { - return HttpResponseStatusCode::HttpStatusCodeUnknown; - } +bool HttpResponseFirstLine::setStatusCode( + const HttpResponseStatusCode& newStatusCode) { + if (newStatusCode.isUnsupportedCode()) { + PCPP_LOG_ERROR("Requested status code is " + << newStatusCode.toString() << ":" + << statusCodeExplanationStringMap.at(newStatusCode)); + return false; + } + + // extend or shorten layer - constexpr size_t messageOffset = 13; // expect "HTTP/x.y XXX YYY", YYY starts from 13 - size_t offset = messageOffset; - bool isMessageFound = false; - while(offset < dataLen) - { - if(data[offset] == '\n') - { - isMessageFound = true; - break; - } - offset++; - } - - if(!isMessageFound) - { - return HttpResponseStatusCode::HttpStatusCodeUnknown; - } - - std::string messageString(data + messageOffset, offset - messageOffset); - if(!messageString.empty() && messageString.back() == '\r') - { - messageString.pop_back(); - } - if(messageString.empty()) - { - return HttpResponseStatusCode::HttpStatusCodeUnknown; - } - - return HttpResponseStatusCode(std::stoi(codeString), messageString); -} - -HttpResponseFirstLine::HttpResponseFirstLine(HttpResponseLayer* httpResponse) : m_HttpResponse(httpResponse) -{ - m_Version = parseVersion((char*)m_HttpResponse->m_Data, m_HttpResponse->getDataLen()); - if (m_Version == HttpVersionUnknown) - { - m_StatusCode = HttpResponseStatusCode::HttpStatusCodeUnknown; - } - else - { - m_StatusCode = parseStatusCode((char*)m_HttpResponse->m_Data, m_HttpResponse->getDataLen()); - } - - - char* endOfFirstLine; - if ((endOfFirstLine = (char*)memchr((char*)(m_HttpResponse->m_Data), '\n', m_HttpResponse->m_DataLen)) != nullptr) - { - m_FirstLineEndOffset = endOfFirstLine - (char*)m_HttpResponse->m_Data + 1; - m_IsComplete = true; - } - else - { - m_FirstLineEndOffset = m_HttpResponse->getDataLen(); - m_IsComplete = false; - } - - if (Logger::getInstance().isDebugEnabled(PacketLogModuleHttpLayer)) - { - std::string version = (m_Version == HttpVersionUnknown ? "Unknown" : VersionEnumToString[m_Version]); - int statusCode = (m_StatusCode == HttpResponseStatusCode::HttpStatusCodeUnknown ? 0 : m_StatusCode.toInt()); - PCPP_LOG_DEBUG("Version='" << version << "'; Status code=" << statusCode << " '" << getStatusCodeString() << "'"); - } -} - - -HttpResponseFirstLine::HttpResponseFirstLine(HttpResponseLayer* httpResponse, HttpVersion version, const HttpResponseStatusCode& statusCode) -{ - if (statusCode.isUnsupportedCode()) - { - m_Exception.setMessage("Status code supplied was " + statusCodeExplanationStringMap.at(statusCode)); - throw m_Exception; - } - - if (version == HttpVersionUnknown) - { - m_Exception.setMessage("Version supplied was HttpVersionUnknown"); - throw m_Exception; - } - - m_HttpResponse = httpResponse; - - m_StatusCode = statusCode; - m_Version = version; - - std::string firstLine = "HTTP/" + VersionEnumToString[m_Version] + " " + m_StatusCode.toString() + " " + m_StatusCode.getMessage() + "\r\n"; - - m_FirstLineEndOffset = firstLine.length(); - - m_HttpResponse->m_DataLen = firstLine.length(); - m_HttpResponse->m_Data = new uint8_t[m_HttpResponse->m_DataLen]; - memcpy(m_HttpResponse->m_Data, firstLine.c_str(), m_HttpResponse->m_DataLen); - - m_IsComplete = true; -} - -HttpVersion HttpResponseFirstLine::parseVersion(const char* data, size_t dataLen) -{ - if (!data || dataLen < 8) // "HTTP/x.y" - { - PCPP_LOG_DEBUG("HTTP response length < 8, cannot identify version"); - return HttpVersionUnknown; - } - - if (data[0] != 'H' || data[1] != 'T' || data[2] != 'T' || data[3] != 'P' || data[4] != '/') - { - PCPP_LOG_DEBUG("HTTP response does not begin with 'HTTP/'"); - return HttpVersionUnknown; - } - - const char* verPos = data + 5; - auto versionAsEnum = HttpVersionStringToEnum.find(std::string(verPos, verPos + 3)); - if (versionAsEnum == HttpVersionStringToEnum.end()) - { - return HttpVersionUnknown; - } - return versionAsEnum->second; + size_t statusStringOffset = 13; + auto newStatusCodeMessage = newStatusCode.getMessage(); + + int lengthDifference = + newStatusCodeMessage.length() - getStatusCodeString().length(); + if (lengthDifference > 0) { + if (!m_HttpResponse->extendLayer(statusStringOffset, lengthDifference)) { + PCPP_LOG_ERROR("Cannot change layer size"); + return false; + } + } else if (lengthDifference < 0) { + if (!m_HttpResponse->shortenLayer(statusStringOffset, + 0 - lengthDifference)) { + PCPP_LOG_ERROR("Cannot change layer size"); + return false; + } + } + + if (lengthDifference != 0) + m_HttpResponse->shiftFieldsOffset(m_HttpResponse->getFirstField(), + lengthDifference); + + // copy status string + memcpy(m_HttpResponse->m_Data + statusStringOffset, + newStatusCodeMessage.c_str(), newStatusCodeMessage.length()); + + // change status code + memcpy(m_HttpResponse->m_Data + 9, newStatusCode.toString().c_str(), 3); + + m_StatusCode = newStatusCode; + + m_FirstLineEndOffset += lengthDifference; + + return true; +} + +void HttpResponseFirstLine::setVersion(HttpVersion newVersion) { + if (newVersion == HttpVersionUnknown) + return; + + char* verPos = (char*)(m_HttpResponse->m_Data + 5); + memcpy(verPos, VersionEnumToString[newVersion].c_str(), 3); + + m_Version = newVersion; +} + +HttpResponseStatusCode HttpResponseFirstLine::parseStatusCode(const char* data, + size_t dataLen) { + // minimum data should be 12B long: "HTTP/x.y XXX" + if (!data || dataLen < 12) { + return HttpResponseStatusCode::HttpStatusCodeUnknown; + } + + const std::string codeString = std::string(data + 9, 3); + + if (codeString.empty() || + (std::find_if(codeString.begin(), codeString.end(), [](unsigned char c) { + return !std::isdigit(c); + }) != codeString.end())) { + return HttpResponseStatusCode::HttpStatusCodeUnknown; + } + + constexpr size_t messageOffset = + 13; // expect "HTTP/x.y XXX YYY", YYY starts from 13 + size_t offset = messageOffset; + bool isMessageFound = false; + while (offset < dataLen) { + if (data[offset] == '\n') { + isMessageFound = true; + break; + } + offset++; + } + + if (!isMessageFound) { + return HttpResponseStatusCode::HttpStatusCodeUnknown; + } + + std::string messageString(data + messageOffset, offset - messageOffset); + if (!messageString.empty() && messageString.back() == '\r') { + messageString.pop_back(); + } + if (messageString.empty()) { + return HttpResponseStatusCode::HttpStatusCodeUnknown; + } + + return HttpResponseStatusCode(std::stoi(codeString), messageString); +} + +HttpResponseFirstLine::HttpResponseFirstLine(HttpResponseLayer* httpResponse) + : m_HttpResponse(httpResponse) { + m_Version = parseVersion((char*)m_HttpResponse->m_Data, + m_HttpResponse->getDataLen()); + if (m_Version == HttpVersionUnknown) { + m_StatusCode = HttpResponseStatusCode::HttpStatusCodeUnknown; + } else { + m_StatusCode = parseStatusCode((char*)m_HttpResponse->m_Data, + m_HttpResponse->getDataLen()); + } + + char* endOfFirstLine; + if ((endOfFirstLine = (char*)memchr((char*)(m_HttpResponse->m_Data), '\n', + m_HttpResponse->m_DataLen)) != nullptr) { + m_FirstLineEndOffset = endOfFirstLine - (char*)m_HttpResponse->m_Data + 1; + m_IsComplete = true; + } else { + m_FirstLineEndOffset = m_HttpResponse->getDataLen(); + m_IsComplete = false; + } + + if (Logger::getInstance().isDebugEnabled(PacketLogModuleHttpLayer)) { + std::string version = + (m_Version == HttpVersionUnknown ? "Unknown" + : VersionEnumToString[m_Version]); + int statusCode = + (m_StatusCode == HttpResponseStatusCode::HttpStatusCodeUnknown + ? 0 + : m_StatusCode.toInt()); + PCPP_LOG_DEBUG("Version='" << version << "'; Status code=" << statusCode + << " '" << getStatusCodeString() << "'"); + } +} + +HttpResponseFirstLine::HttpResponseFirstLine( + HttpResponseLayer* httpResponse, HttpVersion version, + const HttpResponseStatusCode& statusCode) { + if (statusCode.isUnsupportedCode()) { + m_Exception.setMessage("Status code supplied was " + + statusCodeExplanationStringMap.at(statusCode)); + throw m_Exception; + } + + if (version == HttpVersionUnknown) { + m_Exception.setMessage("Version supplied was HttpVersionUnknown"); + throw m_Exception; + } + + m_HttpResponse = httpResponse; + + m_StatusCode = statusCode; + m_Version = version; + + std::string firstLine = "HTTP/" + VersionEnumToString[m_Version] + " " + + m_StatusCode.toString() + " " + + m_StatusCode.getMessage() + "\r\n"; + + m_FirstLineEndOffset = firstLine.length(); + + m_HttpResponse->m_DataLen = firstLine.length(); + m_HttpResponse->m_Data = new uint8_t[m_HttpResponse->m_DataLen]; + memcpy(m_HttpResponse->m_Data, firstLine.c_str(), m_HttpResponse->m_DataLen); + + m_IsComplete = true; +} + +HttpVersion HttpResponseFirstLine::parseVersion(const char* data, + size_t dataLen) { + if (!data || dataLen < 8) // "HTTP/x.y" + { + PCPP_LOG_DEBUG("HTTP response length < 8, cannot identify version"); + return HttpVersionUnknown; + } + + if (data[0] != 'H' || data[1] != 'T' || data[2] != 'T' || data[3] != 'P' || + data[4] != '/') { + PCPP_LOG_DEBUG("HTTP response does not begin with 'HTTP/'"); + return HttpVersionUnknown; + } + + const char* verPos = data + 5; + auto versionAsEnum = + HttpVersionStringToEnum.find(std::string(verPos, verPos + 3)); + if (versionAsEnum == HttpVersionStringToEnum.end()) { + return HttpVersionUnknown; + } + return versionAsEnum->second; } } // namespace pcpp diff --git a/Packet++/src/IPReassembly.cpp b/Packet++/src/IPReassembly.cpp index 92608046b6..c8d6f4fb42 100644 --- a/Packet++/src/IPReassembly.cpp +++ b/Packet++/src/IPReassembly.cpp @@ -1,661 +1,656 @@ #define LOG_MODULE PacketLogModuleIPReassembly #include "IPReassembly.h" +#include "EndianPortable.h" #include "IPv4Layer.h" #include "IPv6Layer.h" -#include "PacketUtils.h" #include "Logger.h" +#include "PacketUtils.h" #include -#include "EndianPortable.h" -namespace pcpp -{ +namespace pcpp { -uint32_t IPReassemblyHashPacket(IPv4Layer* ipv4Layer) -{ - ScalarBuffer vec[3]; +uint32_t IPReassemblyHashPacket(IPv4Layer* ipv4Layer) { + ScalarBuffer vec[3]; - vec[0].buffer = (uint8_t*)&ipv4Layer->getIPv4Header()->ipSrc; - vec[0].len = 4; - vec[1].buffer = (uint8_t*)&ipv4Layer->getIPv4Header()->ipDst; - vec[1].len = 4; - vec[2].buffer = (uint8_t*)&ipv4Layer->getIPv4Header()->ipId; - vec[2].len = 2; + vec[0].buffer = (uint8_t*)&ipv4Layer->getIPv4Header()->ipSrc; + vec[0].len = 4; + vec[1].buffer = (uint8_t*)&ipv4Layer->getIPv4Header()->ipDst; + vec[1].len = 4; + vec[2].buffer = (uint8_t*)&ipv4Layer->getIPv4Header()->ipId; + vec[2].len = 2; - return pcpp::fnvHash(vec, 3); + return pcpp::fnvHash(vec, 3); } -uint32_t IPReassemblyHashBy3Tuple(const IPv4Address& ipSrc, const IPv4Address& ipDst, uint16_t ipID) -{ - ScalarBuffer vec[3]; - - uint16_t ipIdNetworkOrder = htobe16(ipID); - uint32_t ipSrcAsInt = ipSrc.toInt(); - uint32_t ipDstAsInt = ipDst.toInt(); +uint32_t IPReassemblyHashBy3Tuple(const IPv4Address& ipSrc, + const IPv4Address& ipDst, uint16_t ipID) { + ScalarBuffer vec[3]; + uint16_t ipIdNetworkOrder = htobe16(ipID); + uint32_t ipSrcAsInt = ipSrc.toInt(); + uint32_t ipDstAsInt = ipDst.toInt(); - vec[0].buffer = (uint8_t*)&ipSrcAsInt; - vec[0].len = 4; - vec[1].buffer = (uint8_t*)&ipDstAsInt; - vec[1].len = 4; - vec[2].buffer = (uint8_t*)&ipIdNetworkOrder; - vec[2].len = 2; + vec[0].buffer = (uint8_t*)&ipSrcAsInt; + vec[0].len = 4; + vec[1].buffer = (uint8_t*)&ipDstAsInt; + vec[1].len = 4; + vec[2].buffer = (uint8_t*)&ipIdNetworkOrder; + vec[2].len = 2; - return pcpp::fnvHash(vec, 3); + return pcpp::fnvHash(vec, 3); } -class IPFragmentWrapper -{ -public: - virtual bool isFragment() = 0; - virtual bool isFirstFragment() = 0; - virtual bool isLastFragment() = 0; - virtual uint16_t getFragmentOffset() = 0; - virtual uint32_t getFragmentId() = 0; - virtual uint32_t hashPacket() = 0; - virtual IPReassembly::PacketKey* createPacketKey() = 0; +class IPFragmentWrapper { + public: + virtual bool isFragment() = 0; + virtual bool isFirstFragment() = 0; + virtual bool isLastFragment() = 0; + virtual uint16_t getFragmentOffset() = 0; + virtual uint32_t getFragmentId() = 0; + virtual uint32_t hashPacket() = 0; + virtual IPReassembly::PacketKey* createPacketKey() = 0; - virtual uint8_t* getIPLayerPayload() = 0; - virtual size_t getIPLayerPayloadSize() = 0; + virtual uint8_t* getIPLayerPayload() = 0; + virtual size_t getIPLayerPayloadSize() = 0; - virtual ~IPFragmentWrapper() { } + virtual ~IPFragmentWrapper() {} -protected: - - IPFragmentWrapper() {} + protected: + IPFragmentWrapper() {} }; -class IPv4FragmentWrapper : public IPFragmentWrapper -{ -public: - explicit IPv4FragmentWrapper(Packet* fragment) - { - m_IPLayer = fragment->isPacketOfType(IPv4) ? fragment->getLayerOfType() : nullptr; - } - - // implement abstract methods - - bool isFragment() override - { - return m_IPLayer->isFragment(); - } - - bool isFirstFragment() override - { - return m_IPLayer->isFirstFragment(); - } - - bool isLastFragment() override - { - return m_IPLayer->isLastFragment(); - } - - uint16_t getFragmentOffset() override - { - return m_IPLayer->getFragmentOffset(); - } - - uint32_t getFragmentId() override - { - return (uint32_t)be16toh(m_IPLayer->getIPv4Header()->ipId); - } - - uint32_t hashPacket() override - { - ScalarBuffer vec[3]; - - vec[0].buffer = (uint8_t*)&m_IPLayer->getIPv4Header()->ipSrc; - vec[0].len = 4; - vec[1].buffer = (uint8_t*)&m_IPLayer->getIPv4Header()->ipDst; - vec[1].len = 4; - vec[2].buffer = (uint8_t*)&m_IPLayer->getIPv4Header()->ipId; - vec[2].len = 2; - - return pcpp::fnvHash(vec, 3); - } - - IPReassembly::PacketKey* createPacketKey() override - { - return new IPReassembly::IPv4PacketKey(be16toh(m_IPLayer->getIPv4Header()->ipId), m_IPLayer->getSrcIPv4Address(), m_IPLayer->getDstIPv4Address()); - } - - uint8_t* getIPLayerPayload() override - { - return m_IPLayer->getLayerPayload(); - } - - size_t getIPLayerPayloadSize() override - { - return m_IPLayer->getLayerPayloadSize(); - } - -private: - IPv4Layer* m_IPLayer; +class IPv4FragmentWrapper : public IPFragmentWrapper { + public: + explicit IPv4FragmentWrapper(Packet* fragment) { + m_IPLayer = fragment->isPacketOfType(IPv4) + ? fragment->getLayerOfType() + : nullptr; + } -}; + // implement abstract methods + + bool isFragment() override { return m_IPLayer->isFragment(); } + + bool isFirstFragment() override { return m_IPLayer->isFirstFragment(); } -class IPv6FragmentWrapper : public IPFragmentWrapper -{ -public: - explicit IPv6FragmentWrapper(Packet* fragment) - { - m_IPLayer = fragment->isPacketOfType(IPv6) ? fragment->getLayerOfType() : nullptr; - if (m_IPLayer != nullptr) - m_FragHeader = m_IPLayer->getExtensionOfType(); - else - m_FragHeader = nullptr; - } - - // implement abstract methods - - bool isFragment() override - { - return (m_FragHeader != nullptr); - } - - bool isFirstFragment() override - { - if (isFragment()) - return m_FragHeader->isFirstFragment(); - - return false; - } - - bool isLastFragment() override - { - if (isFragment()) - return m_FragHeader->isLastFragment(); - - return false; - } - - - uint16_t getFragmentOffset() override - { - if (isFragment()) - return m_FragHeader->getFragmentOffset(); - - return 0; - } - - uint32_t getFragmentId() override - { - return be32toh(m_FragHeader->getFragHeader()->id); - } - - uint32_t hashPacket() override - { - if (m_FragHeader == nullptr) - return 0; - - ScalarBuffer vec[3]; - - vec[0].buffer = m_IPLayer->getIPv6Header()->ipSrc; - vec[0].len = 16; - vec[1].buffer = m_IPLayer->getIPv6Header()->ipDst; - vec[1].len = 16; - vec[2].buffer = (uint8_t*)&m_FragHeader->getFragHeader()->id; - vec[2].len = 4; - - return pcpp::fnvHash(vec, 3); - } - - IPReassembly::PacketKey* createPacketKey() override - { - return new IPReassembly::IPv6PacketKey(be32toh(m_FragHeader->getFragHeader()->id), m_IPLayer->getSrcIPv6Address(), m_IPLayer->getDstIPv6Address()); - } - - uint8_t* getIPLayerPayload() override - { - return m_IPLayer->getLayerPayload(); - } - - size_t getIPLayerPayloadSize() override - { - return m_IPLayer->getLayerPayloadSize(); - } - -private: - IPv6Layer* m_IPLayer; - IPv6FragmentationHeader* m_FragHeader; + bool isLastFragment() override { return m_IPLayer->isLastFragment(); } + uint16_t getFragmentOffset() override { + return m_IPLayer->getFragmentOffset(); + } + + uint32_t getFragmentId() override { + return (uint32_t)be16toh(m_IPLayer->getIPv4Header()->ipId); + } + + uint32_t hashPacket() override { + ScalarBuffer vec[3]; + + vec[0].buffer = (uint8_t*)&m_IPLayer->getIPv4Header()->ipSrc; + vec[0].len = 4; + vec[1].buffer = (uint8_t*)&m_IPLayer->getIPv4Header()->ipDst; + vec[1].len = 4; + vec[2].buffer = (uint8_t*)&m_IPLayer->getIPv4Header()->ipId; + vec[2].len = 2; + + return pcpp::fnvHash(vec, 3); + } + + IPReassembly::PacketKey* createPacketKey() override { + return new IPReassembly::IPv4PacketKey( + be16toh(m_IPLayer->getIPv4Header()->ipId), + m_IPLayer->getSrcIPv4Address(), m_IPLayer->getDstIPv4Address()); + } + + uint8_t* getIPLayerPayload() override { return m_IPLayer->getLayerPayload(); } + + size_t getIPLayerPayloadSize() override { + return m_IPLayer->getLayerPayloadSize(); + } + + private: + IPv4Layer* m_IPLayer; }; +class IPv6FragmentWrapper : public IPFragmentWrapper { + public: + explicit IPv6FragmentWrapper(Packet* fragment) { + m_IPLayer = fragment->isPacketOfType(IPv6) + ? fragment->getLayerOfType() + : nullptr; + if (m_IPLayer != nullptr) + m_FragHeader = m_IPLayer->getExtensionOfType(); + else + m_FragHeader = nullptr; + } -uint32_t IPReassembly::IPv4PacketKey::getHashValue() const -{ - ScalarBuffer vec[3]; + // implement abstract methods - uint16_t ipIdNetworkOrder = htobe16(m_IpID); - uint32_t ipSrcAsInt = m_SrcIP.toInt(); - uint32_t ipDstAsInt = m_DstIP.toInt(); + bool isFragment() override { return (m_FragHeader != nullptr); } - vec[0].buffer = (uint8_t*)&ipSrcAsInt; - vec[0].len = 4; - vec[1].buffer = (uint8_t*)&ipDstAsInt; - vec[1].len = 4; - vec[2].buffer = (uint8_t*)&ipIdNetworkOrder; - vec[2].len = 2; + bool isFirstFragment() override { + if (isFragment()) + return m_FragHeader->isFirstFragment(); - return pcpp::fnvHash(vec, 3); -} + return false; + } -uint32_t IPReassembly::IPv6PacketKey::getHashValue() const -{ - ScalarBuffer vec[3]; + bool isLastFragment() override { + if (isFragment()) + return m_FragHeader->isLastFragment(); - uint32_t fragIdNetworkOrder = htobe32(m_FragmentID); - uint8_t ipSrcAsByteArr[16]; - uint8_t ipDstAsByteArr[16]; - m_SrcIP.copyTo(ipSrcAsByteArr); - m_DstIP.copyTo(ipDstAsByteArr); + return false; + } - vec[0].buffer = ipSrcAsByteArr; - vec[0].len = 16; - vec[1].buffer = ipDstAsByteArr; - vec[1].len = 16; - vec[2].buffer = (uint8_t*)&fragIdNetworkOrder; - vec[2].len = 4; + uint16_t getFragmentOffset() override { + if (isFragment()) + return m_FragHeader->getFragmentOffset(); - return pcpp::fnvHash(vec, 3); + return 0; + } + + uint32_t getFragmentId() override { + return be32toh(m_FragHeader->getFragHeader()->id); + } + + uint32_t hashPacket() override { + if (m_FragHeader == nullptr) + return 0; + + ScalarBuffer vec[3]; + + vec[0].buffer = m_IPLayer->getIPv6Header()->ipSrc; + vec[0].len = 16; + vec[1].buffer = m_IPLayer->getIPv6Header()->ipDst; + vec[1].len = 16; + vec[2].buffer = (uint8_t*)&m_FragHeader->getFragHeader()->id; + vec[2].len = 4; + + return pcpp::fnvHash(vec, 3); + } + + IPReassembly::PacketKey* createPacketKey() override { + return new IPReassembly::IPv6PacketKey( + be32toh(m_FragHeader->getFragHeader()->id), + m_IPLayer->getSrcIPv6Address(), m_IPLayer->getDstIPv6Address()); + } + + uint8_t* getIPLayerPayload() override { return m_IPLayer->getLayerPayload(); } + + size_t getIPLayerPayloadSize() override { + return m_IPLayer->getLayerPayloadSize(); + } + + private: + IPv6Layer* m_IPLayer; + IPv6FragmentationHeader* m_FragHeader; +}; + +uint32_t IPReassembly::IPv4PacketKey::getHashValue() const { + ScalarBuffer vec[3]; + + uint16_t ipIdNetworkOrder = htobe16(m_IpID); + uint32_t ipSrcAsInt = m_SrcIP.toInt(); + uint32_t ipDstAsInt = m_DstIP.toInt(); + + vec[0].buffer = (uint8_t*)&ipSrcAsInt; + vec[0].len = 4; + vec[1].buffer = (uint8_t*)&ipDstAsInt; + vec[1].len = 4; + vec[2].buffer = (uint8_t*)&ipIdNetworkOrder; + vec[2].len = 2; + + return pcpp::fnvHash(vec, 3); } +uint32_t IPReassembly::IPv6PacketKey::getHashValue() const { + ScalarBuffer vec[3]; + + uint32_t fragIdNetworkOrder = htobe32(m_FragmentID); + uint8_t ipSrcAsByteArr[16]; + uint8_t ipDstAsByteArr[16]; + m_SrcIP.copyTo(ipSrcAsByteArr); + m_DstIP.copyTo(ipDstAsByteArr); + vec[0].buffer = ipSrcAsByteArr; + vec[0].len = 16; + vec[1].buffer = ipDstAsByteArr; + vec[1].len = 16; + vec[2].buffer = (uint8_t*)&fragIdNetworkOrder; + vec[2].len = 4; -IPReassembly::~IPReassembly() -{ - // empty the map - go over all keys, delete all IPFragmentData objects and remove them from the map - while (!m_FragmentMap.empty()) - { - delete m_FragmentMap.begin()->second; - m_FragmentMap.erase(m_FragmentMap.begin()); - } + return pcpp::fnvHash(vec, 3); } -Packet* IPReassembly::processPacket(Packet* fragment, ReassemblyStatus& status, ProtocolType parseUntil, OsiModelLayer parseUntilLayer) -{ - status = NON_IP_PACKET; - - // packet is not an IP packet - if (!fragment->isPacketOfType(IPv4) && !fragment->isPacketOfType(IPv6)) - { - PCPP_LOG_DEBUG("Got a non-IP packet, returning packet to user"); - status = NON_IP_PACKET; - return fragment; - } - - // get IPv4 layer - //IPv4Layer* ipLayer = fragment->getLayerOfType(); - - // create fragment wrapper - IPv4FragmentWrapper ipv4Wrapper(fragment); - IPv6FragmentWrapper ipv6Wrapper(fragment); - IPFragmentWrapper* fragWrapper = nullptr; - if (fragment->isPacketOfType(IPv4)) - fragWrapper = &ipv4Wrapper; - else // fragment->isPacketOfType(IPv6) - fragWrapper = &ipv6Wrapper; - - // packet is not a fragment - if (!(fragWrapper->isFragment())) - { - PCPP_LOG_DEBUG("Got a non fragment packet with FragID=0x" << std::hex << fragWrapper->getFragmentId() << ", returning packet to user"); - status = NON_FRAGMENT; - return fragment; - } - - // create a hash from source IP, destination IP and IP/fragment ID - uint32_t hash = fragWrapper->hashPacket(); - - IPFragmentData* fragData = nullptr; - - // check whether this packet already exists in the map - std::map::iterator iter = m_FragmentMap.find(hash); - - // this is the first fragment seen for this packet - if (iter == m_FragmentMap.end()) - { - PCPP_LOG_DEBUG("Got new packet with FragID=0x" << std::hex << fragWrapper->getFragmentId() << ", allocating place in map"); - - // create the IPFragmentData object - fragData = new IPFragmentData(fragWrapper->createPacketKey(), fragWrapper->getFragmentId()); - - // add the new fragment to the map - addNewFragment(hash, fragData); - } - else // packet was seen before - { - // get the IPFragmentData object - fragData = iter->second; - - // mark this packet as used - m_PacketLRU.put(hash, nullptr); - } - - bool gotLastFragment = false; - - // if current fragment is the first fragment of this packet - if (fragWrapper->isFirstFragment()) - { - if (fragData->data == nullptr) // first fragment - { - PCPP_LOG_DEBUG("[FragID=0x" << std::hex << fragWrapper->getFragmentId() << "] Got first fragment, allocating RawPacket"); - - // create the reassembled packet and copy the fragment data to it - - // copy only data from the beginning of the fragment to the end of IP layer payload. - // Don't copy data beyond it such as packet trailer - auto fragmentRawPacket = fragment->getRawPacket(); - auto rawDataLen = fragWrapper->getIPLayerPayload() - fragmentRawPacket ->getRawData() + fragWrapper->getIPLayerPayloadSize(); - auto rawData = new uint8_t[rawDataLen]; - memcpy(rawData, fragmentRawPacket->getRawData(), rawDataLen); - - fragData->data = new RawPacket(rawData, rawDataLen, fragmentRawPacket->getPacketTimeStamp(), true, fragmentRawPacket->getLinkLayerType()); - fragData->currentOffset = fragWrapper->getIPLayerPayloadSize(); - status = FIRST_FRAGMENT; - - // check if the next fragments already arrived out-of-order and waiting in the out-of-order list - gotLastFragment = matchOutOfOrderFragments(fragData); - } - else // duplicated first fragment - { - PCPP_LOG_DEBUG("[FragID=0x" << std::hex << fragWrapper->getFragmentId() << "] Got duplicated first fragment"); - status = FRAGMENT; - return nullptr; - } - } - - else // not first fragment - { - PCPP_LOG_DEBUG("[FragID=0x" << std::hex << fragWrapper->getFragmentId() << "] Got fragment"); - - uint16_t fragOffset = fragWrapper->getFragmentOffset(); - - // check if the current fragment offset matches the expected fragment offset - if (fragData->currentOffset == fragOffset) - { - // malformed fragment which is not the first fragment but its offset is 0 - if (fragData->data == nullptr) - { - PCPP_LOG_DEBUG("[FragID=0x" << std::hex << fragWrapper->getFragmentId() << "] Fragment is malformed"); - status = MALFORMED_FRAGMENT; - return nullptr; - } - - PCPP_LOG_DEBUG("[FragID=0x" << std::hex << fragWrapper->getFragmentId() << "] Found next matching fragment with offset " << fragOffset << ", adding fragment data to reassembled packet"); - - size_t payloadSize = fragWrapper->getIPLayerPayloadSize(); - // copy fragment data to reassembled packet - fragData->data->reallocateData(fragData->data->getRawDataLen() + payloadSize); - fragData->data->appendData(fragWrapper->getIPLayerPayload(), payloadSize); - - // update expected offset - fragData->currentOffset += payloadSize; - - // if this is the last fragment - mark it - if (fragWrapper->isLastFragment()) - gotLastFragment = true; - else - // if not the last fragment - check if the next fragments are waiting in the out-of-order list - gotLastFragment = matchOutOfOrderFragments(fragData); - } - // if current fragment offset is larger than expected - this means this fragment is out-of-order - else if (fragOffset > fragData->currentOffset) - { - PCPP_LOG_DEBUG("[FragID=0x" << std::hex << fragWrapper->getFragmentId() << "] Got out-of-ordered fragment with offset " << fragOffset << " (expected: " << fragData->currentOffset << "). Adding it to out-of-order list"); - - // create a new IPFragment and copy the fragment data and params to it - size_t payloadSize = fragWrapper->getIPLayerPayloadSize(); - IPFragment* newFrag = new IPFragment(); - newFrag->fragmentOffset = fragWrapper->getFragmentOffset(); - newFrag->fragmentData = new uint8_t[payloadSize]; - newFrag->fragmentDataLen = payloadSize; - memcpy(newFrag->fragmentData, fragWrapper->getIPLayerPayload(), newFrag->fragmentDataLen); - newFrag->lastFragment = fragWrapper->isLastFragment(); - - // store the IPFragment in the out-of-order fragment list - fragData->outOfOrderFragments.pushBack(newFrag); - - status = OUT_OF_ORDER_FRAGMENT; - return nullptr; - } - else - { - PCPP_LOG_DEBUG("[FragID=0x" << std::hex << fragWrapper->getFragmentId() << "] Got a fragment with an offset that was already seen: " << fragOffset << " (current offset is: " << fragData->currentOffset << "), probably duplicated fragment"); - } - - } - - // if seen the last fragment - if (gotLastFragment) - { - PCPP_LOG_DEBUG("[FragID=0x" << std::hex << fragWrapper->getFragmentId() << "] Reassembly process completed, allocating a packet and returning it"); - fragData->deleteData = false; - - // fix IP length field - if (fragData->packetKey->getProtocolType() == IPv4) - { - Packet tempPacket(fragData->data, IPv4); - IPv4Layer* ipLayer = tempPacket.getLayerOfType(); - iphdr* iphdr = ipLayer->getIPv4Header(); - iphdr->totalLength = htobe16(fragData->currentOffset + ipLayer->getHeaderLen()); - iphdr->fragmentOffset = 0; - } - else - { - Packet tempPacket(fragData->data, IPv6); - IPv6Layer* ipLayer = tempPacket.getLayerOfType(); - tempPacket.getLayerOfType()->getIPv6Header()->payloadLength = fragData->currentOffset + ipLayer->getHeaderLen(); - } - - // create a new Packet object with the reassembled data as its RawPacket - Packet* reassembledPacket = new Packet(fragData->data, true, parseUntil, parseUntilLayer); - - if (fragData->packetKey->getProtocolType() == IPv4) - { - // re-calculate all IPv4 fields - reassembledPacket->getLayerOfType()->computeCalculateFields(); - } - else - { - // remove fragment extension - IPv6Layer* ipLayer = reassembledPacket->getLayerOfType(); - ipLayer->removeAllExtensions(); - - // re-calculate all IPv4 fields - ipLayer->computeCalculateFields(); - } - - PCPP_LOG_DEBUG("[FragID=0x" << std::hex << fragWrapper->getFragmentId() << "] Deleting fragment data from map"); - - // delete the IPFragmentData object and remove it from the map - delete fragData; - m_FragmentMap.erase(iter); - m_PacketLRU.eraseElement(hash); - status = REASSEMBLED; - return reassembledPacket; - } - - // if got to here it means this fragment is either the first fragment or a fragment in the middle. Set the appropriate status and return - if (status != FIRST_FRAGMENT) - status = FRAGMENT; - - return nullptr; +IPReassembly::~IPReassembly() { + // empty the map - go over all keys, delete all IPFragmentData objects and + // remove them from the map + while (!m_FragmentMap.empty()) { + delete m_FragmentMap.begin()->second; + m_FragmentMap.erase(m_FragmentMap.begin()); + } } -Packet* IPReassembly::processPacket(RawPacket* fragment, ReassemblyStatus& status, ProtocolType parseUntil, OsiModelLayer parseUntilLayer) -{ - Packet* parsedFragment = new Packet(fragment, false, parseUntil, parseUntilLayer); - Packet* result = processPacket(parsedFragment, status, parseUntil, parseUntilLayer); - if (result != parsedFragment) - delete parsedFragment; +Packet* IPReassembly::processPacket(Packet* fragment, ReassemblyStatus& status, + ProtocolType parseUntil, + OsiModelLayer parseUntilLayer) { + status = NON_IP_PACKET; + + // packet is not an IP packet + if (!fragment->isPacketOfType(IPv4) && !fragment->isPacketOfType(IPv6)) { + PCPP_LOG_DEBUG("Got a non-IP packet, returning packet to user"); + status = NON_IP_PACKET; + return fragment; + } + + // get IPv4 layer + // IPv4Layer* ipLayer = fragment->getLayerOfType(); + + // create fragment wrapper + IPv4FragmentWrapper ipv4Wrapper(fragment); + IPv6FragmentWrapper ipv6Wrapper(fragment); + IPFragmentWrapper* fragWrapper = nullptr; + if (fragment->isPacketOfType(IPv4)) + fragWrapper = &ipv4Wrapper; + else // fragment->isPacketOfType(IPv6) + fragWrapper = &ipv6Wrapper; + + // packet is not a fragment + if (!(fragWrapper->isFragment())) { + PCPP_LOG_DEBUG("Got a non fragment packet with FragID=0x" + << std::hex << fragWrapper->getFragmentId() + << ", returning packet to user"); + status = NON_FRAGMENT; + return fragment; + } + + // create a hash from source IP, destination IP and IP/fragment ID + uint32_t hash = fragWrapper->hashPacket(); + + IPFragmentData* fragData = nullptr; + + // check whether this packet already exists in the map + std::map::iterator iter = + m_FragmentMap.find(hash); + + // this is the first fragment seen for this packet + if (iter == m_FragmentMap.end()) { + PCPP_LOG_DEBUG("Got new packet with FragID=0x" + << std::hex << fragWrapper->getFragmentId() + << ", allocating place in map"); + + // create the IPFragmentData object + fragData = new IPFragmentData(fragWrapper->createPacketKey(), + fragWrapper->getFragmentId()); + + // add the new fragment to the map + addNewFragment(hash, fragData); + } else // packet was seen before + { + // get the IPFragmentData object + fragData = iter->second; + + // mark this packet as used + m_PacketLRU.put(hash, nullptr); + } + + bool gotLastFragment = false; + + // if current fragment is the first fragment of this packet + if (fragWrapper->isFirstFragment()) { + if (fragData->data == nullptr) // first fragment + { + PCPP_LOG_DEBUG("[FragID=0x" + << std::hex << fragWrapper->getFragmentId() + << "] Got first fragment, allocating RawPacket"); + + // create the reassembled packet and copy the fragment data to it + + // copy only data from the beginning of the fragment to the end of IP + // layer payload. Don't copy data beyond it such as packet trailer + auto fragmentRawPacket = fragment->getRawPacket(); + auto rawDataLen = fragWrapper->getIPLayerPayload() - + fragmentRawPacket->getRawData() + + fragWrapper->getIPLayerPayloadSize(); + auto rawData = new uint8_t[rawDataLen]; + memcpy(rawData, fragmentRawPacket->getRawData(), rawDataLen); + + fragData->data = new RawPacket( + rawData, rawDataLen, fragmentRawPacket->getPacketTimeStamp(), true, + fragmentRawPacket->getLinkLayerType()); + fragData->currentOffset = fragWrapper->getIPLayerPayloadSize(); + status = FIRST_FRAGMENT; + + // check if the next fragments already arrived out-of-order and waiting in + // the out-of-order list + gotLastFragment = matchOutOfOrderFragments(fragData); + } else // duplicated first fragment + { + PCPP_LOG_DEBUG("[FragID=0x" << std::hex << fragWrapper->getFragmentId() + << "] Got duplicated first fragment"); + status = FRAGMENT; + return nullptr; + } + } + + else // not first fragment + { + PCPP_LOG_DEBUG("[FragID=0x" << std::hex << fragWrapper->getFragmentId() + << "] Got fragment"); + + uint16_t fragOffset = fragWrapper->getFragmentOffset(); + + // check if the current fragment offset matches the expected fragment offset + if (fragData->currentOffset == fragOffset) { + // malformed fragment which is not the first fragment but its offset is 0 + if (fragData->data == nullptr) { + PCPP_LOG_DEBUG("[FragID=0x" << std::hex << fragWrapper->getFragmentId() + << "] Fragment is malformed"); + status = MALFORMED_FRAGMENT; + return nullptr; + } + + PCPP_LOG_DEBUG("[FragID=0x" + << std::hex << fragWrapper->getFragmentId() + << "] Found next matching fragment with offset " + << fragOffset + << ", adding fragment data to reassembled packet"); + + size_t payloadSize = fragWrapper->getIPLayerPayloadSize(); + // copy fragment data to reassembled packet + fragData->data->reallocateData(fragData->data->getRawDataLen() + + payloadSize); + fragData->data->appendData(fragWrapper->getIPLayerPayload(), payloadSize); + + // update expected offset + fragData->currentOffset += payloadSize; + + // if this is the last fragment - mark it + if (fragWrapper->isLastFragment()) + gotLastFragment = true; + else + // if not the last fragment - check if the next fragments are waiting in + // the out-of-order list + gotLastFragment = matchOutOfOrderFragments(fragData); + } + // if current fragment offset is larger than expected - this means this + // fragment is out-of-order + else if (fragOffset > fragData->currentOffset) { + PCPP_LOG_DEBUG("[FragID=0x" + << std::hex << fragWrapper->getFragmentId() + << "] Got out-of-ordered fragment with offset " + << fragOffset << " (expected: " << fragData->currentOffset + << "). Adding it to out-of-order list"); + + // create a new IPFragment and copy the fragment data and params to it + size_t payloadSize = fragWrapper->getIPLayerPayloadSize(); + IPFragment* newFrag = new IPFragment(); + newFrag->fragmentOffset = fragWrapper->getFragmentOffset(); + newFrag->fragmentData = new uint8_t[payloadSize]; + newFrag->fragmentDataLen = payloadSize; + memcpy(newFrag->fragmentData, fragWrapper->getIPLayerPayload(), + newFrag->fragmentDataLen); + newFrag->lastFragment = fragWrapper->isLastFragment(); + + // store the IPFragment in the out-of-order fragment list + fragData->outOfOrderFragments.pushBack(newFrag); + + status = OUT_OF_ORDER_FRAGMENT; + return nullptr; + } else { + PCPP_LOG_DEBUG( + "[FragID=0x" + << std::hex << fragWrapper->getFragmentId() + << "] Got a fragment with an offset that was already seen: " + << fragOffset << " (current offset is: " << fragData->currentOffset + << "), probably duplicated fragment"); + } + } + + // if seen the last fragment + if (gotLastFragment) { + PCPP_LOG_DEBUG("[FragID=0x" << std::hex << fragWrapper->getFragmentId() + << "] Reassembly process completed, allocating " + "a packet and returning it"); + fragData->deleteData = false; + + // fix IP length field + if (fragData->packetKey->getProtocolType() == IPv4) { + Packet tempPacket(fragData->data, IPv4); + IPv4Layer* ipLayer = tempPacket.getLayerOfType(); + iphdr* iphdr = ipLayer->getIPv4Header(); + iphdr->totalLength = + htobe16(fragData->currentOffset + ipLayer->getHeaderLen()); + iphdr->fragmentOffset = 0; + } else { + Packet tempPacket(fragData->data, IPv6); + IPv6Layer* ipLayer = tempPacket.getLayerOfType(); + tempPacket.getLayerOfType()->getIPv6Header()->payloadLength = + fragData->currentOffset + ipLayer->getHeaderLen(); + } + + // create a new Packet object with the reassembled data as its RawPacket + Packet* reassembledPacket = + new Packet(fragData->data, true, parseUntil, parseUntilLayer); + + if (fragData->packetKey->getProtocolType() == IPv4) { + // re-calculate all IPv4 fields + reassembledPacket->getLayerOfType()->computeCalculateFields(); + } else { + // remove fragment extension + IPv6Layer* ipLayer = reassembledPacket->getLayerOfType(); + ipLayer->removeAllExtensions(); + + // re-calculate all IPv4 fields + ipLayer->computeCalculateFields(); + } + + PCPP_LOG_DEBUG("[FragID=0x" << std::hex << fragWrapper->getFragmentId() + << "] Deleting fragment data from map"); + + // delete the IPFragmentData object and remove it from the map + delete fragData; + m_FragmentMap.erase(iter); + m_PacketLRU.eraseElement(hash); + status = REASSEMBLED; + return reassembledPacket; + } + + // if got to here it means this fragment is either the first fragment or a + // fragment in the middle. Set the appropriate status and return + if (status != FIRST_FRAGMENT) + status = FRAGMENT; + + return nullptr; +} - return result; +Packet* IPReassembly::processPacket(RawPacket* fragment, + ReassemblyStatus& status, + ProtocolType parseUntil, + OsiModelLayer parseUntilLayer) { + Packet* parsedFragment = + new Packet(fragment, false, parseUntil, parseUntilLayer); + Packet* result = + processPacket(parsedFragment, status, parseUntil, parseUntilLayer); + if (result != parsedFragment) + delete parsedFragment; + + return result; } -Packet* IPReassembly::getCurrentPacket(const PacketKey& key) -{ - // create a hash out of the packet key - uint32_t hash = key.getHashValue(); - - // look for this hash value in the map - std::map::iterator iter = m_FragmentMap.find(hash); - - // hash was found - if (iter != m_FragmentMap.end()) - { - IPFragmentData* fragData = iter->second; - - // some data already exists - if (fragData != nullptr && fragData->data != nullptr) - { - // create a copy of the RawPacket object - RawPacket* partialRawPacket = new RawPacket(*(fragData->data)); - - // fix IP length field - if (fragData->packetKey->getProtocolType() == IPv4) - { - Packet tempPacket(partialRawPacket, IPv4); - IPv4Layer* ipLayer = tempPacket.getLayerOfType(); - ipLayer->getIPv4Header()->totalLength = htobe16(fragData->currentOffset + ipLayer->getHeaderLen()); - } - else - { - Packet tempPacket(partialRawPacket, IPv6); - IPv6Layer* ipLayer = tempPacket.getLayerOfType(); - tempPacket.getLayerOfType()->getIPv6Header()->payloadLength = fragData->currentOffset + + ipLayer->getHeaderLen(); - } - - // create a packet object wrapping the RawPacket we've just created - Packet* partialDataPacket = new Packet(partialRawPacket, true); - - // prepare the packet and return it - if (key.getProtocolType() == IPv4) - { - IPv4Layer* ipLayer = partialDataPacket->getLayerOfType(); - ipLayer->getIPv4Header()->fragmentOffset = 0; - ipLayer->computeCalculateFields(); - } - else // key.getProtocolType() == IPv6 - { - IPv6Layer* ipLayer = partialDataPacket->getLayerOfType(); - ipLayer->removeAllExtensions(); - ipLayer->computeCalculateFields(); - } - - return partialDataPacket; - } - } - - return nullptr; +Packet* IPReassembly::getCurrentPacket(const PacketKey& key) { + // create a hash out of the packet key + uint32_t hash = key.getHashValue(); + + // look for this hash value in the map + std::map::iterator iter = + m_FragmentMap.find(hash); + + // hash was found + if (iter != m_FragmentMap.end()) { + IPFragmentData* fragData = iter->second; + + // some data already exists + if (fragData != nullptr && fragData->data != nullptr) { + // create a copy of the RawPacket object + RawPacket* partialRawPacket = new RawPacket(*(fragData->data)); + + // fix IP length field + if (fragData->packetKey->getProtocolType() == IPv4) { + Packet tempPacket(partialRawPacket, IPv4); + IPv4Layer* ipLayer = tempPacket.getLayerOfType(); + ipLayer->getIPv4Header()->totalLength = + htobe16(fragData->currentOffset + ipLayer->getHeaderLen()); + } else { + Packet tempPacket(partialRawPacket, IPv6); + IPv6Layer* ipLayer = tempPacket.getLayerOfType(); + tempPacket.getLayerOfType()->getIPv6Header()->payloadLength = + fragData->currentOffset + +ipLayer->getHeaderLen(); + } + + // create a packet object wrapping the RawPacket we've just created + Packet* partialDataPacket = new Packet(partialRawPacket, true); + + // prepare the packet and return it + if (key.getProtocolType() == IPv4) { + IPv4Layer* ipLayer = partialDataPacket->getLayerOfType(); + ipLayer->getIPv4Header()->fragmentOffset = 0; + ipLayer->computeCalculateFields(); + } else // key.getProtocolType() == IPv6 + { + IPv6Layer* ipLayer = partialDataPacket->getLayerOfType(); + ipLayer->removeAllExtensions(); + ipLayer->computeCalculateFields(); + } + + return partialDataPacket; + } + } + + return nullptr; } -void IPReassembly::removePacket(const PacketKey& key) -{ - // create a hash out of the packet key - uint32_t hash = key.getHashValue(); +void IPReassembly::removePacket(const PacketKey& key) { + // create a hash out of the packet key + uint32_t hash = key.getHashValue(); - // look for this hash value in the map - std::map::iterator iter = m_FragmentMap.find(hash); + // look for this hash value in the map + std::map::iterator iter = + m_FragmentMap.find(hash); - // hash was found - if (iter != m_FragmentMap.end()) - { - // free all data saved in the map - delete iter->second; - m_FragmentMap.erase(iter); + // hash was found + if (iter != m_FragmentMap.end()) { + // free all data saved in the map + delete iter->second; + m_FragmentMap.erase(iter); - // remove from LRU list - m_PacketLRU.eraseElement(hash); - } + // remove from LRU list + m_PacketLRU.eraseElement(hash); + } } -void IPReassembly::addNewFragment(uint32_t hash, IPFragmentData* fragData) -{ - // put the new frag in the LRU list - uint32_t packetRemoved; - - if (m_PacketLRU.put(hash, &packetRemoved) == 1) // this means LRU list was full and the least recently used item was removed - { - // remove this item from the fragment map - std::map::iterator iter = m_FragmentMap.find(packetRemoved); - IPFragmentData* dataRemoved = iter->second; - - PacketKey* key = nullptr; - if (m_OnFragmentsCleanCallback != nullptr) - key = dataRemoved->packetKey->clone(); - - PCPP_LOG_DEBUG("Reached maximum packet capacity, removing data for FragID=0x" << std::hex << dataRemoved->fragmentID); - delete dataRemoved; - m_FragmentMap.erase(iter); - - // fire callback if not null - if (m_OnFragmentsCleanCallback != nullptr) - { - m_OnFragmentsCleanCallback(key, m_CallbackUserCookie); - delete key; - } - } - - // add the new fragment to the map - std::pair pair(hash, fragData); - m_FragmentMap.insert(pair); +void IPReassembly::addNewFragment(uint32_t hash, IPFragmentData* fragData) { + // put the new frag in the LRU list + uint32_t packetRemoved; + + if (m_PacketLRU.put(hash, &packetRemoved) == + 1) // this means LRU list was full and the least recently used item was + // removed + { + // remove this item from the fragment map + std::map::iterator iter = + m_FragmentMap.find(packetRemoved); + IPFragmentData* dataRemoved = iter->second; + + PacketKey* key = nullptr; + if (m_OnFragmentsCleanCallback != nullptr) + key = dataRemoved->packetKey->clone(); + + PCPP_LOG_DEBUG( + "Reached maximum packet capacity, removing data for FragID=0x" + << std::hex << dataRemoved->fragmentID); + delete dataRemoved; + m_FragmentMap.erase(iter); + + // fire callback if not null + if (m_OnFragmentsCleanCallback != nullptr) { + m_OnFragmentsCleanCallback(key, m_CallbackUserCookie); + delete key; + } + } + + // add the new fragment to the map + std::pair pair(hash, fragData); + m_FragmentMap.insert(pair); } -bool IPReassembly::matchOutOfOrderFragments(IPFragmentData* fragData) -{ - PCPP_LOG_DEBUG("[FragID=0x" << std::hex << fragData->fragmentID << "] Searching out-of-order fragment list for the next fragment"); - - // a flag indicating whether the last fragment of the packet was found - bool foundLastSegment = false; - - // run until the last fragment was found or until we finished going over the out-of-order list and didn't find any matching fragment - while (!foundLastSegment) - { - bool foundOutOfOrderFrag = false; - - int index = 0; - - // go over all fragment in the out-of-order list - while (index < (int)fragData->outOfOrderFragments.size()) - { - // get the current fragment from the out-of-order list - IPFragment* frag = fragData->outOfOrderFragments.at(index); - - // this fragment is exactly the one we're looking for - if (fragData->currentOffset == frag->fragmentOffset) - { - // add it to the reassembled packet - PCPP_LOG_DEBUG("[FragID=0x" << std::hex << fragData->fragmentID << "] Found the next matching fragment in out-of-order list with offset " << frag->fragmentOffset << ", adding its data to reassembled packet"); - fragData->data->reallocateData(fragData->data->getRawDataLen() + frag->fragmentDataLen); - fragData->data->appendData(frag->fragmentData, frag->fragmentDataLen); - fragData->currentOffset += frag->fragmentDataLen; - if (frag->lastFragment) // if this is the last fragment of the packet - { - PCPP_LOG_DEBUG("[FragID=0x" << std::hex << fragData->fragmentID << "] Found last fragment inside out-of-order list"); - foundLastSegment = true; - } - - // remove this fragment from the out-of-order list - fragData->outOfOrderFragments.erase(fragData->outOfOrderFragments.begin() + index); - - // mark that we found at least one matching fragment in the out-of-order list - foundOutOfOrderFrag = true; - } - else - index++; - } - - // during the search we did on the out-of-order list we didn't find any matching fragment - if (!foundOutOfOrderFrag) - { - // break the loop - need to wait for the missing fragment in next incoming packets - PCPP_LOG_DEBUG("[FragID=0x" << std::hex << fragData->fragmentID << "] Didn't find the next fragment in out-of-order list"); - break; - } - } - - return foundLastSegment; +bool IPReassembly::matchOutOfOrderFragments(IPFragmentData* fragData) { + PCPP_LOG_DEBUG( + "[FragID=0x" + << std::hex << fragData->fragmentID + << "] Searching out-of-order fragment list for the next fragment"); + + // a flag indicating whether the last fragment of the packet was found + bool foundLastSegment = false; + + // run until the last fragment was found or until we finished going over the + // out-of-order list and didn't find any matching fragment + while (!foundLastSegment) { + bool foundOutOfOrderFrag = false; + + int index = 0; + + // go over all fragment in the out-of-order list + while (index < (int)fragData->outOfOrderFragments.size()) { + // get the current fragment from the out-of-order list + IPFragment* frag = fragData->outOfOrderFragments.at(index); + + // this fragment is exactly the one we're looking for + if (fragData->currentOffset == frag->fragmentOffset) { + // add it to the reassembled packet + PCPP_LOG_DEBUG("[FragID=0x" + << std::hex << fragData->fragmentID + << "] Found the next matching fragment in out-of-order " + "list with offset " + << frag->fragmentOffset + << ", adding its data to reassembled packet"); + fragData->data->reallocateData(fragData->data->getRawDataLen() + + frag->fragmentDataLen); + fragData->data->appendData(frag->fragmentData, frag->fragmentDataLen); + fragData->currentOffset += frag->fragmentDataLen; + if (frag->lastFragment) // if this is the last fragment of the packet + { + PCPP_LOG_DEBUG("[FragID=0x" + << std::hex << fragData->fragmentID + << "] Found last fragment inside out-of-order list"); + foundLastSegment = true; + } + + // remove this fragment from the out-of-order list + fragData->outOfOrderFragments.erase( + fragData->outOfOrderFragments.begin() + index); + + // mark that we found at least one matching fragment in the out-of-order + // list + foundOutOfOrderFrag = true; + } else + index++; + } + + // during the search we did on the out-of-order list we didn't find any + // matching fragment + if (!foundOutOfOrderFrag) { + // break the loop - need to wait for the missing fragment in next incoming + // packets + PCPP_LOG_DEBUG("[FragID=0x" + << std::hex << fragData->fragmentID + << "] Didn't find the next fragment in out-of-order list"); + break; + } + } + + return foundLastSegment; } -} +} // namespace pcpp diff --git a/Packet++/src/IPSecLayer.cpp b/Packet++/src/IPSecLayer.cpp index 0ebee80198..d95d92c91c 100644 --- a/Packet++/src/IPSecLayer.cpp +++ b/Packet++/src/IPSecLayer.cpp @@ -1,131 +1,119 @@ #define LOG_MODULE PacketLogModuleIPSecLayer +#include "IPSecLayer.h" #include "EndianPortable.h" #include "GeneralUtils.h" -#include "IPSecLayer.h" #include "IPv4Layer.h" #include "IPv6Layer.h" -#include "UdpLayer.h" -#include "TcpLayer.h" #include "PayloadLayer.h" +#include "TcpLayer.h" +#include "UdpLayer.h" #include -namespace pcpp -{ +namespace pcpp { // --------------------------------- // AuthenticationHeaderLayer methods // --------------------------------- -uint32_t AuthenticationHeaderLayer::getSPI() const -{ - return be32toh(getAHHeader()->spi); +uint32_t AuthenticationHeaderLayer::getSPI() const { + return be32toh(getAHHeader()->spi); } -uint32_t AuthenticationHeaderLayer::getSequenceNumber() const -{ - return be32toh(getAHHeader()->sequenceNumber); +uint32_t AuthenticationHeaderLayer::getSequenceNumber() const { + return be32toh(getAHHeader()->sequenceNumber); } -size_t AuthenticationHeaderLayer::getICVLength() const -{ - // payloadLen = 3 (fixed ipsec_authentication_header size 32-bit words) + ICV - 2 - // ICV = (payloadLen + 2 - 3) in 32-bit words - return (getAHHeader()->payloadLen - 1)*4; +size_t AuthenticationHeaderLayer::getICVLength() const { + // payloadLen = 3 (fixed ipsec_authentication_header size 32-bit words) + ICV + // - 2 ICV = (payloadLen + 2 - 3) in 32-bit words + return (getAHHeader()->payloadLen - 1) * 4; } -uint8_t* AuthenticationHeaderLayer::getICVBytes() const -{ - size_t icvLength = getICVLength(); - if (icvLength > 0) - return m_Data + sizeof(ipsec_authentication_header); - return nullptr; +uint8_t* AuthenticationHeaderLayer::getICVBytes() const { + size_t icvLength = getICVLength(); + if (icvLength > 0) + return m_Data + sizeof(ipsec_authentication_header); + return nullptr; } -std::string AuthenticationHeaderLayer::getICVHexStream() const -{ - uint8_t* bytes = getICVBytes(); - if (bytes == nullptr) - return ""; +std::string AuthenticationHeaderLayer::getICVHexStream() const { + uint8_t* bytes = getICVBytes(); + if (bytes == nullptr) + return ""; - return byteArrayToHexString(bytes, getICVLength()); + return byteArrayToHexString(bytes, getICVLength()); } -void AuthenticationHeaderLayer::parseNextLayer() -{ - size_t headerLen = getHeaderLen(); - if (m_DataLen <= headerLen) - return; - - uint8_t* payload = m_Data + headerLen; - size_t payloadLen = m_DataLen - headerLen; - - switch (getAHHeader()->nextHeader) - { - case PACKETPP_IPPROTO_UDP: - if (payloadLen >= sizeof(udphdr)) - m_NextLayer = new UdpLayer(payload, payloadLen, this, m_Packet); - break; - case PACKETPP_IPPROTO_TCP: - m_NextLayer = TcpLayer::isDataValid(payload, payloadLen) - ? static_cast(new TcpLayer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case PACKETPP_IPPROTO_IPIP: - { - uint8_t ipVersion = *payload >> 4; - if (ipVersion == 4 && IPv4Layer::isDataValid(payload, payloadLen)) - m_NextLayer = new IPv4Layer(payload, payloadLen, this, m_Packet); - else if (ipVersion == 6 && IPv6Layer::isDataValid(payload, payloadLen)) - m_NextLayer = new IPv6Layer(payload, payloadLen, this, m_Packet); - else - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - break; - } - case PACKETPP_IPPROTO_ESP: - m_NextLayer = ESPLayer::isDataValid(payload, payloadLen) - ? static_cast(new ESPLayer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - default: - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - } +void AuthenticationHeaderLayer::parseNextLayer() { + size_t headerLen = getHeaderLen(); + if (m_DataLen <= headerLen) + return; + + uint8_t* payload = m_Data + headerLen; + size_t payloadLen = m_DataLen - headerLen; + + switch (getAHHeader()->nextHeader) { + case PACKETPP_IPPROTO_UDP: + if (payloadLen >= sizeof(udphdr)) + m_NextLayer = new UdpLayer(payload, payloadLen, this, m_Packet); + break; + case PACKETPP_IPPROTO_TCP: + m_NextLayer = TcpLayer::isDataValid(payload, payloadLen) + ? static_cast( + new TcpLayer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer( + payload, payloadLen, this, m_Packet)); + break; + case PACKETPP_IPPROTO_IPIP: { + uint8_t ipVersion = *payload >> 4; + if (ipVersion == 4 && IPv4Layer::isDataValid(payload, payloadLen)) + m_NextLayer = new IPv4Layer(payload, payloadLen, this, m_Packet); + else if (ipVersion == 6 && IPv6Layer::isDataValid(payload, payloadLen)) + m_NextLayer = new IPv6Layer(payload, payloadLen, this, m_Packet); + else + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + break; + } + case PACKETPP_IPPROTO_ESP: + m_NextLayer = ESPLayer::isDataValid(payload, payloadLen) + ? static_cast( + new ESPLayer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer( + payload, payloadLen, this, m_Packet)); + break; + default: + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + } } -std::string AuthenticationHeaderLayer::toString() const -{ - return "Authentication Header Layer"; +std::string AuthenticationHeaderLayer::toString() const { + return "Authentication Header Layer"; } - // ---------------- // ESPLayer methods // ---------------- -uint32_t ESPLayer::getSPI() const -{ - return be32toh(getESPHeader()->spi); -} +uint32_t ESPLayer::getSPI() const { return be32toh(getESPHeader()->spi); } -uint32_t ESPLayer::getSequenceNumber() const -{ - return be32toh(getESPHeader()->sequenceNumber); +uint32_t ESPLayer::getSequenceNumber() const { + return be32toh(getESPHeader()->sequenceNumber); } -void ESPLayer::parseNextLayer() -{ - size_t headerLen = getHeaderLen(); - if (m_DataLen <= headerLen) - return; +void ESPLayer::parseNextLayer() { + size_t headerLen = getHeaderLen(); + if (m_DataLen <= headerLen) + return; - m_NextLayer = new PayloadLayer(m_Data + headerLen, m_DataLen - headerLen, this, m_Packet); + m_NextLayer = new PayloadLayer(m_Data + headerLen, m_DataLen - headerLen, + this, m_Packet); } -std::string ESPLayer::toString() const -{ - std::ostringstream stream; - stream << "ESP Layer, SPI: 0x" << std::hex << getSPI(); - return stream.str(); +std::string ESPLayer::toString() const { + std::ostringstream stream; + stream << "ESP Layer, SPI: 0x" << std::hex << getSPI(); + return stream.str(); } } // namespace pcpp diff --git a/Packet++/src/IPv4Layer.cpp b/Packet++/src/IPv4Layer.cpp index c1a2de8004..cf4a343b5d 100644 --- a/Packet++/src/IPv4Layer.cpp +++ b/Packet++/src/IPv4Layer.cpp @@ -1,596 +1,594 @@ #define LOG_MODULE PacketLogModuleIPv4Layer #include "IPv4Layer.h" +#include "EndianPortable.h" +#include "GreLayer.h" +#include "IPSecLayer.h" #include "IPv6Layer.h" -#include "PayloadLayer.h" -#include "UdpLayer.h" -#include "TcpLayer.h" #include "IcmpLayer.h" -#include "GreLayer.h" #include "IgmpLayer.h" -#include "IPSecLayer.h" -#include "VrrpLayer.h" +#include "Logger.h" #include "PacketUtils.h" -#include +#include "PayloadLayer.h" +#include "TcpLayer.h" +#include "UdpLayer.h" +#include "VrrpLayer.h" #include -#include "Logger.h" -#include "EndianPortable.h" +#include -namespace pcpp -{ +namespace pcpp { #define IPV4OPT_DUMMY 0xff #define IPV4_MAX_OPT_SIZE 40 - /// ~~~~~~~~~~~~~~~~~ /// IPv4OptionBuilder /// ~~~~~~~~~~~~~~~~~ -IPv4OptionBuilder::IPv4OptionBuilder(IPv4OptionTypes optionType, const std::vector& ipList) -{ - m_RecType = (uint8_t)optionType; - m_RecValueLen = ipList.size() * sizeof(uint32_t) + sizeof(uint8_t); - m_RecValue = new uint8_t[m_RecValueLen]; +IPv4OptionBuilder::IPv4OptionBuilder(IPv4OptionTypes optionType, + const std::vector& ipList) { + m_RecType = (uint8_t)optionType; + m_RecValueLen = ipList.size() * sizeof(uint32_t) + sizeof(uint8_t); + m_RecValue = new uint8_t[m_RecValueLen]; - size_t curOffset = 0; - m_RecValue[curOffset++] = 0; // init pointer value + size_t curOffset = 0; + m_RecValue[curOffset++] = 0; // init pointer value - bool firstZero = false; - for (std::vector::const_iterator iter = ipList.begin(); iter != ipList.end(); iter++) - { - uint32_t ipAddrAsInt = iter->toInt(); + bool firstZero = false; + for (std::vector::const_iterator iter = ipList.begin(); + iter != ipList.end(); iter++) { + uint32_t ipAddrAsInt = iter->toInt(); - if (!firstZero) - m_RecValue[0] += (uint8_t)4; + if (!firstZero) + m_RecValue[0] += (uint8_t)4; - if (!firstZero && ipAddrAsInt == 0) - firstZero = true; + if (!firstZero && ipAddrAsInt == 0) + firstZero = true; - memcpy(m_RecValue + curOffset, &ipAddrAsInt, sizeof(uint32_t)); - curOffset += sizeof(uint32_t); - } + memcpy(m_RecValue + curOffset, &ipAddrAsInt, sizeof(uint32_t)); + curOffset += sizeof(uint32_t); + } - m_BuilderParamsValid = true; + m_BuilderParamsValid = true; } -IPv4OptionBuilder::IPv4OptionBuilder(const IPv4TimestampOptionValue& timestampValue) -{ - m_RecType = (uint8_t)IPV4OPT_Timestamp; - m_RecValueLen = 0; - m_RecValue = nullptr; - - if (timestampValue.type == IPv4TimestampOptionValue::Unknown) - { - PCPP_LOG_ERROR("Cannot build timestamp option of type IPv4TimestampOptionValue::Unknown"); - m_BuilderParamsValid = false; - return; - } - - if (timestampValue.type == IPv4TimestampOptionValue::TimestampsForPrespecifiedIPs) - { - PCPP_LOG_ERROR("Cannot build timestamp option of type IPv4TimestampOptionValue::TimestampsForPrespecifiedIPs - this type is not supported"); - m_BuilderParamsValid = false; - return; - } - - if (timestampValue.type == IPv4TimestampOptionValue::TimestampAndIP && timestampValue.timestamps.size() != timestampValue.ipAddresses.size()) - { - PCPP_LOG_ERROR("Cannot build timestamp option of type IPv4TimestampOptionValue::TimestampAndIP because number of timestamps and IP addresses is not equal"); - m_BuilderParamsValid = false; - return; - } - - m_RecValueLen = timestampValue.timestamps.size() * sizeof(uint32_t) + 2 * sizeof(uint8_t); - - if (timestampValue.type == IPv4TimestampOptionValue::TimestampAndIP) - { - m_RecValueLen += timestampValue.timestamps.size() * sizeof(uint32_t); - } - - m_RecValue = new uint8_t[m_RecValueLen]; - - size_t curOffset = 0; - m_RecValue[curOffset++] = 1; //pointer default value is 1 - means there are no empty timestamps - m_RecValue[curOffset++] = (uint8_t)timestampValue.type; // timestamp type - - int firstZero = -1; - for (int i = 0; i < (int)timestampValue.timestamps.size(); i++) - { - uint32_t timestamp = htobe32(timestampValue.timestamps.at(i)); - - // for pointer calculation - find the first timestamp equals to 0 - if (timestamp == 0 && firstZero == -1) - firstZero = i; - - if (timestampValue.type == IPv4TimestampOptionValue::TimestampAndIP) - { - uint32_t ipAddrAsInt = timestampValue.ipAddresses.at(i).toInt(); - memcpy(m_RecValue + curOffset , &ipAddrAsInt, sizeof(uint32_t)); - curOffset += sizeof(uint32_t); - } - - memcpy(m_RecValue + curOffset , ×tamp, sizeof(uint32_t)); - curOffset += sizeof(uint32_t); - } - - // calculate pointer field - if (firstZero > -1) - { - uint8_t pointerVal = (uint8_t)(4 * sizeof(uint8_t) + firstZero * sizeof(uint32_t) + 1); - if (timestampValue.type == IPv4TimestampOptionValue::TimestampAndIP) - pointerVal += (uint8_t)(firstZero * sizeof(uint32_t)); - - m_RecValue[0] = pointerVal; - } - - m_BuilderParamsValid = true; +IPv4OptionBuilder::IPv4OptionBuilder( + const IPv4TimestampOptionValue& timestampValue) { + m_RecType = (uint8_t)IPV4OPT_Timestamp; + m_RecValueLen = 0; + m_RecValue = nullptr; + + if (timestampValue.type == IPv4TimestampOptionValue::Unknown) { + PCPP_LOG_ERROR("Cannot build timestamp option of type " + "IPv4TimestampOptionValue::Unknown"); + m_BuilderParamsValid = false; + return; + } + + if (timestampValue.type == + IPv4TimestampOptionValue::TimestampsForPrespecifiedIPs) { + PCPP_LOG_ERROR("Cannot build timestamp option of type " + "IPv4TimestampOptionValue::TimestampsForPrespecifiedIPs - " + "this type is not supported"); + m_BuilderParamsValid = false; + return; + } + + if (timestampValue.type == IPv4TimestampOptionValue::TimestampAndIP && + timestampValue.timestamps.size() != timestampValue.ipAddresses.size()) { + PCPP_LOG_ERROR("Cannot build timestamp option of type " + "IPv4TimestampOptionValue::TimestampAndIP because number of " + "timestamps and IP addresses is not equal"); + m_BuilderParamsValid = false; + return; + } + + m_RecValueLen = + timestampValue.timestamps.size() * sizeof(uint32_t) + 2 * sizeof(uint8_t); + + if (timestampValue.type == IPv4TimestampOptionValue::TimestampAndIP) { + m_RecValueLen += timestampValue.timestamps.size() * sizeof(uint32_t); + } + + m_RecValue = new uint8_t[m_RecValueLen]; + + size_t curOffset = 0; + m_RecValue[curOffset++] = + 1; // pointer default value is 1 - means there are no empty timestamps + m_RecValue[curOffset++] = (uint8_t)timestampValue.type; // timestamp type + + int firstZero = -1; + for (int i = 0; i < (int)timestampValue.timestamps.size(); i++) { + uint32_t timestamp = htobe32(timestampValue.timestamps.at(i)); + + // for pointer calculation - find the first timestamp equals to 0 + if (timestamp == 0 && firstZero == -1) + firstZero = i; + + if (timestampValue.type == IPv4TimestampOptionValue::TimestampAndIP) { + uint32_t ipAddrAsInt = timestampValue.ipAddresses.at(i).toInt(); + memcpy(m_RecValue + curOffset, &ipAddrAsInt, sizeof(uint32_t)); + curOffset += sizeof(uint32_t); + } + + memcpy(m_RecValue + curOffset, ×tamp, sizeof(uint32_t)); + curOffset += sizeof(uint32_t); + } + + // calculate pointer field + if (firstZero > -1) { + uint8_t pointerVal = + (uint8_t)(4 * sizeof(uint8_t) + firstZero * sizeof(uint32_t) + 1); + if (timestampValue.type == IPv4TimestampOptionValue::TimestampAndIP) + pointerVal += (uint8_t)(firstZero * sizeof(uint32_t)); + + m_RecValue[0] = pointerVal; + } + + m_BuilderParamsValid = true; } -IPv4Option IPv4OptionBuilder::build() const -{ - if (!m_BuilderParamsValid) - return IPv4Option(nullptr); - - size_t optionSize = m_RecValueLen + 2 * sizeof(uint8_t); - - uint8_t recType = static_cast(m_RecType); - if ((recType == (uint8_t)IPV4OPT_NOP || recType == (uint8_t)IPV4OPT_EndOfOptionsList)) - { - if (m_RecValueLen != 0) - { - PCPP_LOG_ERROR("Can't set IPv4 NOP option or IPv4 End-of-options option with size different than 0, tried to set size " << (int)m_RecValueLen); - return IPv4Option(nullptr); - } - - optionSize = sizeof(uint8_t); - } - - uint8_t* recordBuffer = new uint8_t[optionSize]; - memset(recordBuffer, 0, optionSize); - recordBuffer[0] = recType; - if (optionSize > 1) - { - recordBuffer[1] = static_cast(optionSize); - if (optionSize > 2 && m_RecValue != nullptr) - memcpy(recordBuffer + 2, m_RecValue, m_RecValueLen); - } - - return IPv4Option(recordBuffer); +IPv4Option IPv4OptionBuilder::build() const { + if (!m_BuilderParamsValid) + return IPv4Option(nullptr); + + size_t optionSize = m_RecValueLen + 2 * sizeof(uint8_t); + + uint8_t recType = static_cast(m_RecType); + if ((recType == (uint8_t)IPV4OPT_NOP || + recType == (uint8_t)IPV4OPT_EndOfOptionsList)) { + if (m_RecValueLen != 0) { + PCPP_LOG_ERROR("Can't set IPv4 NOP option or IPv4 End-of-options option " + "with size different than 0, tried to set size " + << (int)m_RecValueLen); + return IPv4Option(nullptr); + } + + optionSize = sizeof(uint8_t); + } + + uint8_t* recordBuffer = new uint8_t[optionSize]; + memset(recordBuffer, 0, optionSize); + recordBuffer[0] = recType; + if (optionSize > 1) { + recordBuffer[1] = static_cast(optionSize); + if (optionSize > 2 && m_RecValue != nullptr) + memcpy(recordBuffer + 2, m_RecValue, m_RecValueLen); + } + + return IPv4Option(recordBuffer); } - /// ~~~~~~~~~ /// IPv4Layer /// ~~~~~~~~~ - -void IPv4Layer::initLayer() -{ - const size_t headerLen = sizeof(iphdr); - m_DataLen = headerLen; - m_Data = new uint8_t[headerLen]; - m_Protocol = IPv4; - memset(m_Data, 0, headerLen); - iphdr* ipHdr = getIPv4Header(); - ipHdr->internetHeaderLength = (5 & 0xf); - m_NumOfTrailingBytes = 0; - m_TempHeaderExtension = 0; +void IPv4Layer::initLayer() { + const size_t headerLen = sizeof(iphdr); + m_DataLen = headerLen; + m_Data = new uint8_t[headerLen]; + m_Protocol = IPv4; + memset(m_Data, 0, headerLen); + iphdr* ipHdr = getIPv4Header(); + ipHdr->internetHeaderLength = (5 & 0xf); + m_NumOfTrailingBytes = 0; + m_TempHeaderExtension = 0; } -void IPv4Layer::initLayerInPacket(bool setTotalLenAsDataLen) -{ - m_Protocol = IPv4; - m_NumOfTrailingBytes = 0; - m_TempHeaderExtension = 0; - if (setTotalLenAsDataLen) - { - size_t totalLen = be16toh(getIPv4Header()->totalLength); - // if totalLen == 0 this usually means TCP Segmentation Offload (TSO). In this case we should ignore the value of totalLen - // and look at the data captured on the wire - if ((totalLen < m_DataLen) && (totalLen !=0)) - m_DataLen = totalLen; - } +void IPv4Layer::initLayerInPacket(bool setTotalLenAsDataLen) { + m_Protocol = IPv4; + m_NumOfTrailingBytes = 0; + m_TempHeaderExtension = 0; + if (setTotalLenAsDataLen) { + size_t totalLen = be16toh(getIPv4Header()->totalLength); + // if totalLen == 0 this usually means TCP Segmentation Offload (TSO). In + // this case we should ignore the value of totalLen and look at the data + // captured on the wire + if ((totalLen < m_DataLen) && (totalLen != 0)) + m_DataLen = totalLen; + } } -void IPv4Layer::copyLayerData(const IPv4Layer& other) -{ - m_OptionReader = other.m_OptionReader; - m_NumOfTrailingBytes = other.m_NumOfTrailingBytes; - m_TempHeaderExtension = other.m_TempHeaderExtension; +void IPv4Layer::copyLayerData(const IPv4Layer& other) { + m_OptionReader = other.m_OptionReader; + m_NumOfTrailingBytes = other.m_NumOfTrailingBytes; + m_TempHeaderExtension = other.m_TempHeaderExtension; } -IPv4Layer::IPv4Layer() -{ - initLayer(); -} +IPv4Layer::IPv4Layer() { initLayer(); } -IPv4Layer::IPv4Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet, bool setTotalLenAsDataLen) : Layer(data, dataLen, prevLayer, packet) -{ - initLayerInPacket(setTotalLenAsDataLen); +IPv4Layer::IPv4Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet, bool setTotalLenAsDataLen) + : Layer(data, dataLen, prevLayer, packet) { + initLayerInPacket(setTotalLenAsDataLen); } -IPv4Layer::IPv4Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) -{ - initLayerInPacket(true); +IPv4Layer::IPv4Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet) + : Layer(data, dataLen, prevLayer, packet) { + initLayerInPacket(true); } -IPv4Layer::IPv4Layer(const IPv4Address& srcIP, const IPv4Address& dstIP) -{ - initLayer(); - iphdr* ipHdr = getIPv4Header(); - ipHdr->ipSrc = srcIP.toInt(); - ipHdr->ipDst = dstIP.toInt(); +IPv4Layer::IPv4Layer(const IPv4Address& srcIP, const IPv4Address& dstIP) { + initLayer(); + iphdr* ipHdr = getIPv4Header(); + ipHdr->ipSrc = srcIP.toInt(); + ipHdr->ipDst = dstIP.toInt(); } -IPv4Layer::IPv4Layer(const IPv4Layer& other) : Layer(other) -{ - copyLayerData(other); +IPv4Layer::IPv4Layer(const IPv4Layer& other) : Layer(other) { + copyLayerData(other); } -IPv4Layer& IPv4Layer::operator=(const IPv4Layer& other) -{ - Layer::operator=(other); +IPv4Layer& IPv4Layer::operator=(const IPv4Layer& other) { + Layer::operator=(other); - copyLayerData(other); + copyLayerData(other); - return *this; + return *this; } -void IPv4Layer::parseNextLayer() -{ - size_t hdrLen = getHeaderLen(); - if (m_DataLen <= hdrLen || hdrLen == 0) - return; - - iphdr* ipHdr = getIPv4Header(); - - ProtocolType greVer = UnknownProtocol; - ProtocolType igmpVer = UnknownProtocol; - bool igmpQuery = false; - - uint8_t ipVersion = 0; - - uint8_t* payload = m_Data + hdrLen; - size_t payloadLen = m_DataLen - hdrLen; - - // If it's a fragment don't parse upper layers, unless if it's the first fragment - // TODO: assuming first fragment contains at least L4 header, what if it's not true? - if (isFragment()) - { - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - return; - } - - switch (ipHdr->protocol) - { - case PACKETPP_IPPROTO_UDP: - if (payloadLen >= sizeof(udphdr)) - m_NextLayer = new UdpLayer(payload, payloadLen, this, m_Packet); - break; - case PACKETPP_IPPROTO_TCP: - m_NextLayer = TcpLayer::isDataValid(payload, payloadLen) - ? static_cast(new TcpLayer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case PACKETPP_IPPROTO_ICMP: - m_NextLayer = IcmpLayer::isDataValid(payload, payloadLen) - ? static_cast(new IcmpLayer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case PACKETPP_IPPROTO_IPIP: - ipVersion = *payload >> 4; - if (ipVersion == 4 && IPv4Layer::isDataValid(payload, payloadLen)) - m_NextLayer = new IPv4Layer(payload, payloadLen, this, m_Packet); - else if (ipVersion == 6 && IPv6Layer::isDataValid(payload, payloadLen)) - m_NextLayer = new IPv6Layer(payload, payloadLen, this, m_Packet); - else - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - break; - case PACKETPP_IPPROTO_GRE: - greVer = GreLayer::getGREVersion(payload, payloadLen); - if (greVer == GREv0 && GREv0Layer::isDataValid(payload, payloadLen)) - m_NextLayer = new GREv0Layer(payload, payloadLen, this, m_Packet); - else if (greVer == GREv1 && GREv1Layer::isDataValid(payload, payloadLen)) - m_NextLayer = new GREv1Layer(payload, payloadLen, this, m_Packet); - else - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - break; - case PACKETPP_IPPROTO_IGMP: - igmpVer = IgmpLayer::getIGMPVerFromData( - payload, std::min(payloadLen, be16toh(getIPv4Header()->totalLength) - hdrLen), igmpQuery); - if (igmpVer == IGMPv1) - m_NextLayer = new IgmpV1Layer(payload, payloadLen, this, m_Packet); - else if (igmpVer == IGMPv2) - m_NextLayer = new IgmpV2Layer(payload, payloadLen, this, m_Packet); - else if (igmpVer == IGMPv3) - { - if (igmpQuery) - m_NextLayer = new IgmpV3QueryLayer(payload, payloadLen, this, m_Packet); - else - m_NextLayer = new IgmpV3ReportLayer(payload, payloadLen, this, m_Packet); - } - else - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - break; - case PACKETPP_IPPROTO_AH: - m_NextLayer = AuthenticationHeaderLayer::isDataValid(payload, payloadLen) - ? static_cast(new AuthenticationHeaderLayer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case PACKETPP_IPPROTO_ESP: - m_NextLayer = ESPLayer::isDataValid(payload, payloadLen) - ? static_cast(new ESPLayer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case PACKETPP_IPPROTO_IPV6: - m_NextLayer = IPv6Layer::isDataValid(payload, payloadLen) - ? static_cast(new IPv6Layer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case PACKETPP_IPPROTO_VRRP: - { - auto vrrpVer = VrrpLayer::getVersionFromData(payload, payloadLen); - if (vrrpVer == VRRPv2) - m_NextLayer = new VrrpV2Layer(payload, payloadLen, this, m_Packet); - else if (vrrpVer == VRRPv3) - m_NextLayer = new VrrpV3Layer(payload, payloadLen, this, m_Packet, IPAddress::IPv4AddressType); - else - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - break; - } - default: - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - } +void IPv4Layer::parseNextLayer() { + size_t hdrLen = getHeaderLen(); + if (m_DataLen <= hdrLen || hdrLen == 0) + return; + + iphdr* ipHdr = getIPv4Header(); + + ProtocolType greVer = UnknownProtocol; + ProtocolType igmpVer = UnknownProtocol; + bool igmpQuery = false; + + uint8_t ipVersion = 0; + + uint8_t* payload = m_Data + hdrLen; + size_t payloadLen = m_DataLen - hdrLen; + + // If it's a fragment don't parse upper layers, unless if it's the first + // fragment + // TODO: assuming first fragment contains at least L4 header, what if it's not + // true? + if (isFragment()) { + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + return; + } + + switch (ipHdr->protocol) { + case PACKETPP_IPPROTO_UDP: + if (payloadLen >= sizeof(udphdr)) + m_NextLayer = new UdpLayer(payload, payloadLen, this, m_Packet); + break; + case PACKETPP_IPPROTO_TCP: + m_NextLayer = TcpLayer::isDataValid(payload, payloadLen) + ? static_cast( + new TcpLayer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer( + payload, payloadLen, this, m_Packet)); + break; + case PACKETPP_IPPROTO_ICMP: + m_NextLayer = IcmpLayer::isDataValid(payload, payloadLen) + ? static_cast( + new IcmpLayer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer( + payload, payloadLen, this, m_Packet)); + break; + case PACKETPP_IPPROTO_IPIP: + ipVersion = *payload >> 4; + if (ipVersion == 4 && IPv4Layer::isDataValid(payload, payloadLen)) + m_NextLayer = new IPv4Layer(payload, payloadLen, this, m_Packet); + else if (ipVersion == 6 && IPv6Layer::isDataValid(payload, payloadLen)) + m_NextLayer = new IPv6Layer(payload, payloadLen, this, m_Packet); + else + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + break; + case PACKETPP_IPPROTO_GRE: + greVer = GreLayer::getGREVersion(payload, payloadLen); + if (greVer == GREv0 && GREv0Layer::isDataValid(payload, payloadLen)) + m_NextLayer = new GREv0Layer(payload, payloadLen, this, m_Packet); + else if (greVer == GREv1 && GREv1Layer::isDataValid(payload, payloadLen)) + m_NextLayer = new GREv1Layer(payload, payloadLen, this, m_Packet); + else + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + break; + case PACKETPP_IPPROTO_IGMP: + igmpVer = IgmpLayer::getIGMPVerFromData( + payload, + std::min(payloadLen, + be16toh(getIPv4Header()->totalLength) - hdrLen), + igmpQuery); + if (igmpVer == IGMPv1) + m_NextLayer = new IgmpV1Layer(payload, payloadLen, this, m_Packet); + else if (igmpVer == IGMPv2) + m_NextLayer = new IgmpV2Layer(payload, payloadLen, this, m_Packet); + else if (igmpVer == IGMPv3) { + if (igmpQuery) + m_NextLayer = new IgmpV3QueryLayer(payload, payloadLen, this, m_Packet); + else + m_NextLayer = + new IgmpV3ReportLayer(payload, payloadLen, this, m_Packet); + } else + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + break; + case PACKETPP_IPPROTO_AH: + m_NextLayer = AuthenticationHeaderLayer::isDataValid(payload, payloadLen) + ? static_cast(new AuthenticationHeaderLayer( + payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer( + payload, payloadLen, this, m_Packet)); + break; + case PACKETPP_IPPROTO_ESP: + m_NextLayer = ESPLayer::isDataValid(payload, payloadLen) + ? static_cast( + new ESPLayer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer( + payload, payloadLen, this, m_Packet)); + break; + case PACKETPP_IPPROTO_IPV6: + m_NextLayer = IPv6Layer::isDataValid(payload, payloadLen) + ? static_cast( + new IPv6Layer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer( + payload, payloadLen, this, m_Packet)); + break; + case PACKETPP_IPPROTO_VRRP: { + auto vrrpVer = VrrpLayer::getVersionFromData(payload, payloadLen); + if (vrrpVer == VRRPv2) + m_NextLayer = new VrrpV2Layer(payload, payloadLen, this, m_Packet); + else if (vrrpVer == VRRPv3) + m_NextLayer = new VrrpV3Layer(payload, payloadLen, this, m_Packet, + IPAddress::IPv4AddressType); + else + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + break; + } + default: + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + } } -void IPv4Layer::computeCalculateFields() -{ - iphdr* ipHdr = getIPv4Header(); - ipHdr->ipVersion = (4 & 0x0f); - ipHdr->totalLength = htobe16(m_DataLen); - ipHdr->headerChecksum = 0; - - if (m_NextLayer != nullptr) - { - switch (m_NextLayer->getProtocol()) - { - case TCP: - ipHdr->protocol = PACKETPP_IPPROTO_TCP; - break; - case UDP: - ipHdr->protocol = PACKETPP_IPPROTO_UDP; - break; - case ICMP: - ipHdr->protocol = PACKETPP_IPPROTO_ICMP; - break; - case GREv0: - case GREv1: - ipHdr->protocol = PACKETPP_IPPROTO_GRE; - break; - case IGMPv1: - case IGMPv2: - case IGMPv3: - ipHdr->protocol = PACKETPP_IPPROTO_IGMP; - break; - case VRRPv2: - case VRRPv3: - ipHdr->protocol = PACKETPP_IPPROTO_VRRP; - break; - default: - break; - } - } - - ScalarBuffer scalar = { (uint16_t*)ipHdr, (size_t)(ipHdr->internetHeaderLength*4) } ; - ipHdr->headerChecksum = htobe16(computeChecksum(&scalar, 1)); +void IPv4Layer::computeCalculateFields() { + iphdr* ipHdr = getIPv4Header(); + ipHdr->ipVersion = (4 & 0x0f); + ipHdr->totalLength = htobe16(m_DataLen); + ipHdr->headerChecksum = 0; + + if (m_NextLayer != nullptr) { + switch (m_NextLayer->getProtocol()) { + case TCP: + ipHdr->protocol = PACKETPP_IPPROTO_TCP; + break; + case UDP: + ipHdr->protocol = PACKETPP_IPPROTO_UDP; + break; + case ICMP: + ipHdr->protocol = PACKETPP_IPPROTO_ICMP; + break; + case GREv0: + case GREv1: + ipHdr->protocol = PACKETPP_IPPROTO_GRE; + break; + case IGMPv1: + case IGMPv2: + case IGMPv3: + ipHdr->protocol = PACKETPP_IPPROTO_IGMP; + break; + case VRRPv2: + case VRRPv3: + ipHdr->protocol = PACKETPP_IPPROTO_VRRP; + break; + default: + break; + } + } + + ScalarBuffer scalar = {(uint16_t*)ipHdr, + (size_t)(ipHdr->internetHeaderLength * 4)}; + ipHdr->headerChecksum = htobe16(computeChecksum(&scalar, 1)); } -bool IPv4Layer::isFragment() const -{ - return ((getFragmentFlags() & PCPP_IP_MORE_FRAGMENTS) != 0 || getFragmentOffset() != 0); +bool IPv4Layer::isFragment() const { + return ((getFragmentFlags() & PCPP_IP_MORE_FRAGMENTS) != 0 || + getFragmentOffset() != 0); } -bool IPv4Layer::isFirstFragment() const -{ - return isFragment() && (getFragmentOffset() == 0); +bool IPv4Layer::isFirstFragment() const { + return isFragment() && (getFragmentOffset() == 0); } -bool IPv4Layer::isLastFragment() const -{ - return isFragment() && ((getFragmentFlags() & PCPP_IP_MORE_FRAGMENTS) == 0); +bool IPv4Layer::isLastFragment() const { + return isFragment() && ((getFragmentFlags() & PCPP_IP_MORE_FRAGMENTS) == 0); } -uint8_t IPv4Layer::getFragmentFlags() const -{ - return getIPv4Header()->fragmentOffset & 0xE0; +uint8_t IPv4Layer::getFragmentFlags() const { + return getIPv4Header()->fragmentOffset & 0xE0; } -uint16_t IPv4Layer::getFragmentOffset() const -{ - return be16toh(getIPv4Header()->fragmentOffset & (uint16_t)0xFF1F) * 8; +uint16_t IPv4Layer::getFragmentOffset() const { + return be16toh(getIPv4Header()->fragmentOffset & (uint16_t)0xFF1F) * 8; } -std::string IPv4Layer::toString() const -{ - std::string fragment = ""; - if (isFragment()) - { - if (isFirstFragment()) - fragment = "First fragment"; - else if (isLastFragment()) - fragment = "Last fragment"; - else - fragment = "Fragment"; - - std::stringstream sstm; - sstm << fragment << " [offset= " << getFragmentOffset() << "], "; - fragment = sstm.str(); - } - - return "IPv4 Layer, " + fragment + "Src: " + getSrcIPv4Address().toString() + ", Dst: " + getDstIPv4Address().toString(); +std::string IPv4Layer::toString() const { + std::string fragment = ""; + if (isFragment()) { + if (isFirstFragment()) + fragment = "First fragment"; + else if (isLastFragment()) + fragment = "Last fragment"; + else + fragment = "Fragment"; + + std::stringstream sstm; + sstm << fragment << " [offset= " << getFragmentOffset() << "], "; + fragment = sstm.str(); + } + + return "IPv4 Layer, " + fragment + "Src: " + getSrcIPv4Address().toString() + + ", Dst: " + getDstIPv4Address().toString(); } -IPv4Option IPv4Layer::getOption(IPv4OptionTypes option) const -{ - return m_OptionReader.getTLVRecord((uint8_t)option, getOptionsBasePtr(), getHeaderLen() - sizeof(iphdr)); +IPv4Option IPv4Layer::getOption(IPv4OptionTypes option) const { + return m_OptionReader.getTLVRecord((uint8_t)option, getOptionsBasePtr(), + getHeaderLen() - sizeof(iphdr)); } -IPv4Option IPv4Layer::getFirstOption() const -{ - return m_OptionReader.getFirstTLVRecord(getOptionsBasePtr(), getHeaderLen() - sizeof(iphdr)); +IPv4Option IPv4Layer::getFirstOption() const { + return m_OptionReader.getFirstTLVRecord(getOptionsBasePtr(), + getHeaderLen() - sizeof(iphdr)); } -IPv4Option IPv4Layer::getNextOption(IPv4Option& option) const -{ - return m_OptionReader.getNextTLVRecord(option, getOptionsBasePtr(), getHeaderLen() - sizeof(iphdr)); +IPv4Option IPv4Layer::getNextOption(IPv4Option& option) const { + return m_OptionReader.getNextTLVRecord(option, getOptionsBasePtr(), + getHeaderLen() - sizeof(iphdr)); } -size_t IPv4Layer::getOptionCount() const -{ - return m_OptionReader.getTLVRecordCount(getOptionsBasePtr(), getHeaderLen() - sizeof(iphdr)); +size_t IPv4Layer::getOptionCount() const { + return m_OptionReader.getTLVRecordCount(getOptionsBasePtr(), + getHeaderLen() - sizeof(iphdr)); } -void IPv4Layer::adjustOptionsTrailer(size_t totalOptSize) -{ - size_t ipHdrSize = sizeof(iphdr); +void IPv4Layer::adjustOptionsTrailer(size_t totalOptSize) { + size_t ipHdrSize = sizeof(iphdr); - int newNumberOfTrailingBytes = 0; - while ((totalOptSize + newNumberOfTrailingBytes) % 4 != 0) - newNumberOfTrailingBytes++; + int newNumberOfTrailingBytes = 0; + while ((totalOptSize + newNumberOfTrailingBytes) % 4 != 0) + newNumberOfTrailingBytes++; - if (newNumberOfTrailingBytes < m_NumOfTrailingBytes) - shortenLayer(ipHdrSize+totalOptSize, m_NumOfTrailingBytes - newNumberOfTrailingBytes); - else if (newNumberOfTrailingBytes > m_NumOfTrailingBytes) - extendLayer(ipHdrSize+totalOptSize, newNumberOfTrailingBytes - m_NumOfTrailingBytes); + if (newNumberOfTrailingBytes < m_NumOfTrailingBytes) + shortenLayer(ipHdrSize + totalOptSize, + m_NumOfTrailingBytes - newNumberOfTrailingBytes); + else if (newNumberOfTrailingBytes > m_NumOfTrailingBytes) + extendLayer(ipHdrSize + totalOptSize, + newNumberOfTrailingBytes - m_NumOfTrailingBytes); - m_NumOfTrailingBytes = newNumberOfTrailingBytes; + m_NumOfTrailingBytes = newNumberOfTrailingBytes; - for (int i = 0; i < m_NumOfTrailingBytes; i++) - m_Data[ipHdrSize + totalOptSize + i] = IPV4OPT_DUMMY; + for (int i = 0; i < m_NumOfTrailingBytes; i++) + m_Data[ipHdrSize + totalOptSize + i] = IPV4OPT_DUMMY; - m_TempHeaderExtension = 0; - getIPv4Header()->internetHeaderLength = ((ipHdrSize + totalOptSize + m_NumOfTrailingBytes)/4 & 0x0f); + m_TempHeaderExtension = 0; + getIPv4Header()->internetHeaderLength = + ((ipHdrSize + totalOptSize + m_NumOfTrailingBytes) / 4 & 0x0f); } -IPv4Option IPv4Layer::addOptionAt(const IPv4OptionBuilder& optionBuilder, int offset) -{ - IPv4Option newOption = optionBuilder.build(); - if (newOption.isNull()) - return newOption; - - size_t sizeToExtend = newOption.getTotalSize(); - - size_t totalOptSize = getHeaderLen() - sizeof(iphdr) - m_NumOfTrailingBytes + sizeToExtend; - - if (totalOptSize > IPV4_MAX_OPT_SIZE) - { - PCPP_LOG_ERROR("Cannot add option - adding this option will exceed IPv4 total option size which is " << IPV4_MAX_OPT_SIZE); - newOption.purgeRecordData(); - return IPv4Option(nullptr); - } - - if (!extendLayer(offset, sizeToExtend)) - { - PCPP_LOG_ERROR("Could not extend IPv4Layer in [" << sizeToExtend << "] bytes"); - newOption.purgeRecordData(); - return IPv4Option(nullptr); - } - - memcpy(m_Data + offset, newOption.getRecordBasePtr(), newOption.getTotalSize()); - - newOption.purgeRecordData(); - - // setting this m_TempHeaderExtension because adjustOptionsTrailer() may extend or shorten the layer and the extend or shorten methods need to know the accurate - // current size of the header. m_TempHeaderExtension will be added to the length extracted from getIPv4Header()->internetHeaderLength as the temp new size - m_TempHeaderExtension = sizeToExtend; - adjustOptionsTrailer(totalOptSize); - // the adjustOptionsTrailer() adds or removed the trailing bytes and sets getIPv4Header()->internetHeaderLength to the correct size, so the m_TempHeaderExtension - // isn't needed anymore - m_TempHeaderExtension = 0; - - m_OptionReader.changeTLVRecordCount(1); - - uint8_t* newOptPtr = m_Data + offset; - - return IPv4Option(newOptPtr); +IPv4Option IPv4Layer::addOptionAt(const IPv4OptionBuilder& optionBuilder, + int offset) { + IPv4Option newOption = optionBuilder.build(); + if (newOption.isNull()) + return newOption; + + size_t sizeToExtend = newOption.getTotalSize(); + + size_t totalOptSize = + getHeaderLen() - sizeof(iphdr) - m_NumOfTrailingBytes + sizeToExtend; + + if (totalOptSize > IPV4_MAX_OPT_SIZE) { + PCPP_LOG_ERROR("Cannot add option - adding this option will exceed IPv4 " + "total option size which is " + << IPV4_MAX_OPT_SIZE); + newOption.purgeRecordData(); + return IPv4Option(nullptr); + } + + if (!extendLayer(offset, sizeToExtend)) { + PCPP_LOG_ERROR("Could not extend IPv4Layer in [" << sizeToExtend + << "] bytes"); + newOption.purgeRecordData(); + return IPv4Option(nullptr); + } + + memcpy(m_Data + offset, newOption.getRecordBasePtr(), + newOption.getTotalSize()); + + newOption.purgeRecordData(); + + // setting this m_TempHeaderExtension because adjustOptionsTrailer() may + // extend or shorten the layer and the extend or shorten methods need to know + // the accurate current size of the header. m_TempHeaderExtension will be + // added to the length extracted from getIPv4Header()->internetHeaderLength as + // the temp new size + m_TempHeaderExtension = sizeToExtend; + adjustOptionsTrailer(totalOptSize); + // the adjustOptionsTrailer() adds or removed the trailing bytes and sets + // getIPv4Header()->internetHeaderLength to the correct size, so the + // m_TempHeaderExtension isn't needed anymore + m_TempHeaderExtension = 0; + + m_OptionReader.changeTLVRecordCount(1); + + uint8_t* newOptPtr = m_Data + offset; + + return IPv4Option(newOptPtr); } -IPv4Option IPv4Layer::addOption(const IPv4OptionBuilder& optionBuilder) -{ - return addOptionAt(optionBuilder, getHeaderLen() - m_NumOfTrailingBytes); +IPv4Option IPv4Layer::addOption(const IPv4OptionBuilder& optionBuilder) { + return addOptionAt(optionBuilder, getHeaderLen() - m_NumOfTrailingBytes); } -IPv4Option IPv4Layer::addOptionAfter(const IPv4OptionBuilder& optionBuilder, IPv4OptionTypes prevOptionType) -{ - int offset = 0; +IPv4Option IPv4Layer::addOptionAfter(const IPv4OptionBuilder& optionBuilder, + IPv4OptionTypes prevOptionType) { + int offset = 0; - IPv4Option prevOpt = getOption(prevOptionType); + IPv4Option prevOpt = getOption(prevOptionType); - if (prevOpt.isNull()) - { - offset = sizeof(iphdr); - } - else - { - offset = prevOpt.getRecordBasePtr() + prevOpt.getTotalSize() - m_Data; - } + if (prevOpt.isNull()) { + offset = sizeof(iphdr); + } else { + offset = prevOpt.getRecordBasePtr() + prevOpt.getTotalSize() - m_Data; + } - return addOptionAt(optionBuilder, offset); + return addOptionAt(optionBuilder, offset); } -bool IPv4Layer::removeOption(IPv4OptionTypes option) -{ - IPv4Option opt = getOption(option); - if (opt.isNull()) - { - return false; - } - - // calculate total option size - IPv4Option curOpt = getFirstOption(); - size_t totalOptSize = 0; - while (!curOpt.isNull()) - { - totalOptSize += curOpt.getTotalSize(); - curOpt = getNextOption(curOpt); - } - totalOptSize -= opt.getTotalSize(); - - - int offset = opt.getRecordBasePtr() - m_Data; - - size_t sizeToShorten = opt.getTotalSize(); - if (!shortenLayer(offset, sizeToShorten)) - { - PCPP_LOG_ERROR("Failed to remove IPv4 option: cannot shorten layer"); - return false; - } - - // setting this m_TempHeaderExtension because adjustOptionsTrailer() may extend or shorten the layer and the extend or shorten methods need to know the accurate - // current size of the header. m_TempHeaderExtension will be added to the length extracted from getIPv4Header()->internetHeaderLength as the temp new size - m_TempHeaderExtension = 0 - sizeToShorten; - adjustOptionsTrailer(totalOptSize); - // the adjustOptionsTrailer() adds or removed the trailing bytes and sets getIPv4Header()->internetHeaderLength to the correct size, so the m_TempHeaderExtension - // isn't needed anymore - m_TempHeaderExtension = 0; - - m_OptionReader.changeTLVRecordCount(-1); - - return true; +bool IPv4Layer::removeOption(IPv4OptionTypes option) { + IPv4Option opt = getOption(option); + if (opt.isNull()) { + return false; + } + + // calculate total option size + IPv4Option curOpt = getFirstOption(); + size_t totalOptSize = 0; + while (!curOpt.isNull()) { + totalOptSize += curOpt.getTotalSize(); + curOpt = getNextOption(curOpt); + } + totalOptSize -= opt.getTotalSize(); + + int offset = opt.getRecordBasePtr() - m_Data; + + size_t sizeToShorten = opt.getTotalSize(); + if (!shortenLayer(offset, sizeToShorten)) { + PCPP_LOG_ERROR("Failed to remove IPv4 option: cannot shorten layer"); + return false; + } + + // setting this m_TempHeaderExtension because adjustOptionsTrailer() may + // extend or shorten the layer and the extend or shorten methods need to know + // the accurate current size of the header. m_TempHeaderExtension will be + // added to the length extracted from getIPv4Header()->internetHeaderLength as + // the temp new size + m_TempHeaderExtension = 0 - sizeToShorten; + adjustOptionsTrailer(totalOptSize); + // the adjustOptionsTrailer() adds or removed the trailing bytes and sets + // getIPv4Header()->internetHeaderLength to the correct size, so the + // m_TempHeaderExtension isn't needed anymore + m_TempHeaderExtension = 0; + + m_OptionReader.changeTLVRecordCount(-1); + + return true; } -bool IPv4Layer::removeAllOptions() -{ - int offset = sizeof(iphdr); +bool IPv4Layer::removeAllOptions() { + int offset = sizeof(iphdr); - if (!shortenLayer(offset, getHeaderLen() - offset)) - return false; + if (!shortenLayer(offset, getHeaderLen() - offset)) + return false; - getIPv4Header()->internetHeaderLength = (5 & 0xf); - m_NumOfTrailingBytes = 0; - m_OptionReader.changeTLVRecordCount(0 - getOptionCount()); - return true; + getIPv4Header()->internetHeaderLength = (5 & 0xf); + m_NumOfTrailingBytes = 0; + m_OptionReader.changeTLVRecordCount(0 - getOptionCount()); + return true; } } // namespace pcpp diff --git a/Packet++/src/IPv6Extensions.cpp b/Packet++/src/IPv6Extensions.cpp index 2ce69ab704..e91ae45deb 100644 --- a/Packet++/src/IPv6Extensions.cpp +++ b/Packet++/src/IPv6Extensions.cpp @@ -1,283 +1,282 @@ #define LOG_MODULE PacketLogModuleIPv6ExtensionLayer -#include -#include "EndianPortable.h" -#include "Logger.h" #include "IPv6Extensions.h" -#include "IPv6Layer.h" +#include "EndianPortable.h" +#include "GreLayer.h" #include "IPv4Layer.h" +#include "IPv6Layer.h" +#include "Logger.h" #include "PayloadLayer.h" -#include "UdpLayer.h" #include "TcpLayer.h" -#include "GreLayer.h" +#include "UdpLayer.h" +#include -namespace pcpp -{ +namespace pcpp { // ============= // IPv6Extension // ============= -IPv6Extension& IPv6Extension::operator=(const IPv6Extension& other) -{ - // notice this is not necessarily safe - it assumes the current extension has enough memory allocated to consume - // the other extension. That's why the assignment operator isn't public (it's currently used only inside IPv6Layer) - memcpy(getDataPtr(), other.getDataPtr(), other.getExtensionLen()); - m_NextHeader = nullptr; - m_ExtType = other.m_ExtType; +IPv6Extension& IPv6Extension::operator=(const IPv6Extension& other) { + // notice this is not necessarily safe - it assumes the current extension has + // enough memory allocated to consume the other extension. That's why the + // assignment operator isn't public (it's currently used only inside + // IPv6Layer) + memcpy(getDataPtr(), other.getDataPtr(), other.getExtensionLen()); + m_NextHeader = nullptr; + m_ExtType = other.m_ExtType; - return *this; + return *this; } -uint8_t* IPv6Extension::getDataPtr() const -{ - if (m_DataContainer != nullptr) - return m_DataContainer->getDataPtr(m_Offset); +uint8_t* IPv6Extension::getDataPtr() const { + if (m_DataContainer != nullptr) + return m_DataContainer->getDataPtr(m_Offset); - return m_ShadowData; + return m_ShadowData; } -void IPv6Extension::initShadowPtr(size_t size) -{ - m_ShadowData = new uint8_t[size]; +void IPv6Extension::initShadowPtr(size_t size) { + m_ShadowData = new uint8_t[size]; } -IPv6Extension::~IPv6Extension() -{ - if (m_ShadowData != nullptr) - delete [] m_ShadowData; +IPv6Extension::~IPv6Extension() { + if (m_ShadowData != nullptr) + delete[] m_ShadowData; } // ======================= // IPv6FragmentationHeader // ======================= -IPv6FragmentationHeader::IPv6FragmentationHeader(uint32_t fragId, uint16_t fragOffset, bool lastFragment) -{ - initShadowPtr(sizeof(ipv6_frag_header)); - m_ExtType = IPv6Fragmentation; - memset(getDataPtr(), 0, sizeof(ipv6_frag_header)); +IPv6FragmentationHeader::IPv6FragmentationHeader(uint32_t fragId, + uint16_t fragOffset, + bool lastFragment) { + initShadowPtr(sizeof(ipv6_frag_header)); + m_ExtType = IPv6Fragmentation; + memset(getDataPtr(), 0, sizeof(ipv6_frag_header)); - ipv6_frag_header* fragHdr = getFragHeader(); - fragHdr->nextHeader = 0; - fragHdr->headerLen = 0; - fragHdr->id = htobe32(fragId); + ipv6_frag_header* fragHdr = getFragHeader(); + fragHdr->nextHeader = 0; + fragHdr->headerLen = 0; + fragHdr->id = htobe32(fragId); - fragOffset /= 8; - fragOffset = htobe16(fragOffset << 3) & (uint16_t)0xf8ff; - if (!lastFragment) - fragOffset = fragOffset | 0x0100; + fragOffset /= 8; + fragOffset = htobe16(fragOffset << 3) & (uint16_t)0xf8ff; + if (!lastFragment) + fragOffset = fragOffset | 0x0100; - fragHdr->fragOffsetAndFlags = fragOffset; + fragHdr->fragOffsetAndFlags = fragOffset; } -bool IPv6FragmentationHeader::isFirstFragment() const -{ - return (getFragmentOffset() == 0); +bool IPv6FragmentationHeader::isFirstFragment() const { + return (getFragmentOffset() == 0); } -bool IPv6FragmentationHeader::isLastFragment() const -{ - return (!isMoreFragments()); +bool IPv6FragmentationHeader::isLastFragment() const { + return (!isMoreFragments()); } -bool IPv6FragmentationHeader::isMoreFragments() const -{ - uint8_t isMoreFragsBit = (getFragHeader()->fragOffsetAndFlags & (uint16_t)0x0100) >> 8; - return (isMoreFragsBit == 1); +bool IPv6FragmentationHeader::isMoreFragments() const { + uint8_t isMoreFragsBit = + (getFragHeader()->fragOffsetAndFlags & (uint16_t)0x0100) >> 8; + return (isMoreFragsBit == 1); } -uint16_t IPv6FragmentationHeader::getFragmentOffset() const -{ - uint16_t fragOffset = (be16toh(getFragHeader()->fragOffsetAndFlags & (uint16_t)0xf8ff) >> 3) * 8; - return fragOffset; +uint16_t IPv6FragmentationHeader::getFragmentOffset() const { + uint16_t fragOffset = + (be16toh(getFragHeader()->fragOffsetAndFlags & (uint16_t)0xf8ff) >> 3) * + 8; + return fragOffset; } // ==================== // IPv6TLVOptionBuilder // ==================== -IPv6TLVOptionHeader::IPv6Option IPv6TLVOptionHeader::IPv6TLVOptionBuilder::build() const -{ - size_t optionTotalSize = sizeof(uint8_t); - uint8_t recType = static_cast(m_RecType); - if (recType != IPv6TLVOptionHeader::IPv6Option::Pad0OptionType) - optionTotalSize += sizeof(uint8_t) + m_RecValueLen; - - uint8_t* recordBuffer = new uint8_t[optionTotalSize]; - memset(recordBuffer, 0, optionTotalSize); - - if (m_RecType != IPv6TLVOptionHeader::IPv6Option::Pad0OptionType) - { - recordBuffer[0] = recType; - recordBuffer[1] = static_cast(m_RecValueLen); - if (m_RecValueLen > 0) - memcpy(recordBuffer+2, m_RecValue, m_RecValueLen); - } - - return IPv6Option(recordBuffer); +IPv6TLVOptionHeader::IPv6Option +IPv6TLVOptionHeader::IPv6TLVOptionBuilder::build() const { + size_t optionTotalSize = sizeof(uint8_t); + uint8_t recType = static_cast(m_RecType); + if (recType != IPv6TLVOptionHeader::IPv6Option::Pad0OptionType) + optionTotalSize += sizeof(uint8_t) + m_RecValueLen; + + uint8_t* recordBuffer = new uint8_t[optionTotalSize]; + memset(recordBuffer, 0, optionTotalSize); + + if (m_RecType != IPv6TLVOptionHeader::IPv6Option::Pad0OptionType) { + recordBuffer[0] = recType; + recordBuffer[1] = static_cast(m_RecValueLen); + if (m_RecValueLen > 0) + memcpy(recordBuffer + 2, m_RecValue, m_RecValueLen); + } + + return IPv6Option(recordBuffer); } // =================== // IPv6TLVOptionHeader // =================== -IPv6TLVOptionHeader::IPv6Option IPv6TLVOptionHeader::getOption(uint8_t optionType) const -{ - return m_OptionReader.getTLVRecord(optionType, getDataPtr() + sizeof(ipv6_ext_base_header), getExtensionLen() - sizeof(ipv6_ext_base_header)); +IPv6TLVOptionHeader::IPv6Option +IPv6TLVOptionHeader::getOption(uint8_t optionType) const { + return m_OptionReader.getTLVRecord( + optionType, getDataPtr() + sizeof(ipv6_ext_base_header), + getExtensionLen() - sizeof(ipv6_ext_base_header)); } -IPv6TLVOptionHeader::IPv6Option IPv6TLVOptionHeader::getFirstOption() const -{ - return m_OptionReader.getFirstTLVRecord(getDataPtr() + sizeof(ipv6_ext_base_header), getExtensionLen() - sizeof(ipv6_ext_base_header)); +IPv6TLVOptionHeader::IPv6Option IPv6TLVOptionHeader::getFirstOption() const { + return m_OptionReader.getFirstTLVRecord( + getDataPtr() + sizeof(ipv6_ext_base_header), + getExtensionLen() - sizeof(ipv6_ext_base_header)); } -IPv6TLVOptionHeader::IPv6Option IPv6TLVOptionHeader::getNextOption(IPv6TLVOptionHeader::IPv6Option& option) const -{ - return m_OptionReader.getNextTLVRecord(option, getDataPtr() + sizeof(ipv6_ext_base_header), getExtensionLen() - sizeof(ipv6_ext_base_header)); +IPv6TLVOptionHeader::IPv6Option IPv6TLVOptionHeader::getNextOption( + IPv6TLVOptionHeader::IPv6Option& option) const { + return m_OptionReader.getNextTLVRecord( + option, getDataPtr() + sizeof(ipv6_ext_base_header), + getExtensionLen() - sizeof(ipv6_ext_base_header)); } -size_t IPv6TLVOptionHeader::getOptionCount() const -{ - return m_OptionReader.getTLVRecordCount(getDataPtr() + sizeof(ipv6_ext_base_header), getExtensionLen() - sizeof(ipv6_ext_base_header)); +size_t IPv6TLVOptionHeader::getOptionCount() const { + return m_OptionReader.getTLVRecordCount( + getDataPtr() + sizeof(ipv6_ext_base_header), + getExtensionLen() - sizeof(ipv6_ext_base_header)); } -IPv6TLVOptionHeader::IPv6TLVOptionHeader(const std::vector& options) -{ - m_ExtType = IPv6ExtensionUnknown; - m_OptionReader.changeTLVRecordCount(options.size()); +IPv6TLVOptionHeader::IPv6TLVOptionHeader( + const std::vector& options) { + m_ExtType = IPv6ExtensionUnknown; + m_OptionReader.changeTLVRecordCount(options.size()); - size_t totalSize = sizeof(uint16_t); // nextHeader + headerLen + size_t totalSize = sizeof(uint16_t); // nextHeader + headerLen - for (std::vector::const_iterator iter = options.begin(); iter != options.end(); iter++) - { - IPv6Option option = iter->build(); - totalSize += option.getTotalSize(); - option.purgeRecordData(); - } + for (std::vector::const_iterator iter = options.begin(); + iter != options.end(); iter++) { + IPv6Option option = iter->build(); + totalSize += option.getTotalSize(); + option.purgeRecordData(); + } - while (totalSize % 8 != 0) - totalSize++; + while (totalSize % 8 != 0) + totalSize++; - initShadowPtr(totalSize); - memset(getDataPtr(), 0, totalSize); + initShadowPtr(totalSize); + memset(getDataPtr(), 0, totalSize); - getBaseHeader()->headerLen = ((totalSize / 8) - 1); + getBaseHeader()->headerLen = ((totalSize / 8) - 1); - size_t offset = sizeof(uint16_t); + size_t offset = sizeof(uint16_t); - for (std::vector::const_iterator iter = options.begin(); iter != options.end(); iter++) - { - IPv6Option option = iter->build(); - memcpy(getDataPtr() + offset, option.getRecordBasePtr(), option.getTotalSize()); - offset += option.getTotalSize(); - option.purgeRecordData(); - } + for (std::vector::const_iterator iter = options.begin(); + iter != options.end(); iter++) { + IPv6Option option = iter->build(); + memcpy(getDataPtr() + offset, option.getRecordBasePtr(), + option.getTotalSize()); + offset += option.getTotalSize(); + option.purgeRecordData(); + } } -IPv6TLVOptionHeader::IPv6TLVOptionHeader(IDataContainer* dataContainer, size_t offset) : IPv6Extension(dataContainer, offset) -{ -} +IPv6TLVOptionHeader::IPv6TLVOptionHeader(IDataContainer* dataContainer, + size_t offset) + : IPv6Extension(dataContainer, offset) {} // ================= // IPv6RoutingHeader // ================= -IPv6RoutingHeader::IPv6RoutingHeader(uint8_t routingType, uint8_t segmentsLeft, const uint8_t* additionalRoutingData, size_t additionalRoutingDataLen) -{ - size_t totalSize = sizeof(ipv6_routing_header) + additionalRoutingDataLen; - while (totalSize % 8 != 0) - totalSize++; +IPv6RoutingHeader::IPv6RoutingHeader(uint8_t routingType, uint8_t segmentsLeft, + const uint8_t* additionalRoutingData, + size_t additionalRoutingDataLen) { + size_t totalSize = sizeof(ipv6_routing_header) + additionalRoutingDataLen; + while (totalSize % 8 != 0) + totalSize++; - initShadowPtr(totalSize); - memset(getDataPtr(), 0, totalSize); + initShadowPtr(totalSize); + memset(getDataPtr(), 0, totalSize); - m_ExtType = IPv6Routing; + m_ExtType = IPv6Routing; - ipv6_routing_header* routingHeader = getRoutingHeader(); - routingHeader->nextHeader = 0; - routingHeader->headerLen = ((totalSize / 8) - 1); - routingHeader->routingType = routingType; - routingHeader->segmentsLeft = segmentsLeft; + ipv6_routing_header* routingHeader = getRoutingHeader(); + routingHeader->nextHeader = 0; + routingHeader->headerLen = ((totalSize / 8) - 1); + routingHeader->routingType = routingType; + routingHeader->segmentsLeft = segmentsLeft; - if (additionalRoutingDataLen > 0 && additionalRoutingData != nullptr) - { - uint8_t* additionalDataPtr = getDataPtr() + sizeof(ipv6_routing_header); - memcpy(additionalDataPtr, additionalRoutingData, additionalRoutingDataLen); - } + if (additionalRoutingDataLen > 0 && additionalRoutingData != nullptr) { + uint8_t* additionalDataPtr = getDataPtr() + sizeof(ipv6_routing_header); + memcpy(additionalDataPtr, additionalRoutingData, additionalRoutingDataLen); + } } -uint8_t* IPv6RoutingHeader::getRoutingAdditionalData() const -{ - if (getExtensionLen() > sizeof(ipv6_routing_header)) - return getDataPtr() + sizeof(ipv6_routing_header); +uint8_t* IPv6RoutingHeader::getRoutingAdditionalData() const { + if (getExtensionLen() > sizeof(ipv6_routing_header)) + return getDataPtr() + sizeof(ipv6_routing_header); - return nullptr; + return nullptr; } -size_t IPv6RoutingHeader::getRoutingAdditionalDataLength() const -{ - int result = getExtensionLen() - sizeof(ipv6_routing_header); - if (result < 0) - return (size_t)0; +size_t IPv6RoutingHeader::getRoutingAdditionalDataLength() const { + int result = getExtensionLen() - sizeof(ipv6_routing_header); + if (result < 0) + return (size_t)0; - return (size_t)result; + return (size_t)result; } -IPv6Address IPv6RoutingHeader::getRoutingAdditionalDataAsIPv6Address(size_t offset) const -{ +IPv6Address +IPv6RoutingHeader::getRoutingAdditionalDataAsIPv6Address(size_t offset) const { - size_t routingAddDataLen = getRoutingAdditionalDataLength(); - if (routingAddDataLen - offset >= 16) - return IPv6Address(getRoutingAdditionalData() + offset); + size_t routingAddDataLen = getRoutingAdditionalDataLength(); + if (routingAddDataLen - offset >= 16) + return IPv6Address(getRoutingAdditionalData() + offset); - return IPv6Address(); + return IPv6Address(); } - // ======================== // IPv6AuthenticationHeader // ======================== -IPv6AuthenticationHeader::IPv6AuthenticationHeader(uint32_t securityParametersIndex, uint32_t sequenceNumber, const uint8_t* integrityCheckValue, size_t integrityCheckValueLen) -{ - size_t totalSize = sizeof(ipv6_authentication_header) + integrityCheckValueLen; - while (totalSize % 8 != 0) - totalSize++; - - initShadowPtr(totalSize); - memset(getDataPtr(), 0, totalSize); - - m_ExtType = IPv6AuthenticationHdr; - - ipv6_authentication_header* authHeader = getAuthHeader(); - authHeader->nextHeader = 0; - authHeader->headerLen = ((totalSize / 4) - 2); - authHeader->securityParametersIndex = htobe32(securityParametersIndex); - authHeader->sequenceNumber = htobe32(sequenceNumber); - - if (integrityCheckValueLen > 0 && integrityCheckValue != nullptr) - { - uint8_t* icvPtr = getDataPtr() + sizeof(ipv6_authentication_header); - memcpy(icvPtr, integrityCheckValue, integrityCheckValueLen); - } +IPv6AuthenticationHeader::IPv6AuthenticationHeader( + uint32_t securityParametersIndex, uint32_t sequenceNumber, + const uint8_t* integrityCheckValue, size_t integrityCheckValueLen) { + size_t totalSize = + sizeof(ipv6_authentication_header) + integrityCheckValueLen; + while (totalSize % 8 != 0) + totalSize++; + + initShadowPtr(totalSize); + memset(getDataPtr(), 0, totalSize); + + m_ExtType = IPv6AuthenticationHdr; + + ipv6_authentication_header* authHeader = getAuthHeader(); + authHeader->nextHeader = 0; + authHeader->headerLen = ((totalSize / 4) - 2); + authHeader->securityParametersIndex = htobe32(securityParametersIndex); + authHeader->sequenceNumber = htobe32(sequenceNumber); + + if (integrityCheckValueLen > 0 && integrityCheckValue != nullptr) { + uint8_t* icvPtr = getDataPtr() + sizeof(ipv6_authentication_header); + memcpy(icvPtr, integrityCheckValue, integrityCheckValueLen); + } } -uint8_t* IPv6AuthenticationHeader::getIntegrityCheckValue() const -{ - if (getExtensionLen() > sizeof(ipv6_authentication_header)) - return getDataPtr() + sizeof(ipv6_authentication_header); +uint8_t* IPv6AuthenticationHeader::getIntegrityCheckValue() const { + if (getExtensionLen() > sizeof(ipv6_authentication_header)) + return getDataPtr() + sizeof(ipv6_authentication_header); - return nullptr; + return nullptr; } -size_t IPv6AuthenticationHeader::getIntegrityCheckValueLength() const -{ - int result = getExtensionLen() - sizeof(ipv6_authentication_header); - if (result < 0) - return (size_t)0; +size_t IPv6AuthenticationHeader::getIntegrityCheckValueLength() const { + int result = getExtensionLen() - sizeof(ipv6_authentication_header); + if (result < 0) + return (size_t)0; - return (size_t)result; + return (size_t)result; } -} +} // namespace pcpp diff --git a/Packet++/src/IPv6Layer.cpp b/Packet++/src/IPv6Layer.cpp index 72d97657a8..3f17d1afd2 100644 --- a/Packet++/src/IPv6Layer.cpp +++ b/Packet++/src/IPv6Layer.cpp @@ -1,367 +1,334 @@ #define LOG_MODULE PacketLogModuleIPv6Layer #include "IPv6Layer.h" -#include "IPv4Layer.h" -#include "PayloadLayer.h" -#include "UdpLayer.h" -#include "TcpLayer.h" +#include "EndianPortable.h" #include "GreLayer.h" #include "IPSecLayer.h" +#include "IPv4Layer.h" #include "IcmpV6Layer.h" -#include "VrrpLayer.h" #include "Packet.h" +#include "PayloadLayer.h" +#include "TcpLayer.h" +#include "UdpLayer.h" +#include "VrrpLayer.h" #include -#include "EndianPortable.h" -namespace pcpp -{ - -void IPv6Layer::initLayer() -{ - m_DataLen = sizeof(ip6_hdr); - m_Data = new uint8_t[m_DataLen]; - m_Protocol = IPv6; - m_FirstExtension = nullptr; - m_LastExtension = nullptr; - m_ExtensionsLen = 0; - memset(m_Data, 0, sizeof(ip6_hdr)); +namespace pcpp { + +void IPv6Layer::initLayer() { + m_DataLen = sizeof(ip6_hdr); + m_Data = new uint8_t[m_DataLen]; + m_Protocol = IPv6; + m_FirstExtension = nullptr; + m_LastExtension = nullptr; + m_ExtensionsLen = 0; + memset(m_Data, 0, sizeof(ip6_hdr)); } -IPv6Layer::IPv6Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) -{ - m_Protocol = IPv6; - m_FirstExtension = nullptr; - m_LastExtension = nullptr; - m_ExtensionsLen = 0; +IPv6Layer::IPv6Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet) + : Layer(data, dataLen, prevLayer, packet) { + m_Protocol = IPv6; + m_FirstExtension = nullptr; + m_LastExtension = nullptr; + m_ExtensionsLen = 0; - parseExtensions(); + parseExtensions(); - size_t totalLen = be16toh(getIPv6Header()->payloadLength) + getHeaderLen(); - if (totalLen < m_DataLen) - m_DataLen = totalLen; + size_t totalLen = be16toh(getIPv6Header()->payloadLength) + getHeaderLen(); + if (totalLen < m_DataLen) + m_DataLen = totalLen; } -IPv6Layer::IPv6Layer() -{ - initLayer(); -} +IPv6Layer::IPv6Layer() { initLayer(); } -IPv6Layer::IPv6Layer(const IPv6Address& srcIP, const IPv6Address& dstIP) -{ - initLayer(); - ip6_hdr* ipHdr = getIPv6Header(); - srcIP.copyTo(ipHdr->ipSrc); - dstIP.copyTo(ipHdr->ipDst); +IPv6Layer::IPv6Layer(const IPv6Address& srcIP, const IPv6Address& dstIP) { + initLayer(); + ip6_hdr* ipHdr = getIPv6Header(); + srcIP.copyTo(ipHdr->ipSrc); + dstIP.copyTo(ipHdr->ipDst); } -IPv6Layer::IPv6Layer(const IPv6Layer& other) : Layer(other) -{ - m_FirstExtension = nullptr; - m_LastExtension = nullptr; - m_ExtensionsLen = 0; - parseExtensions(); +IPv6Layer::IPv6Layer(const IPv6Layer& other) : Layer(other) { + m_FirstExtension = nullptr; + m_LastExtension = nullptr; + m_ExtensionsLen = 0; + parseExtensions(); } -IPv6Layer::~IPv6Layer() -{ - deleteExtensions(); -} +IPv6Layer::~IPv6Layer() { deleteExtensions(); } -IPv6Layer& IPv6Layer::operator=(const IPv6Layer& other) -{ - Layer::operator=(other); +IPv6Layer& IPv6Layer::operator=(const IPv6Layer& other) { + Layer::operator=(other); - deleteExtensions(); + deleteExtensions(); - parseExtensions(); + parseExtensions(); - return *this; + return *this; } -void IPv6Layer::parseExtensions() -{ - uint8_t nextHdr = getIPv6Header()->nextHeader; - IPv6Extension* curExt = nullptr; - - size_t offset = sizeof(ip6_hdr); - - while (offset <= m_DataLen - 2*sizeof(uint8_t)) // 2*sizeof(uint8_t) is the min len for IPv6 extensions - { - IPv6Extension* newExt = nullptr; - - switch (nextHdr) - { - case PACKETPP_IPPROTO_FRAGMENT: - { - newExt = new IPv6FragmentationHeader(this, offset); - break; - } - case PACKETPP_IPPROTO_HOPOPTS: - { - newExt = new IPv6HopByHopHeader(this, offset); - break; - } - case PACKETPP_IPPROTO_DSTOPTS: - { - newExt = new IPv6DestinationHeader(this, offset); - break; - } - case PACKETPP_IPPROTO_ROUTING: - { - newExt = new IPv6RoutingHeader(this, offset); - break; - } - case PACKETPP_IPPROTO_AH: - { - newExt = new IPv6AuthenticationHeader(this, offset); - break; - } - default: - { - break; - } - } - - if (newExt == nullptr) - break; - - if (m_FirstExtension == nullptr) - { - m_FirstExtension = newExt; - curExt = m_FirstExtension; - } - else - { - curExt->setNextHeader(newExt); - curExt = curExt->getNextHeader(); - } - - offset += newExt->getExtensionLen(); - nextHdr = newExt->getBaseHeader()->nextHeader; - m_ExtensionsLen += newExt->getExtensionLen(); - } - - m_LastExtension = curExt; +void IPv6Layer::parseExtensions() { + uint8_t nextHdr = getIPv6Header()->nextHeader; + IPv6Extension* curExt = nullptr; + + size_t offset = sizeof(ip6_hdr); + + while (offset <= + m_DataLen - 2 * sizeof(uint8_t)) // 2*sizeof(uint8_t) is the min len + // for IPv6 extensions + { + IPv6Extension* newExt = nullptr; + + switch (nextHdr) { + case PACKETPP_IPPROTO_FRAGMENT: { + newExt = new IPv6FragmentationHeader(this, offset); + break; + } + case PACKETPP_IPPROTO_HOPOPTS: { + newExt = new IPv6HopByHopHeader(this, offset); + break; + } + case PACKETPP_IPPROTO_DSTOPTS: { + newExt = new IPv6DestinationHeader(this, offset); + break; + } + case PACKETPP_IPPROTO_ROUTING: { + newExt = new IPv6RoutingHeader(this, offset); + break; + } + case PACKETPP_IPPROTO_AH: { + newExt = new IPv6AuthenticationHeader(this, offset); + break; + } + default: { + break; + } + } + + if (newExt == nullptr) + break; + + if (m_FirstExtension == nullptr) { + m_FirstExtension = newExt; + curExt = m_FirstExtension; + } else { + curExt->setNextHeader(newExt); + curExt = curExt->getNextHeader(); + } + + offset += newExt->getExtensionLen(); + nextHdr = newExt->getBaseHeader()->nextHeader; + m_ExtensionsLen += newExt->getExtensionLen(); + } + + m_LastExtension = curExt; } -void IPv6Layer::deleteExtensions() -{ - IPv6Extension* curExt = m_FirstExtension; - while (curExt != nullptr) - { - IPv6Extension* tmpExt = curExt->getNextHeader(); - delete curExt; - curExt = tmpExt; - } - - m_FirstExtension = nullptr; - m_LastExtension = nullptr; - m_ExtensionsLen = 0; - +void IPv6Layer::deleteExtensions() { + IPv6Extension* curExt = m_FirstExtension; + while (curExt != nullptr) { + IPv6Extension* tmpExt = curExt->getNextHeader(); + delete curExt; + curExt = tmpExt; + } + + m_FirstExtension = nullptr; + m_LastExtension = nullptr; + m_ExtensionsLen = 0; } -size_t IPv6Layer::getExtensionCount() const -{ - size_t extensionCount = 0; +size_t IPv6Layer::getExtensionCount() const { + size_t extensionCount = 0; - IPv6Extension* curExt = m_FirstExtension; + IPv6Extension* curExt = m_FirstExtension; - while (curExt != nullptr) - { - extensionCount++; - curExt = curExt->getNextHeader(); - } + while (curExt != nullptr) { + extensionCount++; + curExt = curExt->getNextHeader(); + } - return extensionCount; + return extensionCount; } -void IPv6Layer::removeAllExtensions() -{ - if (m_LastExtension != nullptr) - getIPv6Header()->nextHeader = m_LastExtension->getBaseHeader()->nextHeader; +void IPv6Layer::removeAllExtensions() { + if (m_LastExtension != nullptr) + getIPv6Header()->nextHeader = m_LastExtension->getBaseHeader()->nextHeader; - shortenLayer((int)sizeof(ip6_hdr), m_ExtensionsLen); + shortenLayer((int)sizeof(ip6_hdr), m_ExtensionsLen); - deleteExtensions(); + deleteExtensions(); } -bool IPv6Layer::isFragment() const -{ - IPv6FragmentationHeader* fragHdr = getExtensionOfType(); - return (fragHdr != nullptr); +bool IPv6Layer::isFragment() const { + IPv6FragmentationHeader* fragHdr = + getExtensionOfType(); + return (fragHdr != nullptr); } -void IPv6Layer::parseNextLayer() -{ - size_t headerLen = getHeaderLen(); - - if (m_DataLen <= headerLen) - return; - - uint8_t* payload = m_Data + headerLen; - size_t payloadLen = m_DataLen - headerLen; - - uint8_t nextHdr; - if (m_LastExtension != nullptr) - { - if (m_LastExtension->getExtensionType() == IPv6Extension::IPv6Fragmentation) - { - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - return; - } - - nextHdr = m_LastExtension->getBaseHeader()->nextHeader; - } - else - { - nextHdr = getIPv6Header()->nextHeader; - } - - switch (nextHdr) - { - case PACKETPP_IPPROTO_UDP: - m_NextLayer = new UdpLayer(payload, payloadLen, this, m_Packet); - break; - case PACKETPP_IPPROTO_TCP: - m_NextLayer = TcpLayer::isDataValid(payload, payloadLen) - ? static_cast(new TcpLayer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case PACKETPP_IPPROTO_IPIP: - { - uint8_t ipVersion = *payload >> 4; - if (ipVersion == 4 && IPv4Layer::isDataValid(payload, payloadLen)) - m_NextLayer = new IPv4Layer(payload, payloadLen, this, m_Packet); - else if (ipVersion == 6 && IPv6Layer::isDataValid(payload, payloadLen)) - m_NextLayer = new IPv6Layer(payload, payloadLen, this, m_Packet); - else - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - break; - } - case PACKETPP_IPPROTO_GRE: - { - ProtocolType greVer = GreLayer::getGREVersion(payload, payloadLen); - if (greVer == GREv0 && GREv0Layer::isDataValid(payload, payloadLen)) - m_NextLayer = new GREv0Layer(payload, payloadLen, this, m_Packet); - else if (greVer == GREv1 && GREv1Layer::isDataValid(payload, payloadLen)) - m_NextLayer = new GREv1Layer(payload, payloadLen, this, m_Packet); - else - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - break; - } - case PACKETPP_IPPROTO_AH: - m_NextLayer = AuthenticationHeaderLayer::isDataValid(payload, payloadLen) - ? static_cast(new AuthenticationHeaderLayer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case PACKETPP_IPPROTO_ESP: - m_NextLayer = ESPLayer::isDataValid(payload, payloadLen) - ? static_cast(new ESPLayer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case PACKETPP_IPPROTO_ICMPV6: - { - m_NextLayer = IcmpV6Layer::parseIcmpV6Layer(payload, payloadLen, this, m_Packet); - break; - } - case PACKETPP_IPPROTO_VRRP: - { - auto vrrpVer = VrrpLayer::getVersionFromData(payload, payloadLen); - if (vrrpVer == VRRPv3) - m_NextLayer = new VrrpV3Layer(payload, payloadLen, this, m_Packet, IPAddress::IPv6AddressType); - else - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - break; - } - default: - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - return; - } +void IPv6Layer::parseNextLayer() { + size_t headerLen = getHeaderLen(); + + if (m_DataLen <= headerLen) + return; + + uint8_t* payload = m_Data + headerLen; + size_t payloadLen = m_DataLen - headerLen; + + uint8_t nextHdr; + if (m_LastExtension != nullptr) { + if (m_LastExtension->getExtensionType() == + IPv6Extension::IPv6Fragmentation) { + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + return; + } + + nextHdr = m_LastExtension->getBaseHeader()->nextHeader; + } else { + nextHdr = getIPv6Header()->nextHeader; + } + + switch (nextHdr) { + case PACKETPP_IPPROTO_UDP: + m_NextLayer = new UdpLayer(payload, payloadLen, this, m_Packet); + break; + case PACKETPP_IPPROTO_TCP: + m_NextLayer = TcpLayer::isDataValid(payload, payloadLen) + ? static_cast( + new TcpLayer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer( + payload, payloadLen, this, m_Packet)); + break; + case PACKETPP_IPPROTO_IPIP: { + uint8_t ipVersion = *payload >> 4; + if (ipVersion == 4 && IPv4Layer::isDataValid(payload, payloadLen)) + m_NextLayer = new IPv4Layer(payload, payloadLen, this, m_Packet); + else if (ipVersion == 6 && IPv6Layer::isDataValid(payload, payloadLen)) + m_NextLayer = new IPv6Layer(payload, payloadLen, this, m_Packet); + else + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + break; + } + case PACKETPP_IPPROTO_GRE: { + ProtocolType greVer = GreLayer::getGREVersion(payload, payloadLen); + if (greVer == GREv0 && GREv0Layer::isDataValid(payload, payloadLen)) + m_NextLayer = new GREv0Layer(payload, payloadLen, this, m_Packet); + else if (greVer == GREv1 && GREv1Layer::isDataValid(payload, payloadLen)) + m_NextLayer = new GREv1Layer(payload, payloadLen, this, m_Packet); + else + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + break; + } + case PACKETPP_IPPROTO_AH: + m_NextLayer = AuthenticationHeaderLayer::isDataValid(payload, payloadLen) + ? static_cast(new AuthenticationHeaderLayer( + payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer( + payload, payloadLen, this, m_Packet)); + break; + case PACKETPP_IPPROTO_ESP: + m_NextLayer = ESPLayer::isDataValid(payload, payloadLen) + ? static_cast( + new ESPLayer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer( + payload, payloadLen, this, m_Packet)); + break; + case PACKETPP_IPPROTO_ICMPV6: { + m_NextLayer = + IcmpV6Layer::parseIcmpV6Layer(payload, payloadLen, this, m_Packet); + break; + } + case PACKETPP_IPPROTO_VRRP: { + auto vrrpVer = VrrpLayer::getVersionFromData(payload, payloadLen); + if (vrrpVer == VRRPv3) + m_NextLayer = new VrrpV3Layer(payload, payloadLen, this, m_Packet, + IPAddress::IPv6AddressType); + else + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + break; + } + default: + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + return; + } } -void IPv6Layer::computeCalculateFields() -{ - ip6_hdr* ipHdr = getIPv6Header(); - ipHdr->payloadLength = htobe16(m_DataLen - sizeof(ip6_hdr)); - ipHdr->ipVersion = (6 & 0x0f); - - if (m_NextLayer != nullptr) - { - uint8_t nextHeader = 0; - switch (m_NextLayer->getProtocol()) - { - case TCP: - nextHeader = PACKETPP_IPPROTO_TCP; - break; - case UDP: - nextHeader = PACKETPP_IPPROTO_UDP; - break; - case ICMP: - nextHeader = PACKETPP_IPPROTO_ICMP; - break; - case ICMPv6: - nextHeader = PACKETPP_IPPROTO_ICMPV6; - break; - case GREv0: - case GREv1: - nextHeader = PACKETPP_IPPROTO_GRE; - break; - case VRRPv3: - nextHeader = PACKETPP_IPPROTO_VRRP; - break; - default: - break; - } - - if (nextHeader != 0) - { - if (m_LastExtension != nullptr) - m_LastExtension->getBaseHeader()->nextHeader = nextHeader; - else - ipHdr->nextHeader = nextHeader; - } - } +void IPv6Layer::computeCalculateFields() { + ip6_hdr* ipHdr = getIPv6Header(); + ipHdr->payloadLength = htobe16(m_DataLen - sizeof(ip6_hdr)); + ipHdr->ipVersion = (6 & 0x0f); + + if (m_NextLayer != nullptr) { + uint8_t nextHeader = 0; + switch (m_NextLayer->getProtocol()) { + case TCP: + nextHeader = PACKETPP_IPPROTO_TCP; + break; + case UDP: + nextHeader = PACKETPP_IPPROTO_UDP; + break; + case ICMP: + nextHeader = PACKETPP_IPPROTO_ICMP; + break; + case ICMPv6: + nextHeader = PACKETPP_IPPROTO_ICMPV6; + break; + case GREv0: + case GREv1: + nextHeader = PACKETPP_IPPROTO_GRE; + break; + case VRRPv3: + nextHeader = PACKETPP_IPPROTO_VRRP; + break; + default: + break; + } + + if (nextHeader != 0) { + if (m_LastExtension != nullptr) + m_LastExtension->getBaseHeader()->nextHeader = nextHeader; + else + ipHdr->nextHeader = nextHeader; + } + } } -std::string IPv6Layer::toString() const -{ - std::string result = "IPv6 Layer, Src: " + getSrcIPv6Address().toString() + ", Dst: " + getDstIPv6Address().toString(); - if (m_ExtensionsLen > 0) - { - result += ", Options=["; - IPv6Extension* curExt = m_FirstExtension; - while (curExt != nullptr) - { - switch (curExt->getExtensionType()) - { - case IPv6Extension::IPv6Fragmentation: - result += "Fragment,"; - break; - case IPv6Extension::IPv6HopByHop: - result += "Hop-By-Hop,"; - break; - case IPv6Extension::IPv6Destination: - result += "Destination,"; - break; - case IPv6Extension::IPv6Routing: - result += "Routing,"; - break; - case IPv6Extension::IPv6AuthenticationHdr: - result += "Authentication,"; - break; - default: - result += "Unknown,"; - break; - } - - curExt = curExt->getNextHeader(); - } - - // replace the last ',' - result[result.size() - 1] = ']'; - } - - return result; +std::string IPv6Layer::toString() const { + std::string result = "IPv6 Layer, Src: " + getSrcIPv6Address().toString() + + ", Dst: " + getDstIPv6Address().toString(); + if (m_ExtensionsLen > 0) { + result += ", Options=["; + IPv6Extension* curExt = m_FirstExtension; + while (curExt != nullptr) { + switch (curExt->getExtensionType()) { + case IPv6Extension::IPv6Fragmentation: + result += "Fragment,"; + break; + case IPv6Extension::IPv6HopByHop: + result += "Hop-By-Hop,"; + break; + case IPv6Extension::IPv6Destination: + result += "Destination,"; + break; + case IPv6Extension::IPv6Routing: + result += "Routing,"; + break; + case IPv6Extension::IPv6AuthenticationHdr: + result += "Authentication,"; + break; + default: + result += "Unknown,"; + break; + } + + curExt = curExt->getNextHeader(); + } + + // replace the last ',' + result[result.size() - 1] = ']'; + } + + return result; } -}// namespace pcpp +} // namespace pcpp diff --git a/Packet++/src/IcmpLayer.cpp b/Packet++/src/IcmpLayer.cpp index 061c9ed8e2..05779e11c8 100644 --- a/Packet++/src/IcmpLayer.cpp +++ b/Packet++/src/IcmpLayer.cpp @@ -1,699 +1,707 @@ #define LOG_MODULE PacketLogModuleIcmpLayer #include "IcmpLayer.h" -#include "PayloadLayer.h" +#include "EndianPortable.h" +#include "Logger.h" #include "Packet.h" #include "PacketUtils.h" -#include "Logger.h" +#include "PayloadLayer.h" #include #include -#include "EndianPortable.h" -namespace pcpp -{ +namespace pcpp { -icmp_router_address_structure* icmp_router_advertisement::getRouterAddress(int index) const -{ - if (index < 0 || index >= header->advertisementCount) - return nullptr; +icmp_router_address_structure* +icmp_router_advertisement::getRouterAddress(int index) const { + if (index < 0 || index >= header->advertisementCount) + return nullptr; - uint8_t* headerAsByteArr = (uint8_t*)header; - return (icmp_router_address_structure*)(headerAsByteArr + sizeof(icmp_router_advertisement_hdr) + index * sizeof(icmp_router_address_structure)); + uint8_t* headerAsByteArr = (uint8_t*)header; + return (icmp_router_address_structure*)(headerAsByteArr + sizeof(icmp_router_advertisement_hdr) + + index * sizeof(icmp_router_address_structure)); } -void icmp_router_address_structure::setRouterAddress(IPv4Address addr, uint32_t preference) -{ - routerAddress = addr.toInt(); - preferenceLevel = htobe32(preference); +void icmp_router_address_structure::setRouterAddress(IPv4Address addr, + uint32_t preference) { + routerAddress = addr.toInt(); + preferenceLevel = htobe32(preference); } -IcmpLayer::IcmpLayer() : Layer() -{ - m_DataLen = sizeof(icmphdr); - m_Data = new uint8_t[m_DataLen]; - memset(m_Data, 0, m_DataLen); - m_Protocol = ICMP; +IcmpLayer::IcmpLayer() : Layer() { + m_DataLen = sizeof(icmphdr); + m_Data = new uint8_t[m_DataLen]; + memset(m_Data, 0, m_DataLen); + m_Protocol = ICMP; } -IcmpMessageType IcmpLayer::getMessageType() const -{ - uint8_t type = getIcmpHeader()->type; - if (type > 18) - return ICMP_UNSUPPORTED; +IcmpMessageType IcmpLayer::getMessageType() const { + uint8_t type = getIcmpHeader()->type; + if (type > 18) + return ICMP_UNSUPPORTED; - return (IcmpMessageType)type; + return (IcmpMessageType)type; } -bool IcmpLayer::cleanIcmpLayer() -{ - // remove all layers after +bool IcmpLayer::cleanIcmpLayer() { + // remove all layers after - if (m_Packet != nullptr) - { - bool res = m_Packet->removeAllLayersAfter(this); - if (!res) - return false; - } + if (m_Packet != nullptr) { + bool res = m_Packet->removeAllLayersAfter(this); + if (!res) + return false; + } - // shorten layer to size of icmphdr + // shorten layer to size of icmphdr - size_t headerLen = this->getHeaderLen(); - if (headerLen > sizeof(icmphdr)) - { - if (!this->shortenLayer(sizeof(icmphdr), headerLen - sizeof(icmphdr))) - return false; - } + size_t headerLen = this->getHeaderLen(); + if (headerLen > sizeof(icmphdr)) { + if (!this->shortenLayer(sizeof(icmphdr), headerLen - sizeof(icmphdr))) + return false; + } - return true; + return true; } -bool IcmpLayer::setEchoData(IcmpMessageType echoType, uint16_t id, uint16_t sequence, uint64_t timestamp, const uint8_t* data, size_t dataLen) -{ - if (!cleanIcmpLayer()) - return false; +bool IcmpLayer::setEchoData(IcmpMessageType echoType, uint16_t id, + uint16_t sequence, uint64_t timestamp, + const uint8_t* data, size_t dataLen) { + if (!cleanIcmpLayer()) + return false; - if (!this->extendLayer(m_DataLen, sizeof(icmp_echo_hdr) - sizeof(icmphdr) + dataLen)) - return false; + if (!this->extendLayer(m_DataLen, + sizeof(icmp_echo_hdr) - sizeof(icmphdr) + dataLen)) + return false; - getIcmpHeader()->type = (uint8_t)echoType; + getIcmpHeader()->type = (uint8_t)echoType; - icmp_echo_request* header = nullptr; - if (echoType == ICMP_ECHO_REQUEST) - header = getEchoRequestData(); - else if (echoType == ICMP_ECHO_REPLY) - header = (icmp_echo_request*)getEchoReplyData(); - else - return false; + icmp_echo_request* header = nullptr; + if (echoType == ICMP_ECHO_REQUEST) + header = getEchoRequestData(); + else if (echoType == ICMP_ECHO_REPLY) + header = (icmp_echo_request*)getEchoReplyData(); + else + return false; - header->header->code = 0; - header->header->checksum = 0; - header->header->id = htobe16(id); - header->header->sequence = htobe16(sequence); - header->header->timestamp = timestamp; - if (data != nullptr && dataLen > 0) - memcpy(header->data, data, dataLen); + header->header->code = 0; + header->header->checksum = 0; + header->header->id = htobe16(id); + header->header->sequence = htobe16(sequence); + header->header->timestamp = timestamp; + if (data != nullptr && dataLen > 0) + memcpy(header->data, data, dataLen); - return true; + return true; } -bool IcmpLayer::setIpAndL4Layers(IPv4Layer* ipLayer, Layer* l4Layer) -{ - if (m_Packet == nullptr) - { - PCPP_LOG_ERROR("Cannot set ICMP data that involves IP and L4 layers on a layer not attached to a packet. " - "Please add the ICMP layer to a packet and try again"); - return false; - } +bool IcmpLayer::setIpAndL4Layers(IPv4Layer* ipLayer, Layer* l4Layer) { + if (m_Packet == nullptr) { + PCPP_LOG_ERROR("Cannot set ICMP data that involves IP and L4 layers on a " + "layer not attached to a packet. " + "Please add the ICMP layer to a packet and try again"); + return false; + } - if (ipLayer != nullptr && !m_Packet->addLayer(ipLayer)) - { - PCPP_LOG_ERROR("Couldn't add IP layer to ICMP packet"); - return false; - } + if (ipLayer != nullptr && !m_Packet->addLayer(ipLayer)) { + PCPP_LOG_ERROR("Couldn't add IP layer to ICMP packet"); + return false; + } - if (l4Layer != nullptr && !m_Packet->addLayer(l4Layer)) - { - PCPP_LOG_ERROR("Couldn't add L4 layer to ICMP packet"); - return false; - } + if (l4Layer != nullptr && !m_Packet->addLayer(l4Layer)) { + PCPP_LOG_ERROR("Couldn't add L4 layer to ICMP packet"); + return false; + } - return true; + return true; } -icmp_echo_request* IcmpLayer::getEchoRequestData() -{ - if (!isMessageOfType(ICMP_ECHO_REQUEST)) - return nullptr; +icmp_echo_request* IcmpLayer::getEchoRequestData() { + if (!isMessageOfType(ICMP_ECHO_REQUEST)) + return nullptr; - m_EchoData.header = (icmp_echo_hdr*)m_Data; - m_EchoData.data = (uint8_t*)(m_Data + sizeof(icmp_echo_hdr)); - m_EchoData.dataLength = m_DataLen - sizeof(icmp_echo_hdr); + m_EchoData.header = (icmp_echo_hdr*)m_Data; + m_EchoData.data = (uint8_t*)(m_Data + sizeof(icmp_echo_hdr)); + m_EchoData.dataLength = m_DataLen - sizeof(icmp_echo_hdr); - return &m_EchoData; + return &m_EchoData; } -icmp_echo_request* IcmpLayer::setEchoRequestData(uint16_t id, uint16_t sequence, uint64_t timestamp, const uint8_t* data, size_t dataLen) -{ - if (setEchoData(ICMP_ECHO_REQUEST, id, sequence, timestamp, data, dataLen)) - return getEchoRequestData(); - else - return nullptr; +icmp_echo_request* IcmpLayer::setEchoRequestData(uint16_t id, uint16_t sequence, + uint64_t timestamp, + const uint8_t* data, + size_t dataLen) { + if (setEchoData(ICMP_ECHO_REQUEST, id, sequence, timestamp, data, dataLen)) + return getEchoRequestData(); + else + return nullptr; } -icmp_echo_reply* IcmpLayer::getEchoReplyData() -{ - if (!isMessageOfType(ICMP_ECHO_REPLY)) - return nullptr; +icmp_echo_reply* IcmpLayer::getEchoReplyData() { + if (!isMessageOfType(ICMP_ECHO_REPLY)) + return nullptr; - m_EchoData.header = (icmp_echo_hdr*)m_Data; - m_EchoData.data = (uint8_t*)(m_Data + sizeof(icmp_echo_hdr)); - m_EchoData.dataLength = m_DataLen - sizeof(icmp_echo_hdr); + m_EchoData.header = (icmp_echo_hdr*)m_Data; + m_EchoData.data = (uint8_t*)(m_Data + sizeof(icmp_echo_hdr)); + m_EchoData.dataLength = m_DataLen - sizeof(icmp_echo_hdr); - return &m_EchoData; + return &m_EchoData; } -icmp_echo_reply* IcmpLayer::setEchoReplyData(uint16_t id, uint16_t sequence, uint64_t timestamp, const uint8_t* data, size_t dataLen) -{ - if (setEchoData(ICMP_ECHO_REPLY, id, sequence, timestamp, data, dataLen)) - return getEchoReplyData(); - else - return nullptr; +icmp_echo_reply* IcmpLayer::setEchoReplyData(uint16_t id, uint16_t sequence, + uint64_t timestamp, + const uint8_t* data, + size_t dataLen) { + if (setEchoData(ICMP_ECHO_REPLY, id, sequence, timestamp, data, dataLen)) + return getEchoReplyData(); + else + return nullptr; } +icmp_timestamp_request* IcmpLayer::getTimestampRequestData() { + if (!isMessageOfType(ICMP_TIMESTAMP_REQUEST)) + return nullptr; -icmp_timestamp_request* IcmpLayer::getTimestampRequestData() -{ - if (!isMessageOfType(ICMP_TIMESTAMP_REQUEST)) - return nullptr; - - return (icmp_timestamp_request*)m_Data; + return (icmp_timestamp_request*)m_Data; } -icmp_timestamp_request* IcmpLayer::setTimestampRequestData(uint16_t id, uint16_t sequence, timeval originateTimestamp) -{ - if (!cleanIcmpLayer()) - return nullptr; +icmp_timestamp_request* +IcmpLayer::setTimestampRequestData(uint16_t id, uint16_t sequence, + timeval originateTimestamp) { + if (!cleanIcmpLayer()) + return nullptr; - if (!this->extendLayer(m_DataLen, sizeof(icmp_timestamp_request) - sizeof(icmphdr))) - return nullptr; + if (!this->extendLayer(m_DataLen, + sizeof(icmp_timestamp_request) - sizeof(icmphdr))) + return nullptr; - getIcmpHeader()->type = (uint8_t)ICMP_TIMESTAMP_REQUEST; + getIcmpHeader()->type = (uint8_t)ICMP_TIMESTAMP_REQUEST; - icmp_timestamp_request* header = getTimestampRequestData(); - header->code = 0; - header->id = htobe16(id); - header->sequence = htobe16(sequence); - header->originateTimestamp = htobe32(originateTimestamp.tv_sec*1000 + originateTimestamp.tv_usec/1000); - header->receiveTimestamp = 0; - header->transmitTimestamp = 0; + icmp_timestamp_request* header = getTimestampRequestData(); + header->code = 0; + header->id = htobe16(id); + header->sequence = htobe16(sequence); + header->originateTimestamp = htobe32(originateTimestamp.tv_sec * 1000 + + originateTimestamp.tv_usec / 1000); + header->receiveTimestamp = 0; + header->transmitTimestamp = 0; - return header; + return header; } -icmp_timestamp_reply* IcmpLayer::getTimestampReplyData() -{ - if (!isMessageOfType(ICMP_TIMESTAMP_REPLY)) - return nullptr; +icmp_timestamp_reply* IcmpLayer::getTimestampReplyData() { + if (!isMessageOfType(ICMP_TIMESTAMP_REPLY)) + return nullptr; - return (icmp_timestamp_reply*)m_Data; + return (icmp_timestamp_reply*)m_Data; } -icmp_timestamp_reply* IcmpLayer::setTimestampReplyData(uint16_t id, uint16_t sequence, - timeval originateTimestamp, timeval receiveTimestamp, timeval transmitTimestamp) -{ - if (!cleanIcmpLayer()) - return nullptr; +icmp_timestamp_reply* IcmpLayer::setTimestampReplyData( + uint16_t id, uint16_t sequence, timeval originateTimestamp, + timeval receiveTimestamp, timeval transmitTimestamp) { + if (!cleanIcmpLayer()) + return nullptr; - if (!this->extendLayer(m_DataLen, sizeof(icmp_timestamp_reply) - sizeof(icmphdr))) - return nullptr; + if (!this->extendLayer(m_DataLen, + sizeof(icmp_timestamp_reply) - sizeof(icmphdr))) + return nullptr; - getIcmpHeader()->type = (uint8_t)ICMP_TIMESTAMP_REPLY; + getIcmpHeader()->type = (uint8_t)ICMP_TIMESTAMP_REPLY; - icmp_timestamp_reply* header = getTimestampReplyData(); - header->code = 0; - header->id = htobe16(id); - header->sequence = htobe16(sequence); - header->originateTimestamp = htobe32(originateTimestamp.tv_sec*1000 + originateTimestamp.tv_usec/1000); - header->receiveTimestamp = htobe32(receiveTimestamp.tv_sec*1000 + receiveTimestamp.tv_usec/1000); - header->transmitTimestamp = htobe32(transmitTimestamp.tv_sec*1000 + transmitTimestamp.tv_usec/1000); + icmp_timestamp_reply* header = getTimestampReplyData(); + header->code = 0; + header->id = htobe16(id); + header->sequence = htobe16(sequence); + header->originateTimestamp = htobe32(originateTimestamp.tv_sec * 1000 + + originateTimestamp.tv_usec / 1000); + header->receiveTimestamp = + htobe32(receiveTimestamp.tv_sec * 1000 + receiveTimestamp.tv_usec / 1000); + header->transmitTimestamp = htobe32(transmitTimestamp.tv_sec * 1000 + + transmitTimestamp.tv_usec / 1000); - return header; + return header; } -icmp_destination_unreachable* IcmpLayer::getDestUnreachableData() -{ - if (!isMessageOfType(ICMP_DEST_UNREACHABLE)) - return nullptr; +icmp_destination_unreachable* IcmpLayer::getDestUnreachableData() { + if (!isMessageOfType(ICMP_DEST_UNREACHABLE)) + return nullptr; - return (icmp_destination_unreachable*)m_Data; + return (icmp_destination_unreachable*)m_Data; } -icmp_destination_unreachable* IcmpLayer::setDestUnreachableData(IcmpDestUnreachableCodes code, uint16_t nextHopMTU, IPv4Layer* ipHeader, Layer* l4Header) -{ - if (!cleanIcmpLayer()) - return nullptr; +icmp_destination_unreachable* +IcmpLayer::setDestUnreachableData(IcmpDestUnreachableCodes code, + uint16_t nextHopMTU, IPv4Layer* ipHeader, + Layer* l4Header) { + if (!cleanIcmpLayer()) + return nullptr; - if (!this->extendLayer(m_DataLen, sizeof(icmp_destination_unreachable) - sizeof(icmphdr))) - return nullptr; + if (!this->extendLayer(m_DataLen, sizeof(icmp_destination_unreachable) - + sizeof(icmphdr))) + return nullptr; - getIcmpHeader()->type = (uint8_t)ICMP_DEST_UNREACHABLE; + getIcmpHeader()->type = (uint8_t)ICMP_DEST_UNREACHABLE; - icmp_destination_unreachable* header = getDestUnreachableData(); - header->code = code; - header->nextHopMTU = htobe16(nextHopMTU); - header->unused = 0; + icmp_destination_unreachable* header = getDestUnreachableData(); + header->code = code; + header->nextHopMTU = htobe16(nextHopMTU); + header->unused = 0; - if (!setIpAndL4Layers(ipHeader, l4Header)) - return nullptr; + if (!setIpAndL4Layers(ipHeader, l4Header)) + return nullptr; - return header; + return header; } -icmp_source_quench* IcmpLayer::getSourceQuenchdata() -{ - if (!isMessageOfType(ICMP_SOURCE_QUENCH)) - return nullptr; - - return (icmp_source_quench*)m_Data; +icmp_source_quench* IcmpLayer::getSourceQuenchdata() { + if (!isMessageOfType(ICMP_SOURCE_QUENCH)) + return nullptr; + return (icmp_source_quench*)m_Data; } -icmp_source_quench* IcmpLayer::setSourceQuenchdata(IPv4Layer* ipHeader, Layer* l4Header) -{ - if (!cleanIcmpLayer()) - return nullptr; +icmp_source_quench* IcmpLayer::setSourceQuenchdata(IPv4Layer* ipHeader, + Layer* l4Header) { + if (!cleanIcmpLayer()) + return nullptr; - if (!this->extendLayer(m_DataLen, sizeof(icmp_source_quench) - sizeof(icmphdr))) - return nullptr; + if (!this->extendLayer(m_DataLen, + sizeof(icmp_source_quench) - sizeof(icmphdr))) + return nullptr; - getIcmpHeader()->type = (uint8_t)ICMP_SOURCE_QUENCH; + getIcmpHeader()->type = (uint8_t)ICMP_SOURCE_QUENCH; - icmp_source_quench* header = getSourceQuenchdata(); - header->unused = 0; + icmp_source_quench* header = getSourceQuenchdata(); + header->unused = 0; - if (!setIpAndL4Layers(ipHeader, l4Header)) - return nullptr; + if (!setIpAndL4Layers(ipHeader, l4Header)) + return nullptr; - return header; + return header; } -icmp_redirect* IcmpLayer::getRedirectData() -{ - if (!isMessageOfType(ICMP_REDIRECT)) - return nullptr; +icmp_redirect* IcmpLayer::getRedirectData() { + if (!isMessageOfType(ICMP_REDIRECT)) + return nullptr; - return (icmp_redirect*)m_Data; + return (icmp_redirect*)m_Data; } -icmp_redirect* IcmpLayer::setRedirectData(uint8_t code, IPv4Address gatewayAddress, IPv4Layer* ipHeader, Layer* l4Header) -{ - if (code > 3) - { - PCPP_LOG_ERROR("Unknown code " << (int)code << " for ICMP redirect data"); - return nullptr; - } +icmp_redirect* IcmpLayer::setRedirectData(uint8_t code, + IPv4Address gatewayAddress, + IPv4Layer* ipHeader, + Layer* l4Header) { + if (code > 3) { + PCPP_LOG_ERROR("Unknown code " << (int)code << " for ICMP redirect data"); + return nullptr; + } - if (!cleanIcmpLayer()) - return nullptr; + if (!cleanIcmpLayer()) + return nullptr; - if (!this->extendLayer(m_DataLen, sizeof(icmp_redirect) - sizeof(icmphdr))) - return nullptr; + if (!this->extendLayer(m_DataLen, sizeof(icmp_redirect) - sizeof(icmphdr))) + return nullptr; - getIcmpHeader()->type = (uint8_t)ICMP_REDIRECT; + getIcmpHeader()->type = (uint8_t)ICMP_REDIRECT; - icmp_redirect* header = getRedirectData(); - header->code = code; - header->gatewayAddress = gatewayAddress.toInt(); + icmp_redirect* header = getRedirectData(); + header->code = code; + header->gatewayAddress = gatewayAddress.toInt(); - if (!setIpAndL4Layers(ipHeader, l4Header)) - return nullptr; + if (!setIpAndL4Layers(ipHeader, l4Header)) + return nullptr; - return header; + return header; } -icmp_router_advertisement* IcmpLayer::getRouterAdvertisementData() const -{ - if (!isMessageOfType(ICMP_ROUTER_ADV)) - return nullptr; +icmp_router_advertisement* IcmpLayer::getRouterAdvertisementData() const { + if (!isMessageOfType(ICMP_ROUTER_ADV)) + return nullptr; - m_RouterAdvData.header = (icmp_router_advertisement_hdr*)m_Data; + m_RouterAdvData.header = (icmp_router_advertisement_hdr*)m_Data; - return &m_RouterAdvData; + return &m_RouterAdvData; } -icmp_router_advertisement* IcmpLayer::setRouterAdvertisementData(uint8_t code, uint16_t lifetimeInSeconds, const std::vector& routerAddresses) -{ - if (code != 0 && code != 16) - { - PCPP_LOG_ERROR("Unknown code " << (int)code << " for ICMP router advertisement data (only codes 0 and 16 are legal)"); - return nullptr; - } +icmp_router_advertisement* IcmpLayer::setRouterAdvertisementData( + uint8_t code, uint16_t lifetimeInSeconds, + const std::vector& routerAddresses) { + if (code != 0 && code != 16) { + PCPP_LOG_ERROR("Unknown code " << (int)code + << " for ICMP router advertisement data " + "(only codes 0 and 16 are legal)"); + return nullptr; + } - if (!cleanIcmpLayer()) - return nullptr; + if (!cleanIcmpLayer()) + return nullptr; - if (!this->extendLayer(m_DataLen, sizeof(icmp_router_advertisement_hdr) + (routerAddresses.size()*sizeof(icmp_router_address_structure)) - sizeof(icmphdr))) - return nullptr; + if (!this->extendLayer( + m_DataLen, + sizeof(icmp_router_advertisement_hdr) + + (routerAddresses.size() * sizeof(icmp_router_address_structure)) - + sizeof(icmphdr))) + return nullptr; - getIcmpHeader()->type = (uint8_t)ICMP_ROUTER_ADV; + getIcmpHeader()->type = (uint8_t)ICMP_ROUTER_ADV; - icmp_router_advertisement* header = getRouterAdvertisementData(); - header->header->code = code; - header->header->lifetime = htobe16(lifetimeInSeconds); - header->header->advertisementCount = (uint8_t)routerAddresses.size(); - header->header->addressEntrySize = 2; + icmp_router_advertisement* header = getRouterAdvertisementData(); + header->header->code = code; + header->header->lifetime = htobe16(lifetimeInSeconds); + header->header->advertisementCount = (uint8_t)routerAddresses.size(); + header->header->addressEntrySize = 2; - icmp_router_address_structure* curPos = (icmp_router_address_structure*)((uint8_t*)header->header + sizeof(icmp_router_advertisement_hdr)); - for (std::vector::const_iterator iter = routerAddresses.begin(); iter != routerAddresses.end(); iter++) - { - curPos->routerAddress = iter->routerAddress; - curPos->preferenceLevel = iter->preferenceLevel; - curPos += 1; - } + icmp_router_address_structure* curPos = + (icmp_router_address_structure*)((uint8_t*)header->header + + sizeof(icmp_router_advertisement_hdr)); + for (std::vector::const_iterator iter = + routerAddresses.begin(); + iter != routerAddresses.end(); iter++) { + curPos->routerAddress = iter->routerAddress; + curPos->preferenceLevel = iter->preferenceLevel; + curPos += 1; + } - return header; + return header; } -icmp_router_solicitation* IcmpLayer::getRouterSolicitationData() -{ - if (!isMessageOfType(ICMP_ROUTER_SOL)) - return nullptr; +icmp_router_solicitation* IcmpLayer::getRouterSolicitationData() { + if (!isMessageOfType(ICMP_ROUTER_SOL)) + return nullptr; - return (icmp_router_solicitation*)m_Data; + return (icmp_router_solicitation*)m_Data; } -icmp_router_solicitation* IcmpLayer::setRouterSolicitationData() -{ - if (!cleanIcmpLayer()) - return nullptr; +icmp_router_solicitation* IcmpLayer::setRouterSolicitationData() { + if (!cleanIcmpLayer()) + return nullptr; - getIcmpHeader()->type = (uint8_t)ICMP_ROUTER_SOL; + getIcmpHeader()->type = (uint8_t)ICMP_ROUTER_SOL; - icmp_router_solicitation* header = getRouterSolicitationData(); - header->code = 0; + icmp_router_solicitation* header = getRouterSolicitationData(); + header->code = 0; - return header; + return header; } -icmp_time_exceeded* IcmpLayer::getTimeExceededData() -{ - if (!isMessageOfType(ICMP_TIME_EXCEEDED)) - return nullptr; +icmp_time_exceeded* IcmpLayer::getTimeExceededData() { + if (!isMessageOfType(ICMP_TIME_EXCEEDED)) + return nullptr; - return (icmp_time_exceeded*)m_Data; + return (icmp_time_exceeded*)m_Data; } -icmp_time_exceeded* IcmpLayer::setTimeExceededData(uint8_t code, IPv4Layer* ipHeader, Layer* l4Header) -{ - if (code > 1) - { - PCPP_LOG_ERROR("Unknown code " << (int)code << " for ICMP time exceeded data"); - return nullptr; - } +icmp_time_exceeded* IcmpLayer::setTimeExceededData(uint8_t code, + IPv4Layer* ipHeader, + Layer* l4Header) { + if (code > 1) { + PCPP_LOG_ERROR("Unknown code " << (int)code + << " for ICMP time exceeded data"); + return nullptr; + } - if (!cleanIcmpLayer()) - return nullptr; + if (!cleanIcmpLayer()) + return nullptr; - if (!this->extendLayer(m_DataLen, sizeof(icmp_time_exceeded) - sizeof(icmphdr))) - return nullptr; + if (!this->extendLayer(m_DataLen, + sizeof(icmp_time_exceeded) - sizeof(icmphdr))) + return nullptr; - getIcmpHeader()->type = (uint8_t)ICMP_TIME_EXCEEDED; + getIcmpHeader()->type = (uint8_t)ICMP_TIME_EXCEEDED; - icmp_time_exceeded* header = getTimeExceededData(); - header->code = code; - header->unused = 0; + icmp_time_exceeded* header = getTimeExceededData(); + header->code = code; + header->unused = 0; - if (!setIpAndL4Layers(ipHeader, l4Header)) - return nullptr; + if (!setIpAndL4Layers(ipHeader, l4Header)) + return nullptr; - return header; + return header; } -icmp_param_problem* IcmpLayer::getParamProblemData() -{ - if (!isMessageOfType(ICMP_PARAM_PROBLEM)) - return nullptr; +icmp_param_problem* IcmpLayer::getParamProblemData() { + if (!isMessageOfType(ICMP_PARAM_PROBLEM)) + return nullptr; - return (icmp_param_problem*)m_Data; + return (icmp_param_problem*)m_Data; } -icmp_param_problem* IcmpLayer::setParamProblemData(uint8_t code, uint8_t errorOctetPointer, IPv4Layer* ipHeader, Layer* l4Header) -{ - if (code > 2) - { - PCPP_LOG_ERROR("Unknown code " << (int)code << " for ICMP parameter problem data"); - return nullptr; - } +icmp_param_problem* IcmpLayer::setParamProblemData(uint8_t code, + uint8_t errorOctetPointer, + IPv4Layer* ipHeader, + Layer* l4Header) { + if (code > 2) { + PCPP_LOG_ERROR("Unknown code " << (int)code + << " for ICMP parameter problem data"); + return nullptr; + } - if (!cleanIcmpLayer()) - return nullptr; + if (!cleanIcmpLayer()) + return nullptr; - if (!this->extendLayer(m_DataLen, sizeof(icmp_param_problem) - sizeof(icmphdr))) - return nullptr; + if (!this->extendLayer(m_DataLen, + sizeof(icmp_param_problem) - sizeof(icmphdr))) + return nullptr; - getIcmpHeader()->type = (uint8_t)ICMP_PARAM_PROBLEM; + getIcmpHeader()->type = (uint8_t)ICMP_PARAM_PROBLEM; - icmp_param_problem* header = getParamProblemData(); - header->code = code; - header->unused1 = 0; - header->unused2 = 0; - header->pointer = errorOctetPointer; + icmp_param_problem* header = getParamProblemData(); + header->code = code; + header->unused1 = 0; + header->unused2 = 0; + header->pointer = errorOctetPointer; - if (!setIpAndL4Layers(ipHeader, l4Header)) - return nullptr; + if (!setIpAndL4Layers(ipHeader, l4Header)) + return nullptr; - return header; + return header; } -icmp_address_mask_request* IcmpLayer::getAddressMaskRequestData() -{ - if (!isMessageOfType(ICMP_ADDRESS_MASK_REQUEST)) - return nullptr; +icmp_address_mask_request* IcmpLayer::getAddressMaskRequestData() { + if (!isMessageOfType(ICMP_ADDRESS_MASK_REQUEST)) + return nullptr; - return (icmp_address_mask_request*)m_Data; + return (icmp_address_mask_request*)m_Data; } -icmp_address_mask_request* IcmpLayer::setAddressMaskRequestData(uint16_t id, uint16_t sequence, IPv4Address mask) -{ - if (!cleanIcmpLayer()) - return nullptr; +icmp_address_mask_request* +IcmpLayer::setAddressMaskRequestData(uint16_t id, uint16_t sequence, + IPv4Address mask) { + if (!cleanIcmpLayer()) + return nullptr; - if (!this->extendLayer(m_DataLen, sizeof(icmp_address_mask_request) - sizeof(icmphdr))) - return nullptr; + if (!this->extendLayer(m_DataLen, + sizeof(icmp_address_mask_request) - sizeof(icmphdr))) + return nullptr; - getIcmpHeader()->type = (uint8_t)ICMP_ADDRESS_MASK_REQUEST; + getIcmpHeader()->type = (uint8_t)ICMP_ADDRESS_MASK_REQUEST; - icmp_address_mask_request* header = getAddressMaskRequestData(); - header->code = 0; - header->id = htobe16(id); - header->sequence = htobe16(sequence); - header->addressMask = mask.toInt(); + icmp_address_mask_request* header = getAddressMaskRequestData(); + header->code = 0; + header->id = htobe16(id); + header->sequence = htobe16(sequence); + header->addressMask = mask.toInt(); - return header; + return header; } -icmp_address_mask_reply* IcmpLayer::getAddressMaskReplyData() -{ - if (!isMessageOfType(ICMP_ADDRESS_MASK_REPLY)) - return nullptr; +icmp_address_mask_reply* IcmpLayer::getAddressMaskReplyData() { + if (!isMessageOfType(ICMP_ADDRESS_MASK_REPLY)) + return nullptr; - return (icmp_address_mask_reply*)m_Data; + return (icmp_address_mask_reply*)m_Data; } -icmp_address_mask_reply* IcmpLayer::setAddressMaskReplyData(uint16_t id, uint16_t sequence, IPv4Address mask) -{ - if (!cleanIcmpLayer()) - return nullptr; +icmp_address_mask_reply* IcmpLayer::setAddressMaskReplyData(uint16_t id, + uint16_t sequence, + IPv4Address mask) { + if (!cleanIcmpLayer()) + return nullptr; - if (!this->extendLayer(m_DataLen, sizeof(icmp_address_mask_reply) - sizeof(icmphdr))) - return nullptr; + if (!this->extendLayer(m_DataLen, + sizeof(icmp_address_mask_reply) - sizeof(icmphdr))) + return nullptr; - getIcmpHeader()->type = (uint8_t)ICMP_ADDRESS_MASK_REPLY; + getIcmpHeader()->type = (uint8_t)ICMP_ADDRESS_MASK_REPLY; - icmp_address_mask_reply* header = getAddressMaskReplyData(); - header->code = 0; - header->id = htobe16(id); - header->sequence = htobe16(sequence); - header->addressMask = htobe32(mask.toInt()); + icmp_address_mask_reply* header = getAddressMaskReplyData(); + header->code = 0; + header->id = htobe16(id); + header->sequence = htobe16(sequence); + header->addressMask = htobe32(mask.toInt()); - return header; + return header; } -icmp_info_request* IcmpLayer::getInfoRequestData() -{ - if (!isMessageOfType(ICMP_INFO_REQUEST)) - return nullptr; +icmp_info_request* IcmpLayer::getInfoRequestData() { + if (!isMessageOfType(ICMP_INFO_REQUEST)) + return nullptr; - return (icmp_info_request*)m_Data; + return (icmp_info_request*)m_Data; } -icmp_info_request* IcmpLayer::setInfoRequestData(uint16_t id, uint16_t sequence) -{ - if (!cleanIcmpLayer()) - return nullptr; +icmp_info_request* IcmpLayer::setInfoRequestData(uint16_t id, + uint16_t sequence) { + if (!cleanIcmpLayer()) + return nullptr; - if (!this->extendLayer(m_DataLen, sizeof(icmp_info_request) - sizeof(icmphdr))) - return nullptr; + if (!this->extendLayer(m_DataLen, + sizeof(icmp_info_request) - sizeof(icmphdr))) + return nullptr; + + getIcmpHeader()->type = (uint8_t)ICMP_INFO_REQUEST; - getIcmpHeader()->type = (uint8_t)ICMP_INFO_REQUEST; + icmp_info_request* header = getInfoRequestData(); + header->code = 0; + header->id = htobe16(id); + header->sequence = htobe16(sequence); - icmp_info_request* header = getInfoRequestData(); - header->code = 0; - header->id = htobe16(id); - header->sequence = htobe16(sequence); - - return header; + return header; } -icmp_info_reply* IcmpLayer::getInfoReplyData() -{ - if (!isMessageOfType(ICMP_INFO_REPLY)) - return nullptr; +icmp_info_reply* IcmpLayer::getInfoReplyData() { + if (!isMessageOfType(ICMP_INFO_REPLY)) + return nullptr; - return (icmp_info_reply*)m_Data; + return (icmp_info_reply*)m_Data; } -icmp_info_reply* IcmpLayer::setInfoReplyData(uint16_t id, uint16_t sequence) -{ - if (!cleanIcmpLayer()) - return nullptr; - - if (!this->extendLayer(m_DataLen, sizeof(icmp_info_reply) - sizeof(icmphdr))) - return nullptr; +icmp_info_reply* IcmpLayer::setInfoReplyData(uint16_t id, uint16_t sequence) { + if (!cleanIcmpLayer()) + return nullptr; - getIcmpHeader()->type = (uint8_t)ICMP_INFO_REPLY; - - icmp_info_reply* header = getInfoReplyData(); - header->code = 0; - header->id = htobe16(id); - header->sequence = htobe16(sequence); - - return header; -} - - -void IcmpLayer::parseNextLayer() -{ - size_t headerLen = getHeaderLen(); - - switch (getMessageType()) - { - case ICMP_DEST_UNREACHABLE: - case ICMP_SOURCE_QUENCH: - case ICMP_TIME_EXCEEDED: - case ICMP_REDIRECT: - case ICMP_PARAM_PROBLEM: - m_NextLayer = IPv4Layer::isDataValid(m_Data + headerLen, m_DataLen - headerLen) - ? static_cast(new IPv4Layer(m_Data + headerLen, m_DataLen - headerLen, this, m_Packet)) - : static_cast(new PayloadLayer(m_Data + headerLen, m_DataLen - headerLen, this, m_Packet)); - return; - default: - if (m_DataLen > headerLen) - m_NextLayer = new PayloadLayer(m_Data + headerLen, m_DataLen - headerLen, this, m_Packet); - return; - } -} - -size_t IcmpLayer::getHeaderLen() const -{ - IcmpMessageType type = getMessageType(); - size_t routerAdvSize = 0; - switch (type) - { - case ICMP_ECHO_REQUEST: - case ICMP_ECHO_REPLY: - return m_DataLen; - case ICMP_TIMESTAMP_REQUEST: - case ICMP_TIMESTAMP_REPLY: - return sizeof(icmp_timestamp_request); - case ICMP_ROUTER_SOL: - case ICMP_INFO_REQUEST: - case ICMP_INFO_REPLY: - case ICMP_UNSUPPORTED: - return sizeof(icmphdr); - case ICMP_ADDRESS_MASK_REPLY: - case ICMP_ADDRESS_MASK_REQUEST: - return sizeof(icmp_address_mask_request); - case ICMP_DEST_UNREACHABLE: - return sizeof(icmp_destination_unreachable); - case ICMP_REDIRECT: - return sizeof(icmp_redirect); - case ICMP_TIME_EXCEEDED: - case ICMP_SOURCE_QUENCH: - return sizeof(icmp_time_exceeded); - case ICMP_PARAM_PROBLEM: - return sizeof(icmp_param_problem); - case ICMP_ROUTER_ADV: - routerAdvSize = sizeof(icmp_router_advertisement_hdr) + (getRouterAdvertisementData()->header->advertisementCount*sizeof(icmp_router_address_structure)); - if (routerAdvSize > m_DataLen) - return m_DataLen; - return routerAdvSize; - default: - return sizeof(icmphdr); - } -} - -void IcmpLayer::computeCalculateFields() -{ - // calculate checksum - getIcmpHeader()->checksum = 0; - - size_t icmpLen = 0; - Layer* curLayer = this; - while (curLayer != nullptr) - { - icmpLen += curLayer->getHeaderLen(); - curLayer = curLayer->getNextLayer(); - } - - ScalarBuffer buffer; - buffer.buffer = (uint16_t*)getIcmpHeader(); - buffer.len = icmpLen; - size_t checksum = computeChecksum(&buffer, 1); - - getIcmpHeader()->checksum = htobe16(checksum); -} - -std::string IcmpLayer::toString() const -{ - std::string messageTypeAsString; - IcmpMessageType type = getMessageType(); - switch (type) - { - case ICMP_ECHO_REPLY: - messageTypeAsString = "Echo (ping) reply"; - break; - case ICMP_DEST_UNREACHABLE: - messageTypeAsString = "Destination unreachable"; - break; - case ICMP_SOURCE_QUENCH: - messageTypeAsString = "Source quench (flow control)"; - break; - case ICMP_REDIRECT: - messageTypeAsString = "Redirect"; - break; - case ICMP_ECHO_REQUEST: - messageTypeAsString = "Echo (ping) request"; - break; - case ICMP_ROUTER_ADV: - messageTypeAsString = "Router advertisement"; - break; - case ICMP_ROUTER_SOL: - messageTypeAsString = "Router solicitation"; - break; - case ICMP_TIME_EXCEEDED: - messageTypeAsString = "Time-to-live exceeded"; - break; - case ICMP_PARAM_PROBLEM: - messageTypeAsString = "Parameter problem: bad IP header"; - break; - case ICMP_TIMESTAMP_REQUEST: - messageTypeAsString = "Timestamp request"; - break; - case ICMP_TIMESTAMP_REPLY: - messageTypeAsString = "Timestamp reply"; - break; - case ICMP_INFO_REQUEST: - messageTypeAsString = "Information request"; - break; - case ICMP_INFO_REPLY: - messageTypeAsString = "Information reply"; - break; - case ICMP_ADDRESS_MASK_REQUEST: - messageTypeAsString = "Address mask request"; - break; - case ICMP_ADDRESS_MASK_REPLY: - messageTypeAsString = "Address mask reply"; - break; - default: - messageTypeAsString = "Unknown"; - break; - } - - std::ostringstream typeStream; - typeStream << (int)getIcmpHeader()->type; - - return "ICMP Layer, " + messageTypeAsString + " (type: " + typeStream.str() + ")"; + if (!this->extendLayer(m_DataLen, sizeof(icmp_info_reply) - sizeof(icmphdr))) + return nullptr; + + getIcmpHeader()->type = (uint8_t)ICMP_INFO_REPLY; + + icmp_info_reply* header = getInfoReplyData(); + header->code = 0; + header->id = htobe16(id); + header->sequence = htobe16(sequence); + + return header; +} + +void IcmpLayer::parseNextLayer() { + size_t headerLen = getHeaderLen(); + + switch (getMessageType()) { + case ICMP_DEST_UNREACHABLE: + case ICMP_SOURCE_QUENCH: + case ICMP_TIME_EXCEEDED: + case ICMP_REDIRECT: + case ICMP_PARAM_PROBLEM: + m_NextLayer = + IPv4Layer::isDataValid(m_Data + headerLen, m_DataLen - headerLen) + ? static_cast(new IPv4Layer( + m_Data + headerLen, m_DataLen - headerLen, this, m_Packet)) + : static_cast(new PayloadLayer( + m_Data + headerLen, m_DataLen - headerLen, this, m_Packet)); + return; + default: + if (m_DataLen > headerLen) + m_NextLayer = new PayloadLayer(m_Data + headerLen, m_DataLen - headerLen, + this, m_Packet); + return; + } +} + +size_t IcmpLayer::getHeaderLen() const { + IcmpMessageType type = getMessageType(); + size_t routerAdvSize = 0; + switch (type) { + case ICMP_ECHO_REQUEST: + case ICMP_ECHO_REPLY: + return m_DataLen; + case ICMP_TIMESTAMP_REQUEST: + case ICMP_TIMESTAMP_REPLY: + return sizeof(icmp_timestamp_request); + case ICMP_ROUTER_SOL: + case ICMP_INFO_REQUEST: + case ICMP_INFO_REPLY: + case ICMP_UNSUPPORTED: + return sizeof(icmphdr); + case ICMP_ADDRESS_MASK_REPLY: + case ICMP_ADDRESS_MASK_REQUEST: + return sizeof(icmp_address_mask_request); + case ICMP_DEST_UNREACHABLE: + return sizeof(icmp_destination_unreachable); + case ICMP_REDIRECT: + return sizeof(icmp_redirect); + case ICMP_TIME_EXCEEDED: + case ICMP_SOURCE_QUENCH: + return sizeof(icmp_time_exceeded); + case ICMP_PARAM_PROBLEM: + return sizeof(icmp_param_problem); + case ICMP_ROUTER_ADV: + routerAdvSize = sizeof(icmp_router_advertisement_hdr) + + (getRouterAdvertisementData()->header->advertisementCount * + sizeof(icmp_router_address_structure)); + if (routerAdvSize > m_DataLen) + return m_DataLen; + return routerAdvSize; + default: + return sizeof(icmphdr); + } +} + +void IcmpLayer::computeCalculateFields() { + // calculate checksum + getIcmpHeader()->checksum = 0; + + size_t icmpLen = 0; + Layer* curLayer = this; + while (curLayer != nullptr) { + icmpLen += curLayer->getHeaderLen(); + curLayer = curLayer->getNextLayer(); + } + + ScalarBuffer buffer; + buffer.buffer = (uint16_t*)getIcmpHeader(); + buffer.len = icmpLen; + size_t checksum = computeChecksum(&buffer, 1); + + getIcmpHeader()->checksum = htobe16(checksum); +} + +std::string IcmpLayer::toString() const { + std::string messageTypeAsString; + IcmpMessageType type = getMessageType(); + switch (type) { + case ICMP_ECHO_REPLY: + messageTypeAsString = "Echo (ping) reply"; + break; + case ICMP_DEST_UNREACHABLE: + messageTypeAsString = "Destination unreachable"; + break; + case ICMP_SOURCE_QUENCH: + messageTypeAsString = "Source quench (flow control)"; + break; + case ICMP_REDIRECT: + messageTypeAsString = "Redirect"; + break; + case ICMP_ECHO_REQUEST: + messageTypeAsString = "Echo (ping) request"; + break; + case ICMP_ROUTER_ADV: + messageTypeAsString = "Router advertisement"; + break; + case ICMP_ROUTER_SOL: + messageTypeAsString = "Router solicitation"; + break; + case ICMP_TIME_EXCEEDED: + messageTypeAsString = "Time-to-live exceeded"; + break; + case ICMP_PARAM_PROBLEM: + messageTypeAsString = "Parameter problem: bad IP header"; + break; + case ICMP_TIMESTAMP_REQUEST: + messageTypeAsString = "Timestamp request"; + break; + case ICMP_TIMESTAMP_REPLY: + messageTypeAsString = "Timestamp reply"; + break; + case ICMP_INFO_REQUEST: + messageTypeAsString = "Information request"; + break; + case ICMP_INFO_REPLY: + messageTypeAsString = "Information reply"; + break; + case ICMP_ADDRESS_MASK_REQUEST: + messageTypeAsString = "Address mask request"; + break; + case ICMP_ADDRESS_MASK_REPLY: + messageTypeAsString = "Address mask reply"; + break; + default: + messageTypeAsString = "Unknown"; + break; + } + + std::ostringstream typeStream; + typeStream << (int)getIcmpHeader()->type; + + return "ICMP Layer, " + messageTypeAsString + " (type: " + typeStream.str() + + ")"; } } // namespace pcpp diff --git a/Packet++/src/IcmpV6Layer.cpp b/Packet++/src/IcmpV6Layer.cpp index 7fab0db9da..dd620e1e6b 100644 --- a/Packet++/src/IcmpV6Layer.cpp +++ b/Packet++/src/IcmpV6Layer.cpp @@ -12,158 +12,147 @@ // IcmpV6Layer -namespace pcpp -{ - -Layer* IcmpV6Layer::parseIcmpV6Layer(uint8_t *data, size_t dataLen, Layer* prevLayer, Packet* packet) -{ - if (dataLen < sizeof(icmpv6hdr)) - return new PayloadLayer(data, dataLen, prevLayer, packet); - - icmpv6hdr *hdr = (icmpv6hdr *)data; - ICMPv6MessageType messageType = static_cast(hdr->type); - - switch (messageType) - { - case ICMPv6MessageType::ICMPv6_ECHO_REQUEST: - case ICMPv6MessageType::ICMPv6_ECHO_REPLY: - return new ICMPv6EchoLayer(data, dataLen, prevLayer, packet); - case ICMPv6MessageType::ICMPv6_NEIGHBOR_SOLICITATION: - return new NDPNeighborSolicitationLayer(data, dataLen, prevLayer, packet); - case ICMPv6MessageType::ICMPv6_NEIGHBOR_ADVERTISEMENT: - return new NDPNeighborAdvertisementLayer(data, dataLen, prevLayer, packet); - case ICMPv6MessageType::ICMPv6_UNKNOWN_MESSAGE: - return new PayloadLayer(data, dataLen, prevLayer, packet); - default: - return new IcmpV6Layer(data, dataLen, prevLayer, packet); - } +namespace pcpp { + +Layer* IcmpV6Layer::parseIcmpV6Layer(uint8_t* data, size_t dataLen, + Layer* prevLayer, Packet* packet) { + if (dataLen < sizeof(icmpv6hdr)) + return new PayloadLayer(data, dataLen, prevLayer, packet); + + icmpv6hdr* hdr = (icmpv6hdr*)data; + ICMPv6MessageType messageType = static_cast(hdr->type); + + switch (messageType) { + case ICMPv6MessageType::ICMPv6_ECHO_REQUEST: + case ICMPv6MessageType::ICMPv6_ECHO_REPLY: + return new ICMPv6EchoLayer(data, dataLen, prevLayer, packet); + case ICMPv6MessageType::ICMPv6_NEIGHBOR_SOLICITATION: + return new NDPNeighborSolicitationLayer(data, dataLen, prevLayer, packet); + case ICMPv6MessageType::ICMPv6_NEIGHBOR_ADVERTISEMENT: + return new NDPNeighborAdvertisementLayer(data, dataLen, prevLayer, packet); + case ICMPv6MessageType::ICMPv6_UNKNOWN_MESSAGE: + return new PayloadLayer(data, dataLen, prevLayer, packet); + default: + return new IcmpV6Layer(data, dataLen, prevLayer, packet); + } } -IcmpV6Layer::IcmpV6Layer(ICMPv6MessageType msgType, uint8_t code, const uint8_t *data, size_t dataLen) -{ - m_DataLen = sizeof(icmpv6hdr) + dataLen; - m_Data = new uint8_t[m_DataLen]; - memset(m_Data, 0, m_DataLen); - m_Protocol = ICMPv6; +IcmpV6Layer::IcmpV6Layer(ICMPv6MessageType msgType, uint8_t code, + const uint8_t* data, size_t dataLen) { + m_DataLen = sizeof(icmpv6hdr) + dataLen; + m_Data = new uint8_t[m_DataLen]; + memset(m_Data, 0, m_DataLen); + m_Protocol = ICMPv6; - icmpv6hdr *hdr = (icmpv6hdr *)m_Data; - hdr->type = static_cast(msgType); - hdr->code = code; + icmpv6hdr* hdr = (icmpv6hdr*)m_Data; + hdr->type = static_cast(msgType); + hdr->code = code; - if (data != nullptr && dataLen > 0) - memcpy(m_Data + sizeof(icmpv6hdr), data, dataLen); + if (data != nullptr && dataLen > 0) + memcpy(m_Data + sizeof(icmpv6hdr), data, dataLen); } -ICMPv6MessageType IcmpV6Layer::getMessageType() const -{ - return static_cast(getIcmpv6Header()->type); +ICMPv6MessageType IcmpV6Layer::getMessageType() const { + return static_cast(getIcmpv6Header()->type); } -uint8_t IcmpV6Layer::getCode() const -{ - return getIcmpv6Header()->code; -} - -uint16_t IcmpV6Layer::getChecksum() const -{ - return be16toh(getIcmpv6Header()->checksum); -} +uint8_t IcmpV6Layer::getCode() const { return getIcmpv6Header()->code; } -void IcmpV6Layer::computeCalculateFields() -{ - calculateChecksum(); +uint16_t IcmpV6Layer::getChecksum() const { + return be16toh(getIcmpv6Header()->checksum); } -void IcmpV6Layer::calculateChecksum() -{ - /* Pseudo header of 40 bytes which is composed as follows(in order): - - 16 bytes for the source address - - 16 bytes for the destination address - - 4 bytes big endian payload length(the same value as in the IPv6 header) - - 3 bytes zero + 1 byte nextheader( 58 decimal) big endian - */ - - getIcmpv6Header()->checksum = 0; - - if (m_PrevLayer != nullptr) - { - ScalarBuffer vec[2]; - - vec[0].buffer = (uint16_t *)m_Data; - vec[0].len = m_DataLen; - - const unsigned int pseudoHeaderLen = 40; - const unsigned int bigEndianLen = htobe32(m_DataLen); - const unsigned int bigEndianNextHeader = htobe32(PACKETPP_IPPROTO_ICMPV6); - - uint16_t pseudoHeader[pseudoHeaderLen / 2]; - ((IPv6Layer *)m_PrevLayer)->getSrcIPv6Address().copyTo((uint8_t *)pseudoHeader); - ((IPv6Layer *)m_PrevLayer)->getDstIPv6Address().copyTo((uint8_t *)(pseudoHeader + 8)); - memcpy(&pseudoHeader[16], &bigEndianLen, sizeof(uint32_t)); - memcpy(&pseudoHeader[18], &bigEndianNextHeader, sizeof(uint32_t)); - vec[1].buffer = pseudoHeader; - vec[1].len = pseudoHeaderLen; - - /* Calculate and write checksum */ - getIcmpv6Header()->checksum = htobe16(computeChecksum(vec, 2)); - } +void IcmpV6Layer::computeCalculateFields() { calculateChecksum(); } + +void IcmpV6Layer::calculateChecksum() { + /* Pseudo header of 40 bytes which is composed as follows(in order): + - 16 bytes for the source address + - 16 bytes for the destination address + - 4 bytes big endian payload length(the same value as in the IPv6 header) + - 3 bytes zero + 1 byte nextheader( 58 decimal) big endian + */ + + getIcmpv6Header()->checksum = 0; + + if (m_PrevLayer != nullptr) { + ScalarBuffer vec[2]; + + vec[0].buffer = (uint16_t*)m_Data; + vec[0].len = m_DataLen; + + const unsigned int pseudoHeaderLen = 40; + const unsigned int bigEndianLen = htobe32(m_DataLen); + const unsigned int bigEndianNextHeader = htobe32(PACKETPP_IPPROTO_ICMPV6); + + uint16_t pseudoHeader[pseudoHeaderLen / 2]; + ((IPv6Layer*)m_PrevLayer) + ->getSrcIPv6Address() + .copyTo((uint8_t*)pseudoHeader); + ((IPv6Layer*)m_PrevLayer) + ->getDstIPv6Address() + .copyTo((uint8_t*)(pseudoHeader + 8)); + memcpy(&pseudoHeader[16], &bigEndianLen, sizeof(uint32_t)); + memcpy(&pseudoHeader[18], &bigEndianNextHeader, sizeof(uint32_t)); + vec[1].buffer = pseudoHeader; + vec[1].len = pseudoHeaderLen; + + /* Calculate and write checksum */ + getIcmpv6Header()->checksum = htobe16(computeChecksum(vec, 2)); + } } -std::string IcmpV6Layer::toString() const -{ - std::ostringstream typeStream; - typeStream << (int)getMessageType(); - return "ICMPv6 Layer, Message type: " + typeStream.str(); +std::string IcmpV6Layer::toString() const { + std::ostringstream typeStream; + typeStream << (int)getMessageType(); + return "ICMPv6 Layer, Message type: " + typeStream.str(); } // // ICMPv6EchoLayer // -ICMPv6EchoLayer::ICMPv6EchoLayer(ICMPv6EchoType echoType, uint16_t id, uint16_t sequence, const uint8_t *data, size_t dataLen) -{ - m_DataLen = sizeof(icmpv6_echo_hdr) + dataLen; - m_Data = new uint8_t[m_DataLen]; - memset(m_Data, 0, m_DataLen); - m_Protocol = ICMPv6; - - icmpv6_echo_hdr *header = getEchoHeader(); - - switch (echoType) - { - case REPLY: - header->type = static_cast(ICMPv6MessageType::ICMPv6_ECHO_REPLY); - break; - case REQUEST: - default: - header->type = static_cast(ICMPv6MessageType::ICMPv6_ECHO_REQUEST); - break; - } - - header->code = 0; - header->checksum = 0; - header->id = htobe16(id); - header->sequence = htobe16(sequence); - - if (data != nullptr && dataLen > 0) - memcpy(getEchoDataPtr(), data, dataLen); +ICMPv6EchoLayer::ICMPv6EchoLayer(ICMPv6EchoType echoType, uint16_t id, + uint16_t sequence, const uint8_t* data, + size_t dataLen) { + m_DataLen = sizeof(icmpv6_echo_hdr) + dataLen; + m_Data = new uint8_t[m_DataLen]; + memset(m_Data, 0, m_DataLen); + m_Protocol = ICMPv6; + + icmpv6_echo_hdr* header = getEchoHeader(); + + switch (echoType) { + case REPLY: + header->type = static_cast(ICMPv6MessageType::ICMPv6_ECHO_REPLY); + break; + case REQUEST: + default: + header->type = static_cast(ICMPv6MessageType::ICMPv6_ECHO_REQUEST); + break; + } + + header->code = 0; + header->checksum = 0; + header->id = htobe16(id); + header->sequence = htobe16(sequence); + + if (data != nullptr && dataLen > 0) + memcpy(getEchoDataPtr(), data, dataLen); } -uint16_t ICMPv6EchoLayer::getIdentifier() const -{ - return be16toh(getEchoHeader()->id); +uint16_t ICMPv6EchoLayer::getIdentifier() const { + return be16toh(getEchoHeader()->id); } -uint16_t ICMPv6EchoLayer::getSequenceNr() const -{ - return be16toh(getEchoHeader()->sequence); +uint16_t ICMPv6EchoLayer::getSequenceNr() const { + return be16toh(getEchoHeader()->sequence); } -std::string ICMPv6EchoLayer::toString() const -{ - std::ostringstream typeStream; - typeStream << (int)getMessageType(); - return "ICMPv6 Layer, Echo Request/Reply Message (type: " + typeStream.str() + ")"; +std::string ICMPv6EchoLayer::toString() const { + std::ostringstream typeStream; + typeStream << (int)getMessageType(); + return "ICMPv6 Layer, Echo Request/Reply Message (type: " + typeStream.str() + + ")"; } } // namespace pcpp diff --git a/Packet++/src/IgmpLayer.cpp b/Packet++/src/IgmpLayer.cpp index d1df38e237..b71baa6ddc 100644 --- a/Packet++/src/IgmpLayer.cpp +++ b/Packet++/src/IgmpLayer.cpp @@ -1,554 +1,513 @@ #define LOG_MODULE PacketLogModuleIgmpLayer #include "IgmpLayer.h" -#include "PacketUtils.h" +#include "EndianPortable.h" #include "Logger.h" +#include "PacketUtils.h" #include -#include "EndianPortable.h" -namespace pcpp -{ +namespace pcpp { /************* * IgmpLayer *************/ -IgmpLayer::IgmpLayer(IgmpType type, const IPv4Address& groupAddr, uint8_t maxResponseTime, ProtocolType igmpVer) -{ - m_DataLen = getHeaderSizeByVerAndType(igmpVer, type); - m_Data = new uint8_t[m_DataLen]; - memset(m_Data, 0, m_DataLen); - m_Protocol = igmpVer; - - setType(type); - if (groupAddr.isValid()) - setGroupAddress(groupAddr); - - getIgmpHeader()->maxResponseTime = maxResponseTime; -} - -void IgmpLayer::setGroupAddress(const IPv4Address& groupAddr) -{ - igmp_header* hdr = getIgmpHeader(); - hdr->groupAddress = groupAddr.toInt(); -} - -IgmpType IgmpLayer::getType() const -{ - uint8_t type = getIgmpHeader()->type; - if (type < (uint8_t)IgmpType_MembershipQuery || - (type > (uint8_t)IgmpType_LeaveGroup && type < (uint8_t)IgmpType_MulticastTracerouteResponse) || - (type > (uint8_t)IgmpType_MulticastTraceroute && type < (uint8_t)IgmpType_MembershipReportV3) || - (type > (uint8_t)IgmpType_MembershipReportV3 && type < (uint8_t)IgmpType_MulticastRouterAdvertisement) || - type > IgmpType_MulticastRouterTermination) - return IgmpType_Unknown; - - return (IgmpType)type; -} - -void IgmpLayer::setType(IgmpType type) -{ - if (type == IgmpType_Unknown) - return; - - igmp_header* hdr = getIgmpHeader(); - hdr->type = type; +IgmpLayer::IgmpLayer(IgmpType type, const IPv4Address& groupAddr, + uint8_t maxResponseTime, ProtocolType igmpVer) { + m_DataLen = getHeaderSizeByVerAndType(igmpVer, type); + m_Data = new uint8_t[m_DataLen]; + memset(m_Data, 0, m_DataLen); + m_Protocol = igmpVer; + + setType(type); + if (groupAddr.isValid()) + setGroupAddress(groupAddr); + + getIgmpHeader()->maxResponseTime = maxResponseTime; +} + +void IgmpLayer::setGroupAddress(const IPv4Address& groupAddr) { + igmp_header* hdr = getIgmpHeader(); + hdr->groupAddress = groupAddr.toInt(); +} + +IgmpType IgmpLayer::getType() const { + uint8_t type = getIgmpHeader()->type; + if (type < (uint8_t)IgmpType_MembershipQuery || + (type > (uint8_t)IgmpType_LeaveGroup && + type < (uint8_t)IgmpType_MulticastTracerouteResponse) || + (type > (uint8_t)IgmpType_MulticastTraceroute && + type < (uint8_t)IgmpType_MembershipReportV3) || + (type > (uint8_t)IgmpType_MembershipReportV3 && + type < (uint8_t)IgmpType_MulticastRouterAdvertisement) || + type > IgmpType_MulticastRouterTermination) + return IgmpType_Unknown; + + return (IgmpType)type; +} + +void IgmpLayer::setType(IgmpType type) { + if (type == IgmpType_Unknown) + return; + + igmp_header* hdr = getIgmpHeader(); + hdr->type = type; +} + +ProtocolType IgmpLayer::getIGMPVerFromData(uint8_t* data, size_t dataLen, + bool& isQuery) { + isQuery = false; + + if (dataLen < 8 || data == nullptr) + return UnknownProtocol; + + switch ((int)data[0]) { + case IgmpType_MembershipReportV2: + case IgmpType_LeaveGroup: + return IGMPv2; + case IgmpType_MembershipReportV1: + return IGMPv1; + case IgmpType_MembershipReportV3: + return IGMPv3; + case IgmpType_MembershipQuery: { + isQuery = true; + + if (dataLen >= sizeof(igmpv3_query_header)) + return IGMPv3; + + if (data[1] == 0) + return IGMPv1; + else + return IGMPv2; + } + default: + return UnknownProtocol; + } +} + +uint16_t IgmpLayer::calculateChecksum() { + ScalarBuffer buffer; + buffer.buffer = (uint16_t*)getIgmpHeader(); + buffer.len = getHeaderLen(); + return computeChecksum(&buffer, 1); +} + +size_t IgmpLayer::getHeaderSizeByVerAndType(ProtocolType igmpVer, + IgmpType igmpType) const { + if (igmpVer == IGMPv1 || igmpVer == IGMPv2) + return sizeof(igmp_header); + + if (igmpVer == IGMPv3) { + if (igmpType == IgmpType_MembershipQuery) + return sizeof(igmpv3_query_header); + else if (igmpType == IgmpType_MembershipReportV3) + return sizeof(igmpv3_report_header); + } + + return 0; +} + +std::string IgmpLayer::toString() const { + std::string igmpVer = ""; + switch (getProtocol()) { + case IGMPv1: + igmpVer = "1"; + break; + case IGMPv2: + igmpVer = "2"; + break; + default: + igmpVer = "3"; + } + + std::string msgType; + + switch (getType()) { + case IgmpType_MembershipQuery: + msgType = "Membership Query"; + break; + case IgmpType_MembershipReportV1: + msgType = "Membership Report"; + break; + case IgmpType_DVMRP: + msgType = "DVMRP"; + break; + case IgmpType_P1Mv1: + msgType = "PIMv1"; + break; + case IgmpType_CiscoTrace: + msgType = "Cisco Trace"; + break; + case IgmpType_MembershipReportV2: + msgType = "Membership Report"; + break; + case IgmpType_LeaveGroup: + msgType = "Leave Group"; + break; + case IgmpType_MulticastTracerouteResponse: + msgType = "Multicast Traceroute Response"; + break; + case IgmpType_MulticastTraceroute: + msgType = "Multicast Traceroute"; + break; + case IgmpType_MembershipReportV3: + msgType = "Membership Report"; + break; + case IgmpType_MulticastRouterAdvertisement: + msgType = "Multicast Router Advertisement"; + break; + case IgmpType_MulticastRouterSolicitation: + msgType = "Multicast Router Solicitation"; + break; + case IgmpType_MulticastRouterTermination: + msgType = "Multicast Router Termination"; + break; + default: + msgType = "Unknown"; + break; + } + + std::string result = "IGMPv" + igmpVer + " Layer, " + msgType + " message"; + return result; } -ProtocolType IgmpLayer::getIGMPVerFromData(uint8_t* data, size_t dataLen, bool& isQuery) -{ - isQuery = false; - - if (dataLen < 8 || data == nullptr) - return UnknownProtocol; - - switch ((int)data[0]) - { - case IgmpType_MembershipReportV2: - case IgmpType_LeaveGroup: - return IGMPv2; - case IgmpType_MembershipReportV1: - return IGMPv1; - case IgmpType_MembershipReportV3: - return IGMPv3; - case IgmpType_MembershipQuery: - { - isQuery = true; - - if (dataLen >= sizeof(igmpv3_query_header)) - return IGMPv3; - - if (data[1] == 0) - return IGMPv1; - else - return IGMPv2; - } - default: - return UnknownProtocol; - } -} - -uint16_t IgmpLayer::calculateChecksum() -{ - ScalarBuffer buffer; - buffer.buffer = (uint16_t*)getIgmpHeader(); - buffer.len = getHeaderLen(); - return computeChecksum(&buffer, 1); -} - -size_t IgmpLayer::getHeaderSizeByVerAndType(ProtocolType igmpVer, IgmpType igmpType) const -{ - if (igmpVer == IGMPv1 || igmpVer == IGMPv2) - return sizeof(igmp_header); - - if (igmpVer == IGMPv3) - { - if (igmpType == IgmpType_MembershipQuery) - return sizeof(igmpv3_query_header); - else if (igmpType == IgmpType_MembershipReportV3) - return sizeof(igmpv3_report_header); - } - - return 0; -} - -std::string IgmpLayer::toString() const -{ - std::string igmpVer = ""; - switch (getProtocol()) - { - case IGMPv1: - igmpVer = "1"; - break; - case IGMPv2: - igmpVer = "2"; - break; - default: - igmpVer = "3"; - } - - std::string msgType; - - switch (getType()) - { - case IgmpType_MembershipQuery: - msgType = "Membership Query"; - break; - case IgmpType_MembershipReportV1: - msgType = "Membership Report"; - break; - case IgmpType_DVMRP: - msgType = "DVMRP"; - break; - case IgmpType_P1Mv1: - msgType = "PIMv1"; - break; - case IgmpType_CiscoTrace: - msgType = "Cisco Trace"; - break; - case IgmpType_MembershipReportV2: - msgType = "Membership Report"; - break; - case IgmpType_LeaveGroup: - msgType = "Leave Group"; - break; - case IgmpType_MulticastTracerouteResponse: - msgType = "Multicast Traceroute Response"; - break; - case IgmpType_MulticastTraceroute: - msgType = "Multicast Traceroute"; - break; - case IgmpType_MembershipReportV3: - msgType = "Membership Report"; - break; - case IgmpType_MulticastRouterAdvertisement: - msgType = "Multicast Router Advertisement"; - break; - case IgmpType_MulticastRouterSolicitation: - msgType = "Multicast Router Solicitation"; - break; - case IgmpType_MulticastRouterTermination: - msgType = "Multicast Router Termination"; - break; - default: - msgType = "Unknown"; - break; - } - - std::string result = "IGMPv" + igmpVer + " Layer, " + msgType + " message"; - return result; -} - - - - /************* * IgmpV1Layer *************/ - -void IgmpV1Layer::computeCalculateFields() -{ - igmp_header* hdr = getIgmpHeader(); - hdr->checksum = 0; - hdr->checksum = htobe16(calculateChecksum()); - hdr->maxResponseTime = 0; +void IgmpV1Layer::computeCalculateFields() { + igmp_header* hdr = getIgmpHeader(); + hdr->checksum = 0; + hdr->checksum = htobe16(calculateChecksum()); + hdr->maxResponseTime = 0; } - - - - /************* * IgmpV2Layer *************/ - -void IgmpV2Layer::computeCalculateFields() -{ - igmp_header* hdr = getIgmpHeader(); - hdr->checksum = 0; - hdr->checksum = htobe16(calculateChecksum()); +void IgmpV2Layer::computeCalculateFields() { + igmp_header* hdr = getIgmpHeader(); + hdr->checksum = 0; + hdr->checksum = htobe16(calculateChecksum()); } - - - - /****************** * IgmpV3QueryLayer ******************/ +IgmpV3QueryLayer::IgmpV3QueryLayer(uint8_t* data, size_t dataLen, + Layer* prevLayer, Packet* packet) + : IgmpLayer(data, dataLen, prevLayer, packet, IGMPv3) {} -IgmpV3QueryLayer::IgmpV3QueryLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : - IgmpLayer(data, dataLen, prevLayer, packet, IGMPv3) -{ -} - -IgmpV3QueryLayer::IgmpV3QueryLayer(const IPv4Address& multicastAddr, uint8_t maxResponseTime, uint8_t s_qrv) : - IgmpLayer(IgmpType_MembershipQuery, multicastAddr, maxResponseTime, IGMPv3) -{ - getIgmpV3QueryHeader()->s_qrv = s_qrv; +IgmpV3QueryLayer::IgmpV3QueryLayer(const IPv4Address& multicastAddr, + uint8_t maxResponseTime, uint8_t s_qrv) + : IgmpLayer(IgmpType_MembershipQuery, multicastAddr, maxResponseTime, + IGMPv3) { + getIgmpV3QueryHeader()->s_qrv = s_qrv; } -uint16_t IgmpV3QueryLayer::getSourceAddressCount() const -{ - return be16toh(getIgmpV3QueryHeader()->numOfSources); +uint16_t IgmpV3QueryLayer::getSourceAddressCount() const { + return be16toh(getIgmpV3QueryHeader()->numOfSources); } -IPv4Address IgmpV3QueryLayer::getSourceAddressAtIndex(int index) const -{ - uint16_t numOfSources = getSourceAddressCount(); - if (index < 0 || index >= numOfSources) - return IPv4Address(); +IPv4Address IgmpV3QueryLayer::getSourceAddressAtIndex(int index) const { + uint16_t numOfSources = getSourceAddressCount(); + if (index < 0 || index >= numOfSources) + return IPv4Address(); - // verify numOfRecords is a reasonable number that points to data within the packet - int ptrOffset = index * sizeof(uint32_t) + sizeof(igmpv3_query_header); - if (ptrOffset + sizeof(uint32_t) > getDataLen()) - return IPv4Address(); + // verify numOfRecords is a reasonable number that points to data within the + // packet + int ptrOffset = index * sizeof(uint32_t) + sizeof(igmpv3_query_header); + if (ptrOffset + sizeof(uint32_t) > getDataLen()) + return IPv4Address(); - uint8_t* ptr = m_Data + ptrOffset; - return IPv4Address(*(uint32_t*)ptr); + uint8_t* ptr = m_Data + ptrOffset; + return IPv4Address(*(uint32_t*)ptr); } -size_t IgmpV3QueryLayer::getHeaderLen() const -{ - uint16_t numOfSources = getSourceAddressCount(); +size_t IgmpV3QueryLayer::getHeaderLen() const { + uint16_t numOfSources = getSourceAddressCount(); - int headerLen = numOfSources * sizeof(uint32_t) + sizeof(igmpv3_query_header); + int headerLen = numOfSources * sizeof(uint32_t) + sizeof(igmpv3_query_header); - // verify numOfRecords is a reasonable number that points to data within the packet - if ((size_t)headerLen > getDataLen()) - return getDataLen(); + // verify numOfRecords is a reasonable number that points to data within the + // packet + if ((size_t)headerLen > getDataLen()) + return getDataLen(); - return (size_t)headerLen; + return (size_t)headerLen; } -void IgmpV3QueryLayer::computeCalculateFields() -{ - igmpv3_query_header* hdr = getIgmpV3QueryHeader(); - hdr->checksum = 0; - hdr->checksum = htobe16(calculateChecksum()); +void IgmpV3QueryLayer::computeCalculateFields() { + igmpv3_query_header* hdr = getIgmpV3QueryHeader(); + hdr->checksum = 0; + hdr->checksum = htobe16(calculateChecksum()); } -bool IgmpV3QueryLayer::addSourceAddress(const IPv4Address& addr) -{ - return addSourceAddressAtIndex(addr, getSourceAddressCount()); +bool IgmpV3QueryLayer::addSourceAddress(const IPv4Address& addr) { + return addSourceAddressAtIndex(addr, getSourceAddressCount()); } -bool IgmpV3QueryLayer::addSourceAddressAtIndex(const IPv4Address& addr, int index) -{ - uint16_t sourceAddrCount = getSourceAddressCount(); +bool IgmpV3QueryLayer::addSourceAddressAtIndex(const IPv4Address& addr, + int index) { + uint16_t sourceAddrCount = getSourceAddressCount(); - if (index < 0 || index > (int)sourceAddrCount) - { - PCPP_LOG_ERROR("Cannot add source address at index " << index << ", index is out of bounds"); - return false; - } + if (index < 0 || index > (int)sourceAddrCount) { + PCPP_LOG_ERROR("Cannot add source address at index " + << index << ", index is out of bounds"); + return false; + } - size_t offset = sizeof(igmpv3_query_header) + index * sizeof(uint32_t); - if (offset > getHeaderLen()) - { - PCPP_LOG_ERROR("Cannot add source address at index " << index << ", index is out of packet bounds"); - return false; - } + size_t offset = sizeof(igmpv3_query_header) + index * sizeof(uint32_t); + if (offset > getHeaderLen()) { + PCPP_LOG_ERROR("Cannot add source address at index " + << index << ", index is out of packet bounds"); + return false; + } - if (!extendLayer(offset, sizeof(uint32_t))) - { - PCPP_LOG_ERROR("Cannot add source address at index " << index << ", didn't manage to extend layer"); - return false; - } + if (!extendLayer(offset, sizeof(uint32_t))) { + PCPP_LOG_ERROR("Cannot add source address at index " + << index << ", didn't manage to extend layer"); + return false; + } - memcpy(m_Data + offset, addr.toBytes(), sizeof(uint32_t)); + memcpy(m_Data + offset, addr.toBytes(), sizeof(uint32_t)); - getIgmpV3QueryHeader()->numOfSources = htobe16(sourceAddrCount+1); + getIgmpV3QueryHeader()->numOfSources = htobe16(sourceAddrCount + 1); - return true; + return true; } -bool IgmpV3QueryLayer::removeSourceAddressAtIndex(int index) -{ - uint16_t sourceAddrCount = getSourceAddressCount(); +bool IgmpV3QueryLayer::removeSourceAddressAtIndex(int index) { + uint16_t sourceAddrCount = getSourceAddressCount(); - if (index < 0 || index > (int)sourceAddrCount-1) - { - PCPP_LOG_ERROR("Cannot remove source address at index " << index << ", index is out of bounds"); - return false; - } + if (index < 0 || index > (int)sourceAddrCount - 1) { + PCPP_LOG_ERROR("Cannot remove source address at index " + << index << ", index is out of bounds"); + return false; + } - size_t offset = sizeof(igmpv3_query_header) + index * sizeof(uint32_t); - if (offset >= getHeaderLen()) - { - PCPP_LOG_ERROR("Cannot remove source address at index " << index << ", index is out of packet bounds"); - return false; - } + size_t offset = sizeof(igmpv3_query_header) + index * sizeof(uint32_t); + if (offset >= getHeaderLen()) { + PCPP_LOG_ERROR("Cannot remove source address at index " + << index << ", index is out of packet bounds"); + return false; + } - if (!shortenLayer(offset, sizeof(uint32_t))) - { - PCPP_LOG_ERROR("Cannot remove source address at index " << index << ", didn't manage to shorten layer"); - return false; - } + if (!shortenLayer(offset, sizeof(uint32_t))) { + PCPP_LOG_ERROR("Cannot remove source address at index " + << index << ", didn't manage to shorten layer"); + return false; + } - getIgmpV3QueryHeader()->numOfSources = htobe16(sourceAddrCount-1); + getIgmpV3QueryHeader()->numOfSources = htobe16(sourceAddrCount - 1); - return true; + return true; } -bool IgmpV3QueryLayer::removeAllSourceAddresses() -{ - size_t offset = sizeof(igmpv3_query_header); - size_t numOfBytesToShorted = getHeaderLen() - offset; +bool IgmpV3QueryLayer::removeAllSourceAddresses() { + size_t offset = sizeof(igmpv3_query_header); + size_t numOfBytesToShorted = getHeaderLen() - offset; - if (!shortenLayer(offset, numOfBytesToShorted)) - { - PCPP_LOG_ERROR("Cannot remove all source addresses, didn't manage to shorten layer"); - return false; - } + if (!shortenLayer(offset, numOfBytesToShorted)) { + PCPP_LOG_ERROR( + "Cannot remove all source addresses, didn't manage to shorten layer"); + return false; + } - getIgmpV3QueryHeader()->numOfSources = 0; + getIgmpV3QueryHeader()->numOfSources = 0; - return true; + return true; } - - - - /******************* * IgmpV3ReportLayer *******************/ - -uint16_t IgmpV3ReportLayer::getGroupRecordCount() const -{ - return be16toh(getReportHeader()->numOfGroupRecords); +uint16_t IgmpV3ReportLayer::getGroupRecordCount() const { + return be16toh(getReportHeader()->numOfGroupRecords); } -igmpv3_group_record* IgmpV3ReportLayer::getFirstGroupRecord() const -{ - // check if there are group records at all - if (getHeaderLen() <= sizeof(igmpv3_report_header)) - return nullptr; +igmpv3_group_record* IgmpV3ReportLayer::getFirstGroupRecord() const { + // check if there are group records at all + if (getHeaderLen() <= sizeof(igmpv3_report_header)) + return nullptr; - uint8_t* curGroupPtr = m_Data + sizeof(igmpv3_report_header); - return (igmpv3_group_record*)curGroupPtr; + uint8_t* curGroupPtr = m_Data + sizeof(igmpv3_report_header); + return (igmpv3_group_record*)curGroupPtr; } -igmpv3_group_record* IgmpV3ReportLayer::getNextGroupRecord(igmpv3_group_record* groupRecord) const -{ - if (groupRecord == nullptr) - return nullptr; +igmpv3_group_record* +IgmpV3ReportLayer::getNextGroupRecord(igmpv3_group_record* groupRecord) const { + if (groupRecord == nullptr) + return nullptr; - // prev group was the last group - if ((uint8_t*)groupRecord + groupRecord->getRecordLen() - m_Data >= (int)getHeaderLen()) - return nullptr; + // prev group was the last group + if ((uint8_t*)groupRecord + groupRecord->getRecordLen() - m_Data >= + (int)getHeaderLen()) + return nullptr; - igmpv3_group_record* nextGroup = (igmpv3_group_record*)((uint8_t*)groupRecord + groupRecord->getRecordLen()); + igmpv3_group_record* nextGroup = + (igmpv3_group_record*)((uint8_t*)groupRecord + + groupRecord->getRecordLen()); - return nextGroup; + return nextGroup; } -void IgmpV3ReportLayer::computeCalculateFields() -{ - igmpv3_report_header* hdr = getReportHeader(); - hdr->checksum = 0; - hdr->checksum = htobe16(calculateChecksum()); +void IgmpV3ReportLayer::computeCalculateFields() { + igmpv3_report_header* hdr = getReportHeader(); + hdr->checksum = 0; + hdr->checksum = htobe16(calculateChecksum()); } -igmpv3_group_record* IgmpV3ReportLayer::addGroupRecordAt(uint8_t recordType, const IPv4Address& multicastAddress, const std::vector& sourceAddresses, int offset) -{ - if (offset > (int)getHeaderLen()) - { - PCPP_LOG_ERROR("Cannot add group record, offset is out of layer bounds"); - return nullptr; - } +igmpv3_group_record* IgmpV3ReportLayer::addGroupRecordAt( + uint8_t recordType, const IPv4Address& multicastAddress, + const std::vector& sourceAddresses, int offset) { + if (offset > (int)getHeaderLen()) { + PCPP_LOG_ERROR("Cannot add group record, offset is out of layer bounds"); + return nullptr; + } - size_t groupRecordSize = sizeof(igmpv3_group_record) + sizeof(uint32_t)*sourceAddresses.size(); + size_t groupRecordSize = + sizeof(igmpv3_group_record) + sizeof(uint32_t) * sourceAddresses.size(); - if (!extendLayer(offset, groupRecordSize)) - { - PCPP_LOG_ERROR("Cannot add group record, cannot extend layer"); - return nullptr; - } + if (!extendLayer(offset, groupRecordSize)) { + PCPP_LOG_ERROR("Cannot add group record, cannot extend layer"); + return nullptr; + } - uint8_t* groupRecordBuffer = new uint8_t[groupRecordSize]; - memset(groupRecordBuffer, 0, groupRecordSize); - igmpv3_group_record* newGroupRecord = (igmpv3_group_record*)groupRecordBuffer; - newGroupRecord->multicastAddress = multicastAddress.toInt(); - newGroupRecord->recordType = recordType; - newGroupRecord->auxDataLen = 0; - newGroupRecord->numOfSources = htobe16(sourceAddresses.size()); + uint8_t* groupRecordBuffer = new uint8_t[groupRecordSize]; + memset(groupRecordBuffer, 0, groupRecordSize); + igmpv3_group_record* newGroupRecord = + (igmpv3_group_record*)groupRecordBuffer; + newGroupRecord->multicastAddress = multicastAddress.toInt(); + newGroupRecord->recordType = recordType; + newGroupRecord->auxDataLen = 0; + newGroupRecord->numOfSources = htobe16(sourceAddresses.size()); - int srcAddrOffset = 0; - for (std::vector::const_iterator iter = sourceAddresses.begin(); iter != sourceAddresses.end(); iter++) - { - memcpy(newGroupRecord->sourceAddresses + srcAddrOffset, iter->toBytes(), sizeof(uint32_t)); - srcAddrOffset += sizeof(uint32_t); - } + int srcAddrOffset = 0; + for (std::vector::const_iterator iter = sourceAddresses.begin(); + iter != sourceAddresses.end(); iter++) { + memcpy(newGroupRecord->sourceAddresses + srcAddrOffset, iter->toBytes(), + sizeof(uint32_t)); + srcAddrOffset += sizeof(uint32_t); + } - memcpy(m_Data + offset, groupRecordBuffer, groupRecordSize); + memcpy(m_Data + offset, groupRecordBuffer, groupRecordSize); - delete[] groupRecordBuffer; + delete[] groupRecordBuffer; - getReportHeader()->numOfGroupRecords = htobe16(getGroupRecordCount() + 1); + getReportHeader()->numOfGroupRecords = htobe16(getGroupRecordCount() + 1); - return (igmpv3_group_record*)(m_Data + offset); + return (igmpv3_group_record*)(m_Data + offset); } -igmpv3_group_record* IgmpV3ReportLayer::addGroupRecord(uint8_t recordType, const IPv4Address& multicastAddress, const std::vector& sourceAddresses) -{ - return addGroupRecordAt(recordType, multicastAddress, sourceAddresses, (int)getHeaderLen()); +igmpv3_group_record* IgmpV3ReportLayer::addGroupRecord( + uint8_t recordType, const IPv4Address& multicastAddress, + const std::vector& sourceAddresses) { + return addGroupRecordAt(recordType, multicastAddress, sourceAddresses, + (int)getHeaderLen()); } -igmpv3_group_record* IgmpV3ReportLayer::addGroupRecordAtIndex(uint8_t recordType, const IPv4Address& multicastAddress, const std::vector& sourceAddresses, int index) -{ - int groupCnt = (int)getGroupRecordCount(); +igmpv3_group_record* IgmpV3ReportLayer::addGroupRecordAtIndex( + uint8_t recordType, const IPv4Address& multicastAddress, + const std::vector& sourceAddresses, int index) { + int groupCnt = (int)getGroupRecordCount(); - if (index < 0 || index > groupCnt) - { - PCPP_LOG_ERROR("Cannot add group record, index " << index << " out of bounds"); - return nullptr; - } + if (index < 0 || index > groupCnt) { + PCPP_LOG_ERROR("Cannot add group record, index " << index + << " out of bounds"); + return nullptr; + } - size_t offset = sizeof(igmpv3_report_header); + size_t offset = sizeof(igmpv3_report_header); - igmpv3_group_record* curRecord = getFirstGroupRecord(); - for (int i = 0; i < index; i++) - { - if (curRecord == nullptr) - { - PCPP_LOG_ERROR("Cannot add group record, cannot find group record at index " << i); - return nullptr; - } + igmpv3_group_record* curRecord = getFirstGroupRecord(); + for (int i = 0; i < index; i++) { + if (curRecord == nullptr) { + PCPP_LOG_ERROR( + "Cannot add group record, cannot find group record at index " << i); + return nullptr; + } - offset += curRecord->getRecordLen(); - curRecord = getNextGroupRecord(curRecord); - } + offset += curRecord->getRecordLen(); + curRecord = getNextGroupRecord(curRecord); + } - return addGroupRecordAt(recordType, multicastAddress, sourceAddresses, (int)offset); + return addGroupRecordAt(recordType, multicastAddress, sourceAddresses, + (int)offset); } -bool IgmpV3ReportLayer::removeGroupRecordAtIndex(int index) -{ - int groupCnt = (int)getGroupRecordCount(); +bool IgmpV3ReportLayer::removeGroupRecordAtIndex(int index) { + int groupCnt = (int)getGroupRecordCount(); - if (index < 0 || index >= groupCnt) - { - PCPP_LOG_ERROR("Cannot remove group record, index " << index << " is out of bounds"); - return false; - } + if (index < 0 || index >= groupCnt) { + PCPP_LOG_ERROR("Cannot remove group record, index " << index + << " is out of bounds"); + return false; + } - size_t offset = sizeof(igmpv3_report_header); + size_t offset = sizeof(igmpv3_report_header); - igmpv3_group_record* curRecord = getFirstGroupRecord(); - for (int i = 0; i < index; i++) - { - if (curRecord == nullptr) - { - PCPP_LOG_ERROR("Cannot remove group record at index " << index << ", cannot find group record at index " << i); - return false; - } + igmpv3_group_record* curRecord = getFirstGroupRecord(); + for (int i = 0; i < index; i++) { + if (curRecord == nullptr) { + PCPP_LOG_ERROR("Cannot remove group record at index " + << index << ", cannot find group record at index " << i); + return false; + } - offset += curRecord->getRecordLen(); - curRecord = getNextGroupRecord(curRecord); - } + offset += curRecord->getRecordLen(); + curRecord = getNextGroupRecord(curRecord); + } - if (!shortenLayer((int)offset, curRecord->getRecordLen())) - { - PCPP_LOG_ERROR("Cannot remove group record at index " << index << ", cannot shorted layer"); - return false; - } + if (!shortenLayer((int)offset, curRecord->getRecordLen())) { + PCPP_LOG_ERROR("Cannot remove group record at index " + << index << ", cannot shorted layer"); + return false; + } - getReportHeader()->numOfGroupRecords = htobe16(groupCnt-1); + getReportHeader()->numOfGroupRecords = htobe16(groupCnt - 1); - return true; + return true; } -bool IgmpV3ReportLayer::removeAllGroupRecords() -{ - int offset = (int)sizeof(igmpv3_report_header); +bool IgmpV3ReportLayer::removeAllGroupRecords() { + int offset = (int)sizeof(igmpv3_report_header); - if (!shortenLayer(offset, getHeaderLen()-offset)) - { - PCPP_LOG_ERROR("Cannot remove all group records, cannot shorted layer"); - return false; - } + if (!shortenLayer(offset, getHeaderLen() - offset)) { + PCPP_LOG_ERROR("Cannot remove all group records, cannot shorted layer"); + return false; + } - getReportHeader()->numOfGroupRecords = 0; - return true; + getReportHeader()->numOfGroupRecords = 0; + return true; } - - - - - /********************* * igmpv3_group_record *********************/ -uint16_t igmpv3_group_record::getSourceAddressCount() const -{ - return be16toh(numOfSources); +uint16_t igmpv3_group_record::getSourceAddressCount() const { + return be16toh(numOfSources); } -IPv4Address igmpv3_group_record::getSourceAddressAtIndex(int index) const -{ - uint16_t numOfRecords = getSourceAddressCount(); - if (index < 0 || index >= numOfRecords) - return IPv4Address(); +IPv4Address igmpv3_group_record::getSourceAddressAtIndex(int index) const { + uint16_t numOfRecords = getSourceAddressCount(); + if (index < 0 || index >= numOfRecords) + return IPv4Address(); - int offset = index * sizeof(uint32_t); - const uint8_t* ptr = sourceAddresses + offset; - return IPv4Address(*(uint32_t*)ptr); + int offset = index * sizeof(uint32_t); + const uint8_t* ptr = sourceAddresses + offset; + return IPv4Address(*(uint32_t*)ptr); } -size_t igmpv3_group_record::getRecordLen() const -{ - uint16_t numOfRecords = getSourceAddressCount(); +size_t igmpv3_group_record::getRecordLen() const { + uint16_t numOfRecords = getSourceAddressCount(); - int headerLen = numOfRecords * sizeof(uint32_t) + sizeof(igmpv3_group_record); - return (size_t)headerLen; + int headerLen = numOfRecords * sizeof(uint32_t) + sizeof(igmpv3_group_record); + return (size_t)headerLen; } -} +} // namespace pcpp diff --git a/Packet++/src/LLCLayer.cpp b/Packet++/src/LLCLayer.cpp index a533466cae..3283af5317 100644 --- a/Packet++/src/LLCLayer.cpp +++ b/Packet++/src/LLCLayer.cpp @@ -5,51 +5,44 @@ #include "StpLayer.h" #include -namespace pcpp -{ - -LLCLayer::LLCLayer(uint8_t dsap, uint8_t ssap, uint8_t control) -{ - m_DataLen = sizeof(llc_header); - m_Data = new uint8_t[sizeof(llc_header)]; - memset(m_Data, 0, sizeof(llc_header)); - - m_Protocol = LLC; - - // Set values - llc_header* header = getLlcHeader(); - header->dsap = dsap; - header->ssap = ssap; - header->control = control; -} +namespace pcpp { + +LLCLayer::LLCLayer(uint8_t dsap, uint8_t ssap, uint8_t control) { + m_DataLen = sizeof(llc_header); + m_Data = new uint8_t[sizeof(llc_header)]; + memset(m_Data, 0, sizeof(llc_header)); -void LLCLayer::parseNextLayer() -{ - if (m_DataLen <= sizeof(llc_header)) - return; - - llc_header *hdr = getLlcHeader(); - uint8_t *payload = m_Data + sizeof(llc_header); - size_t payloadLen = m_DataLen - sizeof(llc_header); - - if (hdr->dsap == 0x42 && hdr->ssap == 0x42 && StpLayer::isDataValid(payload, payloadLen)) - { - m_NextLayer = StpLayer::parseStpLayer(payload, payloadLen, this, m_Packet); - if (!m_NextLayer) - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - return; - } - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + m_Protocol = LLC; + + // Set values + llc_header* header = getLlcHeader(); + header->dsap = dsap; + header->ssap = ssap; + header->control = control; } -std::string LLCLayer::toString() const -{ - return "Logical Link Control"; +void LLCLayer::parseNextLayer() { + if (m_DataLen <= sizeof(llc_header)) + return; + + llc_header* hdr = getLlcHeader(); + uint8_t* payload = m_Data + sizeof(llc_header); + size_t payloadLen = m_DataLen - sizeof(llc_header); + + if (hdr->dsap == 0x42 && hdr->ssap == 0x42 && + StpLayer::isDataValid(payload, payloadLen)) { + m_NextLayer = StpLayer::parseStpLayer(payload, payloadLen, this, m_Packet); + if (!m_NextLayer) + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + return; + } + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); } -bool LLCLayer::isDataValid(const uint8_t *data, size_t dataLen) -{ - return dataLen >= sizeof(llc_header) && !(data[0] == 0xFF && data[1] == 0xFF); +std::string LLCLayer::toString() const { return "Logical Link Control"; } + +bool LLCLayer::isDataValid(const uint8_t* data, size_t dataLen) { + return dataLen >= sizeof(llc_header) && !(data[0] == 0xFF && data[1] == 0xFF); } } // namespace pcpp diff --git a/Packet++/src/Layer.cpp b/Packet++/src/Layer.cpp index f2b45542fc..a1240aad5b 100644 --- a/Packet++/src/Layer.cpp +++ b/Packet++/src/Layer.cpp @@ -1,105 +1,95 @@ #define LOG_MODULE PacketLogModuleLayer #include "Layer.h" -#include #include "Logger.h" #include "Packet.h" +#include -namespace pcpp -{ +namespace pcpp { -Layer::~Layer() -{ - if (!isAllocatedToPacket()) - delete [] m_Data; +Layer::~Layer() { + if (!isAllocatedToPacket()) + delete[] m_Data; } -Layer::Layer(const Layer& other) : m_Packet(nullptr), m_Protocol(other.m_Protocol), m_NextLayer(nullptr), m_PrevLayer(nullptr), m_IsAllocatedInPacket(false) -{ - m_DataLen = other.getHeaderLen(); - m_Data = new uint8_t[other.m_DataLen]; - memcpy(m_Data, other.m_Data, other.m_DataLen); +Layer::Layer(const Layer& other) + : m_Packet(nullptr), m_Protocol(other.m_Protocol), m_NextLayer(nullptr), + m_PrevLayer(nullptr), m_IsAllocatedInPacket(false) { + m_DataLen = other.getHeaderLen(); + m_Data = new uint8_t[other.m_DataLen]; + memcpy(m_Data, other.m_Data, other.m_DataLen); } -Layer& Layer::operator=(const Layer& other) -{ - if (this == &other) - return *this; +Layer& Layer::operator=(const Layer& other) { + if (this == &other) + return *this; - if (m_Data != nullptr) - delete [] m_Data; + if (m_Data != nullptr) + delete[] m_Data; - m_DataLen = other.getHeaderLen(); - m_Packet = nullptr; - m_Protocol = other.m_Protocol; - m_NextLayer = nullptr; - m_PrevLayer = nullptr; - m_Data = new uint8_t[other.m_DataLen]; - m_IsAllocatedInPacket = false; - memcpy(m_Data, other.m_Data, other.m_DataLen); - - return *this; -} + m_DataLen = other.getHeaderLen(); + m_Packet = nullptr; + m_Protocol = other.m_Protocol; + m_NextLayer = nullptr; + m_PrevLayer = nullptr; + m_Data = new uint8_t[other.m_DataLen]; + m_IsAllocatedInPacket = false; + memcpy(m_Data, other.m_Data, other.m_DataLen); -void Layer::copyData(uint8_t* toArr) const -{ - memcpy(toArr, m_Data, m_DataLen); + return *this; } -bool Layer::extendLayer(int offsetInLayer, size_t numOfBytesToExtend) -{ - if (m_Data == nullptr) - { - PCPP_LOG_ERROR("Layer's data is NULL"); - return false; - } - - if (m_Packet == nullptr) - { - if ((size_t)offsetInLayer > m_DataLen) - { - PCPP_LOG_ERROR("Requested offset is larger than data length"); - return false; - } - - uint8_t* newData = new uint8_t[m_DataLen + numOfBytesToExtend]; - memcpy(newData, m_Data, offsetInLayer); - memcpy(newData + offsetInLayer + numOfBytesToExtend, m_Data + offsetInLayer, m_DataLen - offsetInLayer); - delete [] m_Data; - m_Data = newData; - m_DataLen += numOfBytesToExtend; - return true; - } - - return m_Packet->extendLayer(this, offsetInLayer, numOfBytesToExtend); +void Layer::copyData(uint8_t* toArr) const { memcpy(toArr, m_Data, m_DataLen); } + +bool Layer::extendLayer(int offsetInLayer, size_t numOfBytesToExtend) { + if (m_Data == nullptr) { + PCPP_LOG_ERROR("Layer's data is NULL"); + return false; + } + + if (m_Packet == nullptr) { + if ((size_t)offsetInLayer > m_DataLen) { + PCPP_LOG_ERROR("Requested offset is larger than data length"); + return false; + } + + uint8_t* newData = new uint8_t[m_DataLen + numOfBytesToExtend]; + memcpy(newData, m_Data, offsetInLayer); + memcpy(newData + offsetInLayer + numOfBytesToExtend, m_Data + offsetInLayer, + m_DataLen - offsetInLayer); + delete[] m_Data; + m_Data = newData; + m_DataLen += numOfBytesToExtend; + return true; + } + + return m_Packet->extendLayer(this, offsetInLayer, numOfBytesToExtend); } -bool Layer::shortenLayer(int offsetInLayer, size_t numOfBytesToShorten) -{ - if (m_Data == nullptr) - { - PCPP_LOG_ERROR("Layer's data is NULL"); - return false; - } - - if (m_Packet == nullptr) - { - if ((size_t)offsetInLayer >= m_DataLen) - { - PCPP_LOG_ERROR("Requested offset is larger than data length"); - return false; - } - - uint8_t* newData = new uint8_t[m_DataLen - numOfBytesToShorten]; - memcpy(newData, m_Data, offsetInLayer); - memcpy(newData + offsetInLayer, m_Data + offsetInLayer + numOfBytesToShorten, m_DataLen - offsetInLayer - numOfBytesToShorten); - delete [] m_Data; - m_Data = newData; - m_DataLen -= numOfBytesToShorten; - return true; - } - - return m_Packet->shortenLayer(this, offsetInLayer, numOfBytesToShorten); +bool Layer::shortenLayer(int offsetInLayer, size_t numOfBytesToShorten) { + if (m_Data == nullptr) { + PCPP_LOG_ERROR("Layer's data is NULL"); + return false; + } + + if (m_Packet == nullptr) { + if ((size_t)offsetInLayer >= m_DataLen) { + PCPP_LOG_ERROR("Requested offset is larger than data length"); + return false; + } + + uint8_t* newData = new uint8_t[m_DataLen - numOfBytesToShorten]; + memcpy(newData, m_Data, offsetInLayer); + memcpy(newData + offsetInLayer, + m_Data + offsetInLayer + numOfBytesToShorten, + m_DataLen - offsetInLayer - numOfBytesToShorten); + delete[] m_Data; + m_Data = newData; + m_DataLen -= numOfBytesToShorten; + return true; + } + + return m_Packet->shortenLayer(this, offsetInLayer, numOfBytesToShorten); } } // namespace pcpp diff --git a/Packet++/src/MplsLayer.cpp b/Packet++/src/MplsLayer.cpp index c497445c86..933037df9c 100644 --- a/Packet++/src/MplsLayer.cpp +++ b/Packet++/src/MplsLayer.cpp @@ -1,159 +1,153 @@ #define LOG_MODULE PacketLogModuleMplsLayer #include "MplsLayer.h" +#include "EndianPortable.h" #include "IPv4Layer.h" #include "IPv6Layer.h" -#include "PayloadLayer.h" #include "Logger.h" -#include +#include "PayloadLayer.h" #include -#include "EndianPortable.h" +#include + +namespace pcpp { -namespace pcpp -{ - -MplsLayer::MplsLayer(uint32_t mplsLabel, uint8_t ttl, uint8_t experimentalUseValue, bool bottomOfStack) -{ - const size_t headerLen = sizeof(mpls_header); - m_DataLen = headerLen; - m_Data = new uint8_t[headerLen]; - memset(m_Data, 0, headerLen); - m_Protocol = MPLS; - - setMplsLabel(mplsLabel); - setTTL(ttl); - setExperimentalUseValue(experimentalUseValue); - setBottomOfStack(bottomOfStack); +MplsLayer::MplsLayer(uint32_t mplsLabel, uint8_t ttl, + uint8_t experimentalUseValue, bool bottomOfStack) { + const size_t headerLen = sizeof(mpls_header); + m_DataLen = headerLen; + m_Data = new uint8_t[headerLen]; + memset(m_Data, 0, headerLen); + m_Protocol = MPLS; + + setMplsLabel(mplsLabel); + setTTL(ttl); + setExperimentalUseValue(experimentalUseValue); + setBottomOfStack(bottomOfStack); } -bool MplsLayer::isBottomOfStack() const -{ - return (getMplsHeader()->misc & 0x01); +bool MplsLayer::isBottomOfStack() const { + return (getMplsHeader()->misc & 0x01); } -void MplsLayer::setBottomOfStack(bool val) -{ - if (!val) - getMplsHeader()->misc &= 0xFE; - else - getMplsHeader()->misc |= 0x1; +void MplsLayer::setBottomOfStack(bool val) { + if (!val) + getMplsHeader()->misc &= 0xFE; + else + getMplsHeader()->misc |= 0x1; } -uint8_t MplsLayer::getExperimentalUseValue() const -{ - return ((getMplsHeader()->misc & 0x0E) >> 1); +uint8_t MplsLayer::getExperimentalUseValue() const { + return ((getMplsHeader()->misc & 0x0E) >> 1); } -bool MplsLayer::setExperimentalUseValue(uint8_t val) -{ - // exp value is only 3 bits - if (val > 7) - { - PCPP_LOG_ERROR("Set ExperimentalUse value got an illegal value: " << (int)val << ". Value must be lower than 8"); - return false; - } +bool MplsLayer::setExperimentalUseValue(uint8_t val) { + // exp value is only 3 bits + if (val > 7) { + PCPP_LOG_ERROR("Set ExperimentalUse value got an illegal value: " + << (int)val << ". Value must be lower than 8"); + return false; + } - mpls_header* hdr = getMplsHeader(); + mpls_header* hdr = getMplsHeader(); - // clear the 3 exp bits - hdr->misc &= 0xF1; + // clear the 3 exp bits + hdr->misc &= 0xF1; - // move the 3 bits to their place - val = val << 1; + // move the 3 bits to their place + val = val << 1; - hdr->misc |= val; + hdr->misc |= val; - return true; + return true; } -uint32_t MplsLayer::getMplsLabel() const -{ - return (htobe16(getMplsHeader()->hiLabel) << 4) | ((getMplsHeader()->misc & 0xF0) >> 4); +uint32_t MplsLayer::getMplsLabel() const { + return (htobe16(getMplsHeader()->hiLabel) << 4) | + ((getMplsHeader()->misc & 0xF0) >> 4); } -bool MplsLayer::setMplsLabel(uint32_t label) -{ - if (label > 0xFFFFF) - { - PCPP_LOG_ERROR("MPLS label mustn't exceed 20 bits which is the value 0xffff. Got a parameter with the value 0x" << std::hex << label); - return false; - } +bool MplsLayer::setMplsLabel(uint32_t label) { + if (label > 0xFFFFF) { + PCPP_LOG_ERROR("MPLS label mustn't exceed 20 bits which is the value " + "0xffff. Got a parameter with the value 0x" + << std::hex << label); + return false; + } - mpls_header* hdr = getMplsHeader(); + mpls_header* hdr = getMplsHeader(); - // clear the 4 label bits in misc field - hdr->misc &= 0x0F; + // clear the 4 label bits in misc field + hdr->misc &= 0x0F; - // take the last nibble of the label value and move this nibble to its place in misc - uint8_t miscVal = (label & 0x0F) << 4; + // take the last nibble of the label value and move this nibble to its place + // in misc + uint8_t miscVal = (label & 0x0F) << 4; - // update misc field - hdr->misc |= miscVal; + // update misc field + hdr->misc |= miscVal; - // get rid of the nibble that went to misc - label = label >> 4; + // get rid of the nibble that went to misc + label = label >> 4; - // set the high 2 bytes of the label - hdr->hiLabel = (uint16_t)htobe16(label); + // set the high 2 bytes of the label + hdr->hiLabel = (uint16_t)htobe16(label); - return true; + return true; } - -void MplsLayer::parseNextLayer() -{ - size_t headerLen = getHeaderLen(); - if (m_DataLen < headerLen + 1) - return; - - uint8_t* payload = m_Data + sizeof(mpls_header); - size_t payloadLen = m_DataLen - sizeof(mpls_header); - - if (!isBottomOfStack()) - { - m_NextLayer = new MplsLayer(payload, payloadLen, this, m_Packet); - return; - } - - uint8_t nextNibble = (*((uint8_t*)(m_Data + headerLen)) & 0xF0) >> 4; - switch (nextNibble) - { - case 4: - m_NextLayer = IPv4Layer::isDataValid(payload, payloadLen) - ? static_cast(new IPv4Layer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case 6: - m_NextLayer = IPv6Layer::isDataValid(payload, payloadLen) - ? static_cast(new IPv6Layer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - default: - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - } +void MplsLayer::parseNextLayer() { + size_t headerLen = getHeaderLen(); + if (m_DataLen < headerLen + 1) + return; + + uint8_t* payload = m_Data + sizeof(mpls_header); + size_t payloadLen = m_DataLen - sizeof(mpls_header); + + if (!isBottomOfStack()) { + m_NextLayer = new MplsLayer(payload, payloadLen, this, m_Packet); + return; + } + + uint8_t nextNibble = (*((uint8_t*)(m_Data + headerLen)) & 0xF0) >> 4; + switch (nextNibble) { + case 4: + m_NextLayer = IPv4Layer::isDataValid(payload, payloadLen) + ? static_cast( + new IPv4Layer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer( + payload, payloadLen, this, m_Packet)); + break; + case 6: + m_NextLayer = IPv6Layer::isDataValid(payload, payloadLen) + ? static_cast( + new IPv6Layer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer( + payload, payloadLen, this, m_Packet)); + break; + default: + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + } } -void MplsLayer::computeCalculateFields() -{ - Layer* nextLayer = getNextLayer(); - if (nextLayer != nullptr) - { - setBottomOfStack((nextLayer->getProtocol() != MPLS)); - } +void MplsLayer::computeCalculateFields() { + Layer* nextLayer = getNextLayer(); + if (nextLayer != nullptr) { + setBottomOfStack((nextLayer->getProtocol() != MPLS)); + } } -std::string MplsLayer::toString() const -{ - std::ostringstream labelStream; - labelStream << getMplsLabel(); - std::ostringstream expStream; - expStream << (int)getExperimentalUseValue(); - std::ostringstream ttlStream; - ttlStream << (int)getTTL(); - std::string bottomOfStack = isBottomOfStack() ? "true" : "false"; - - return "MPLS Layer, Label: " + labelStream.str() + ", Exp: " + expStream.str() + ", TTL: " + ttlStream.str() + - ", Bottom of stack: " + bottomOfStack; +std::string MplsLayer::toString() const { + std::ostringstream labelStream; + labelStream << getMplsLabel(); + std::ostringstream expStream; + expStream << (int)getExperimentalUseValue(); + std::ostringstream ttlStream; + ttlStream << (int)getTTL(); + std::string bottomOfStack = isBottomOfStack() ? "true" : "false"; + + return "MPLS Layer, Label: " + labelStream.str() + + ", Exp: " + expStream.str() + ", TTL: " + ttlStream.str() + + ", Bottom of stack: " + bottomOfStack; } } // namespace pcpp diff --git a/Packet++/src/NdpLayer.cpp b/Packet++/src/NdpLayer.cpp index 29853f1e27..7065a384e7 100644 --- a/Packet++/src/NdpLayer.cpp +++ b/Packet++/src/NdpLayer.cpp @@ -3,217 +3,219 @@ #include "NdpLayer.h" #include "Logger.h" -namespace pcpp -{ +namespace pcpp { /* * NdpOptionBuilder */ -NdpOption NdpOptionBuilder::build() const -{ - size_t optionSize = m_RecValueLen + 2 * sizeof(uint8_t); - size_t padding = (8 - (optionSize % 8)) % 8; // Padding bytes for a option with 8 byte boundary - size_t optionSizeWithPadding = optionSize + padding; +NdpOption NdpOptionBuilder::build() const { + size_t optionSize = m_RecValueLen + 2 * sizeof(uint8_t); + size_t padding = (8 - (optionSize % 8)) % + 8; // Padding bytes for a option with 8 byte boundary + size_t optionSizeWithPadding = optionSize + padding; - uint8_t *recordBuffer = new uint8_t[optionSizeWithPadding]; - memset(recordBuffer, 0, optionSizeWithPadding); - recordBuffer[0] = static_cast(m_RecType); - recordBuffer[1] = static_cast(optionSizeWithPadding / 8); // length value is stored in units of 8 octets - memcpy(recordBuffer + 2, m_RecValue, m_RecValueLen); + uint8_t* recordBuffer = new uint8_t[optionSizeWithPadding]; + memset(recordBuffer, 0, optionSizeWithPadding); + recordBuffer[0] = static_cast(m_RecType); + recordBuffer[1] = static_cast( + optionSizeWithPadding / 8); // length value is stored in units of 8 octets + memcpy(recordBuffer + 2, m_RecValue, m_RecValueLen); - return NdpOption(recordBuffer); + return NdpOption(recordBuffer); } /* * NDPLayerBase */ -size_t NDPLayerBase::getNdpOptionCount() const -{ - return m_OptionReader.getTLVRecordCount(getNdpOptionsBasePtr(), getHeaderLen() - getNdpHeaderLen()); +size_t NDPLayerBase::getNdpOptionCount() const { + return m_OptionReader.getTLVRecordCount(getNdpOptionsBasePtr(), + getHeaderLen() - getNdpHeaderLen()); } -NdpOption NDPLayerBase::getFirstNdpOption() const -{ - return m_OptionReader.getFirstTLVRecord(getNdpOptionsBasePtr(), getHeaderLen() - getNdpHeaderLen()); +NdpOption NDPLayerBase::getFirstNdpOption() const { + return m_OptionReader.getFirstTLVRecord(getNdpOptionsBasePtr(), + getHeaderLen() - getNdpHeaderLen()); } -NdpOption NDPLayerBase::getNextNdpOption(NdpOption &option) const -{ - return m_OptionReader.getNextTLVRecord(option, getNdpOptionsBasePtr(), getHeaderLen() - getNdpHeaderLen()); +NdpOption NDPLayerBase::getNextNdpOption(NdpOption& option) const { + return m_OptionReader.getNextTLVRecord(option, getNdpOptionsBasePtr(), + getHeaderLen() - getNdpHeaderLen()); } -NdpOption NDPLayerBase::getNdpOption(NDPNeighborOptionTypes option) const -{ - return m_OptionReader.getTLVRecord((uint8_t)option, getNdpOptionsBasePtr(), getHeaderLen() - getNdpHeaderLen()); +NdpOption NDPLayerBase::getNdpOption(NDPNeighborOptionTypes option) const { + return m_OptionReader.getTLVRecord((uint8_t)option, getNdpOptionsBasePtr(), + getHeaderLen() - getNdpHeaderLen()); } -NdpOption NDPLayerBase::addNdpOption(const NdpOptionBuilder &optionBuilder) -{ - return addNdpOptionAt(optionBuilder, getHeaderLen()); +NdpOption NDPLayerBase::addNdpOption(const NdpOptionBuilder& optionBuilder) { + return addNdpOptionAt(optionBuilder, getHeaderLen()); } -NdpOption NDPLayerBase::addNdpOptionAt(const NdpOptionBuilder &optionBuilder, int offset) -{ - NdpOption newOption = optionBuilder.build(); +NdpOption NDPLayerBase::addNdpOptionAt(const NdpOptionBuilder& optionBuilder, + int offset) { + NdpOption newOption = optionBuilder.build(); - if (newOption.isNull()) - { - PCPP_LOG_ERROR("Cannot build new option of type " << (int)newOption.getType()); - return newOption; - } + if (newOption.isNull()) { + PCPP_LOG_ERROR("Cannot build new option of type " + << (int)newOption.getType()); + return newOption; + } - size_t sizeToExtend = newOption.getTotalSize(); + size_t sizeToExtend = newOption.getTotalSize(); - if (!extendLayer(offset, sizeToExtend)) - { - PCPP_LOG_ERROR("Could not extend NdpLayer in [" << sizeToExtend << "] bytes"); - newOption.purgeRecordData(); - return NdpOption(nullptr); - } + if (!extendLayer(offset, sizeToExtend)) { + PCPP_LOG_ERROR("Could not extend NdpLayer in [" << sizeToExtend + << "] bytes"); + newOption.purgeRecordData(); + return NdpOption(nullptr); + } - memcpy(m_Data + offset, newOption.getRecordBasePtr(), newOption.getTotalSize()); + memcpy(m_Data + offset, newOption.getRecordBasePtr(), + newOption.getTotalSize()); - newOption.purgeRecordData(); + newOption.purgeRecordData(); - m_OptionReader.changeTLVRecordCount(1); + m_OptionReader.changeTLVRecordCount(1); - uint8_t *newOptPtr = m_Data + offset; + uint8_t* newOptPtr = m_Data + offset; - return NdpOption(newOptPtr); + return NdpOption(newOptPtr); } -bool NDPLayerBase::removeAllNdpOptions() -{ - int offset = getNdpHeaderLen(); - if (!shortenLayer(offset, getHeaderLen() - offset)) - return false; +bool NDPLayerBase::removeAllNdpOptions() { + int offset = getNdpHeaderLen(); + if (!shortenLayer(offset, getHeaderLen() - offset)) + return false; - m_OptionReader.changeTLVRecordCount(0 - getNdpOptionCount()); - return true; + m_OptionReader.changeTLVRecordCount(0 - getNdpOptionCount()); + return true; } /* * NDPNeighborSolicitationLayer */ -NDPNeighborSolicitationLayer::NDPNeighborSolicitationLayer(uint8_t code, const IPv6Address &targetIP) -{ - initLayer(code, targetIP); +NDPNeighborSolicitationLayer::NDPNeighborSolicitationLayer( + uint8_t code, const IPv6Address& targetIP) { + initLayer(code, targetIP); } -NDPNeighborSolicitationLayer::NDPNeighborSolicitationLayer(uint8_t code, const IPv6Address &targetIP, - const MacAddress &srcMac) -{ - initLayer(code, targetIP); - this->addNdpOption( - pcpp::NdpOptionBuilder(pcpp::NDPNeighborOptionTypes::NDP_OPTION_SOURCE_LINK_LAYER, srcMac.getRawData(), 6)); +NDPNeighborSolicitationLayer::NDPNeighborSolicitationLayer( + uint8_t code, const IPv6Address& targetIP, const MacAddress& srcMac) { + initLayer(code, targetIP); + this->addNdpOption(pcpp::NdpOptionBuilder( + pcpp::NDPNeighborOptionTypes::NDP_OPTION_SOURCE_LINK_LAYER, + srcMac.getRawData(), 6)); } -void NDPNeighborSolicitationLayer::initLayer(uint8_t code, const IPv6Address &targetIP) -{ - m_DataLen = sizeof(ndpneighborsolicitationhdr); - m_Data = new uint8_t[m_DataLen]; - memset(m_Data, 0, m_DataLen); - m_Protocol = ICMPv6; +void NDPNeighborSolicitationLayer::initLayer(uint8_t code, + const IPv6Address& targetIP) { + m_DataLen = sizeof(ndpneighborsolicitationhdr); + m_Data = new uint8_t[m_DataLen]; + memset(m_Data, 0, m_DataLen); + m_Protocol = ICMPv6; - ndpneighborsolicitationhdr *pHdr = getNdpHeader(); - pHdr->type = static_cast(ICMPv6MessageType::ICMPv6_NEIGHBOR_SOLICITATION); - pHdr->code = code; - memcpy(pHdr->targetIP, targetIP.toBytes(), 16); + ndpneighborsolicitationhdr* pHdr = getNdpHeader(); + pHdr->type = + static_cast(ICMPv6MessageType::ICMPv6_NEIGHBOR_SOLICITATION); + pHdr->code = code; + memcpy(pHdr->targetIP, targetIP.toBytes(), 16); } -bool NDPNeighborSolicitationLayer::hasLinkLayerAddress() const -{ - NdpOption option = this->getNdpOption(NDPNeighborOptionTypes::NDP_OPTION_SOURCE_LINK_LAYER); - return option.isNull() ? false : true; +bool NDPNeighborSolicitationLayer::hasLinkLayerAddress() const { + NdpOption option = + this->getNdpOption(NDPNeighborOptionTypes::NDP_OPTION_SOURCE_LINK_LAYER); + return option.isNull() ? false : true; } -MacAddress NDPNeighborSolicitationLayer::getLinkLayerAddress() const -{ - NdpOption option = this->getNdpOption(NDPNeighborOptionTypes::NDP_OPTION_SOURCE_LINK_LAYER); +MacAddress NDPNeighborSolicitationLayer::getLinkLayerAddress() const { + NdpOption option = + this->getNdpOption(NDPNeighborOptionTypes::NDP_OPTION_SOURCE_LINK_LAYER); - if (option.isNull()) - { - return MacAddress::Zero; - } + if (option.isNull()) { + return MacAddress::Zero; + } - return MacAddress(option.getValue()); + return MacAddress(option.getValue()); } -std::string NDPNeighborSolicitationLayer::toString() const -{ - std::ostringstream typeStream; - typeStream << "ICMPv6 Layer, Neighbor Solicitation Message, TargetIP: " + getTargetIP().toString(); - hasLinkLayerAddress() ? typeStream << ", SourceMAC: " + getLinkLayerAddress().toString() : typeStream << ", no Option"; - - return typeStream.str(); +std::string NDPNeighborSolicitationLayer::toString() const { + std::ostringstream typeStream; + typeStream << "ICMPv6 Layer, Neighbor Solicitation Message, TargetIP: " + + getTargetIP().toString(); + hasLinkLayerAddress() + ? typeStream << ", SourceMAC: " + getLinkLayerAddress().toString() + : typeStream << ", no Option"; + return typeStream.str(); } /* * NDPNeighborAdvertisementLayer */ -NDPNeighborAdvertisementLayer::NDPNeighborAdvertisementLayer(uint8_t code, const IPv6Address &targetIP, - const MacAddress &targetMac, bool routerFlag, - bool unicastFlag, bool overrideFlag) -{ - initLayer(code, targetIP, routerFlag, unicastFlag, overrideFlag); - this->addNdpOption( - pcpp::NdpOptionBuilder(pcpp::NDPNeighborOptionTypes::NDP_OPTION_TARGET_LINK_LAYER, targetMac.getRawData(), 6)); +NDPNeighborAdvertisementLayer::NDPNeighborAdvertisementLayer( + uint8_t code, const IPv6Address& targetIP, const MacAddress& targetMac, + bool routerFlag, bool unicastFlag, bool overrideFlag) { + initLayer(code, targetIP, routerFlag, unicastFlag, overrideFlag); + this->addNdpOption(pcpp::NdpOptionBuilder( + pcpp::NDPNeighborOptionTypes::NDP_OPTION_TARGET_LINK_LAYER, + targetMac.getRawData(), 6)); } -NDPNeighborAdvertisementLayer::NDPNeighborAdvertisementLayer(uint8_t code, const IPv6Address &targetIP, bool routerFlag, - bool unicastFlag, bool overrideFlag) -{ - initLayer(code, targetIP, routerFlag, unicastFlag, overrideFlag); +NDPNeighborAdvertisementLayer::NDPNeighborAdvertisementLayer( + uint8_t code, const IPv6Address& targetIP, bool routerFlag, + bool unicastFlag, bool overrideFlag) { + initLayer(code, targetIP, routerFlag, unicastFlag, overrideFlag); } -void NDPNeighborAdvertisementLayer::initLayer(uint8_t code, const IPv6Address &targetIP, bool routerFlag, - bool unicastFlag, bool overrideFlag) -{ - m_DataLen = sizeof(ndpneighboradvertisementhdr); - m_Data = new uint8_t[m_DataLen]; - memset(m_Data, 0, m_DataLen); - m_Protocol = ICMPv6; +void NDPNeighborAdvertisementLayer::initLayer(uint8_t code, + const IPv6Address& targetIP, + bool routerFlag, bool unicastFlag, + bool overrideFlag) { + m_DataLen = sizeof(ndpneighboradvertisementhdr); + m_Data = new uint8_t[m_DataLen]; + memset(m_Data, 0, m_DataLen); + m_Protocol = ICMPv6; - ndpneighboradvertisementhdr *pHdr = getNdpHeader(); - pHdr->type = static_cast(ICMPv6MessageType::ICMPv6_NEIGHBOR_ADVERTISEMENT); - pHdr->code = code; - pHdr->router = routerFlag; - pHdr->solicited = unicastFlag; - pHdr->override = overrideFlag; + ndpneighboradvertisementhdr* pHdr = getNdpHeader(); + pHdr->type = + static_cast(ICMPv6MessageType::ICMPv6_NEIGHBOR_ADVERTISEMENT); + pHdr->code = code; + pHdr->router = routerFlag; + pHdr->solicited = unicastFlag; + pHdr->override = overrideFlag; - memcpy(pHdr->targetIP, targetIP.toBytes(), 16); + memcpy(pHdr->targetIP, targetIP.toBytes(), 16); } -bool NDPNeighborAdvertisementLayer::hasTargetMacInfo() const -{ - NdpOption option = this->getNdpOption(NDPNeighborOptionTypes::NDP_OPTION_TARGET_LINK_LAYER); - return option.isNull() ? false : true; +bool NDPNeighborAdvertisementLayer::hasTargetMacInfo() const { + NdpOption option = + this->getNdpOption(NDPNeighborOptionTypes::NDP_OPTION_TARGET_LINK_LAYER); + return option.isNull() ? false : true; } -MacAddress NDPNeighborAdvertisementLayer::getTargetMac() const -{ - NdpOption option = this->getNdpOption(NDPNeighborOptionTypes::NDP_OPTION_TARGET_LINK_LAYER); +MacAddress NDPNeighborAdvertisementLayer::getTargetMac() const { + NdpOption option = + this->getNdpOption(NDPNeighborOptionTypes::NDP_OPTION_TARGET_LINK_LAYER); - if (option.isNull()) - { - return MacAddress::Zero; - } + if (option.isNull()) { + return MacAddress::Zero; + } - return MacAddress(option.getValue()); + return MacAddress(option.getValue()); } -std::string NDPNeighborAdvertisementLayer::toString() const -{ - std::ostringstream typeStream; - typeStream << "ICMPv6 Layer, Neighbor Advertisement Message, TargetIP: " << getTargetIP().toString(); - hasTargetMacInfo() ? typeStream << ", TargetMAC: " + getTargetMac().toString() : typeStream << ", no Option"; +std::string NDPNeighborAdvertisementLayer::toString() const { + std::ostringstream typeStream; + typeStream << "ICMPv6 Layer, Neighbor Advertisement Message, TargetIP: " + << getTargetIP().toString(); + hasTargetMacInfo() ? typeStream << ", TargetMAC: " + getTargetMac().toString() + : typeStream << ", no Option"; - return typeStream.str(); + return typeStream.str(); } } // namespace pcpp diff --git a/Packet++/src/NflogLayer.cpp b/Packet++/src/NflogLayer.cpp index c7a067bd86..cf11c7957a 100644 --- a/Packet++/src/NflogLayer.cpp +++ b/Packet++/src/NflogLayer.cpp @@ -1,110 +1,96 @@ #define LOG_MODULE PacketLogModuleNflogLayer #include "NflogLayer.h" +#include "EndianPortable.h" +#include "GeneralUtils.h" #include "IPv4Layer.h" #include "IPv6Layer.h" #include "PayloadLayer.h" -#include "GeneralUtils.h" -#include "EndianPortable.h" #include +namespace pcpp { +/** IPv4 protocol */ +#define PCPP_WS_NFPROTO_IPV4 2 +/** IPv6 protocol */ +#define PCPP_WS_NFPROTO_IPV6 10 -namespace pcpp -{ - /** IPv4 protocol */ - #define PCPP_WS_NFPROTO_IPV4 2 - /** IPv6 protocol */ - #define PCPP_WS_NFPROTO_IPV6 10 - +uint8_t NflogLayer::getFamily() { return getNflogHeader()->addressFamily; } -uint8_t NflogLayer::getFamily() -{ - return getNflogHeader()->addressFamily; -} - -uint8_t NflogLayer::getVersion() -{ - return getNflogHeader()->version; -} +uint8_t NflogLayer::getVersion() { return getNflogHeader()->version; } -uint16_t NflogLayer::getResourceId() -{ - return be16toh(getNflogHeader()->resourceId); +uint16_t NflogLayer::getResourceId() { + return be16toh(getNflogHeader()->resourceId); } -NflogTlv NflogLayer::getTlvByType(NflogTlvType type) const -{ - NflogTlv tlv = m_TlvReader.getTLVRecord( - static_cast (type), - getTlvsBasePtr(), - m_DataLen - sizeof(nflog_header)); +NflogTlv NflogLayer::getTlvByType(NflogTlvType type) const { + NflogTlv tlv = + m_TlvReader.getTLVRecord(static_cast(type), getTlvsBasePtr(), + m_DataLen - sizeof(nflog_header)); - return tlv; + return tlv; } -void NflogLayer::parseNextLayer() -{ - if (m_DataLen <= sizeof(nflog_header)) - return; - auto payloadInfo = getTlvByType(NflogTlvType::NFULA_PAYLOAD); - if (payloadInfo.isNull()) - { - return; - } - - uint8_t* payload = payloadInfo.getValue(); - size_t payloadLen = payloadInfo.getTotalSize() - sizeof(uint16_t) * 2; - - uint8_t family = getFamily(); - - switch (family) - { - case PCPP_WS_NFPROTO_IPV4: - m_NextLayer = IPv4Layer::isDataValid(payload, payloadLen) - ? static_cast(new IPv4Layer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case PCPP_WS_NFPROTO_IPV6: - m_NextLayer = IPv6Layer::isDataValid(payload, payloadLen) - ? static_cast(new IPv6Layer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - default: - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - } - +void NflogLayer::parseNextLayer() { + if (m_DataLen <= sizeof(nflog_header)) + return; + auto payloadInfo = getTlvByType(NflogTlvType::NFULA_PAYLOAD); + if (payloadInfo.isNull()) { + return; + } + + uint8_t* payload = payloadInfo.getValue(); + size_t payloadLen = payloadInfo.getTotalSize() - sizeof(uint16_t) * 2; + + uint8_t family = getFamily(); + + switch (family) { + case PCPP_WS_NFPROTO_IPV4: + m_NextLayer = IPv4Layer::isDataValid(payload, payloadLen) + ? static_cast( + new IPv4Layer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer( + payload, payloadLen, this, m_Packet)); + break; + case PCPP_WS_NFPROTO_IPV6: + m_NextLayer = IPv6Layer::isDataValid(payload, payloadLen) + ? static_cast( + new IPv6Layer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer( + payload, payloadLen, this, m_Packet)); + break; + default: + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + } } -size_t NflogLayer::getHeaderLen() const -{ - size_t headerLen = sizeof(nflog_header); - NflogTlv currentTLV = m_TlvReader.getFirstTLVRecord( - getTlvsBasePtr(), - m_DataLen - sizeof(nflog_header)); - - while (!currentTLV.isNull() && currentTLV.getType() != static_cast (NflogTlvType::NFULA_PAYLOAD)) - { - headerLen += currentTLV.getTotalSize(); - currentTLV = m_TlvReader.getNextTLVRecord(currentTLV, getTlvsBasePtr(), m_DataLen - sizeof(nflog_header)); - } - if (!currentTLV.isNull() && currentTLV.getType() == static_cast (NflogTlvType::NFULA_PAYLOAD)) - { - // for the length and type of the payload TLV - headerLen += 2 * sizeof (uint16_t); - } - // nflog_header has not a form of TLV and contains 3 fields (family, resource_id, version) - return headerLen; +size_t NflogLayer::getHeaderLen() const { + size_t headerLen = sizeof(nflog_header); + NflogTlv currentTLV = m_TlvReader.getFirstTLVRecord( + getTlvsBasePtr(), m_DataLen - sizeof(nflog_header)); + + while (!currentTLV.isNull() && + currentTLV.getType() != + static_cast(NflogTlvType::NFULA_PAYLOAD)) { + headerLen += currentTLV.getTotalSize(); + currentTLV = m_TlvReader.getNextTLVRecord(currentTLV, getTlvsBasePtr(), + m_DataLen - sizeof(nflog_header)); + } + if (!currentTLV.isNull() && + currentTLV.getType() == + static_cast(NflogTlvType::NFULA_PAYLOAD)) { + // for the length and type of the payload TLV + headerLen += 2 * sizeof(uint16_t); + } + // nflog_header has not a form of TLV and contains 3 fields (family, + // resource_id, version) + return headerLen; } -std::string NflogLayer::toString() const -{ - return "Linux Netfilter NFLOG"; -} +std::string NflogLayer::toString() const { return "Linux Netfilter NFLOG"; } -bool NflogLayer::isDataValid(const uint8_t* data, size_t dataLen) -{ - return data && dataLen >= sizeof(nflog_header); +bool NflogLayer::isDataValid(const uint8_t* data, size_t dataLen) { + return data && dataLen >= sizeof(nflog_header); } } // namespace pcpp diff --git a/Packet++/src/NtpLayer.cpp b/Packet++/src/NtpLayer.cpp index 6c135852e5..f2c516dc88 100644 --- a/Packet++/src/NtpLayer.cpp +++ b/Packet++/src/NtpLayer.cpp @@ -1,9 +1,9 @@ #define LOG_MODULE PacketLogModuleNtpLayer -#include "Logger.h" #include "NtpLayer.h" -#include "SystemUtils.h" #include "GeneralUtils.h" +#include "Logger.h" +#include "SystemUtils.h" #include #include @@ -15,611 +15,520 @@ /// Epoch offset between Unix time and NTP #define EPOCH_OFFSET 2208988800ULL -namespace pcpp -{ - NtpLayer::NtpLayer() - { - m_DataLen = sizeof(ntp_header); - m_Data = new uint8_t[sizeof(ntp_header)]; - memset(m_Data, 0, sizeof(ntp_header)); - m_Protocol = NTP; - } +namespace pcpp { +NtpLayer::NtpLayer() { + m_DataLen = sizeof(ntp_header); + m_Data = new uint8_t[sizeof(ntp_header)]; + memset(m_Data, 0, sizeof(ntp_header)); + m_Protocol = NTP; +} - NtpLayer::LeapIndicator NtpLayer::getLeapIndicator() const - { - if (getNtpHeader()->leapIndicator < 4) // Since leap indicator field is 2bit - return static_cast(getNtpHeader()->leapIndicator); - PCPP_LOG_ERROR("Unknown NTP Leap Indicator"); - return Unknown; - } +NtpLayer::LeapIndicator NtpLayer::getLeapIndicator() const { + if (getNtpHeader()->leapIndicator < 4) // Since leap indicator field is 2bit + return static_cast(getNtpHeader()->leapIndicator); + PCPP_LOG_ERROR("Unknown NTP Leap Indicator"); + return Unknown; +} - void NtpLayer::setLeapIndicator(LeapIndicator val) - { - getNtpHeader()->leapIndicator = val; - } +void NtpLayer::setLeapIndicator(LeapIndicator val) { + getNtpHeader()->leapIndicator = val; +} - uint8_t NtpLayer::getVersion() const - { - return getNtpHeader()->version; - } +uint8_t NtpLayer::getVersion() const { return getNtpHeader()->version; } - void NtpLayer::setVersion(uint8_t val) - { - getNtpHeader()->version = val; - } +void NtpLayer::setVersion(uint8_t val) { getNtpHeader()->version = val; } - NtpLayer::Mode NtpLayer::getMode() const - { - if (getNtpHeader()->mode < 8) // Since mode field 3bit - return static_cast(getNtpHeader()->mode); - PCPP_LOG_ERROR("Unknown NTP Mode"); - return Reserved; - } +NtpLayer::Mode NtpLayer::getMode() const { + if (getNtpHeader()->mode < 8) // Since mode field 3bit + return static_cast(getNtpHeader()->mode); + PCPP_LOG_ERROR("Unknown NTP Mode"); + return Reserved; +} - std::string NtpLayer::getModeString() const - { - switch (getMode()) - { - case Reserved: - return "Reserved"; - case SymActive: - return "Symmetrically Active"; - case SymPassive: - return "Symmetrically Passive"; - case Client: - return "Client"; - case Server: - return "Server"; - case Broadcast: - return "Broadcast"; - case Control: - return "Control"; - case PrivateUse: - return "Private Use"; - default: - PCPP_LOG_ERROR("Unknown NTP Mode"); - return std::string(); - } +std::string NtpLayer::getModeString() const { + switch (getMode()) { + case Reserved: + return "Reserved"; + case SymActive: + return "Symmetrically Active"; + case SymPassive: + return "Symmetrically Passive"; + case Client: + return "Client"; + case Server: + return "Server"; + case Broadcast: + return "Broadcast"; + case Control: + return "Control"; + case PrivateUse: + return "Private Use"; + default: + PCPP_LOG_ERROR("Unknown NTP Mode"); + return std::string(); } +} - void NtpLayer::setMode(Mode val) - { - getNtpHeader()->mode = val; - } +void NtpLayer::setMode(Mode val) { getNtpHeader()->mode = val; } - uint8_t NtpLayer::getStratum() const - { - return getNtpHeader()->stratum; - } +uint8_t NtpLayer::getStratum() const { return getNtpHeader()->stratum; } - void NtpLayer::setStratum(uint8_t val) - { - getNtpHeader()->stratum = val; - } +void NtpLayer::setStratum(uint8_t val) { getNtpHeader()->stratum = val; } - int8_t NtpLayer::getPollInterval() const - { - return getNtpHeader()->pollInterval; - } +int8_t NtpLayer::getPollInterval() const { + return getNtpHeader()->pollInterval; +} - void NtpLayer::setPollInterval(int8_t val) - { - getNtpHeader()->pollInterval = val; - } +void NtpLayer::setPollInterval(int8_t val) { + getNtpHeader()->pollInterval = val; +} - double NtpLayer::getPollIntervalInSecs() const - { - return pow(2, getPollInterval()); - } +double NtpLayer::getPollIntervalInSecs() const { + return pow(2, getPollInterval()); +} - int8_t NtpLayer::getPrecision() const - { - return getNtpHeader()->precision; - } +int8_t NtpLayer::getPrecision() const { return getNtpHeader()->precision; } - void NtpLayer::setPrecision(int8_t val) - { - getNtpHeader()->precision = val; - } +void NtpLayer::setPrecision(int8_t val) { getNtpHeader()->precision = val; } - double NtpLayer::getPrecisionInSecs() const - { - return pow(2, getPrecision()); - } +double NtpLayer::getPrecisionInSecs() const { return pow(2, getPrecision()); } - uint32_t NtpLayer::getRootDelay() const - { - return getNtpHeader()->rootDelay; - } +uint32_t NtpLayer::getRootDelay() const { return getNtpHeader()->rootDelay; } - void NtpLayer::setRootDelay(uint32_t val) - { - getNtpHeader()->rootDelay = val; - } +void NtpLayer::setRootDelay(uint32_t val) { getNtpHeader()->rootDelay = val; } - double NtpLayer::getRootDelayInSecs() const - { - return convertFromShortFormat(getRootDelay()); - } +double NtpLayer::getRootDelayInSecs() const { + return convertFromShortFormat(getRootDelay()); +} - void NtpLayer::setRootDelayInSecs(double val) - { - getNtpHeader()->rootDelay = convertToShortFormat(val); - } +void NtpLayer::setRootDelayInSecs(double val) { + getNtpHeader()->rootDelay = convertToShortFormat(val); +} - uint32_t NtpLayer::getRootDispersion() const - { - return getNtpHeader()->rootDispersion; - } +uint32_t NtpLayer::getRootDispersion() const { + return getNtpHeader()->rootDispersion; +} - void NtpLayer::setRootDispersion(uint32_t val) - { - getNtpHeader()->rootDispersion = val; - } +void NtpLayer::setRootDispersion(uint32_t val) { + getNtpHeader()->rootDispersion = val; +} - double NtpLayer::getRootDispersionInSecs() const - { - return convertFromShortFormat(getRootDispersion()); - } +double NtpLayer::getRootDispersionInSecs() const { + return convertFromShortFormat(getRootDispersion()); +} - void NtpLayer::setRootDispersionInSecs(double val) - { - getNtpHeader()->rootDispersion = convertToShortFormat(val); - } +void NtpLayer::setRootDispersionInSecs(double val) { + getNtpHeader()->rootDispersion = convertToShortFormat(val); +} - uint32_t NtpLayer::getReferenceIdentifier() const - { - return getNtpHeader()->referenceIdentifier; - } +uint32_t NtpLayer::getReferenceIdentifier() const { + return getNtpHeader()->referenceIdentifier; +} - void NtpLayer::setReferenceIdentifier(IPv4Address val) - { - getNtpHeader()->referenceIdentifier = val.toInt(); - } +void NtpLayer::setReferenceIdentifier(IPv4Address val) { + getNtpHeader()->referenceIdentifier = val.toInt(); +} - void NtpLayer::setReferenceIdentifier(ClockSource val) - { - getNtpHeader()->referenceIdentifier = static_cast(val); - } +void NtpLayer::setReferenceIdentifier(ClockSource val) { + getNtpHeader()->referenceIdentifier = static_cast(val); +} - void NtpLayer::setReferenceIdentifier(KissODeath val) - { - getNtpHeader()->referenceIdentifier = static_cast(val); - } +void NtpLayer::setReferenceIdentifier(KissODeath val) { + getNtpHeader()->referenceIdentifier = static_cast(val); +} - std::string NtpLayer::getReferenceIdentifierString() const - { - uint8_t stratum = getStratum(); - uint8_t version = getVersion(); - uint32_t refID = getReferenceIdentifier(); - - if (stratum == 0) - { - switch (version) - { - case 3: - { - switch (static_cast(refID)) - { - case ClockSource::DCN: - return "DCN routing protocol"; - case ClockSource::NIST: - return "NIST public modem"; - case ClockSource::TSP: - return "TSP time protocol"; - case ClockSource::DTS: - return "Digital Time Service"; - default: - return "Unknown"; - } +std::string NtpLayer::getReferenceIdentifierString() const { + uint8_t stratum = getStratum(); + uint8_t version = getVersion(); + uint32_t refID = getReferenceIdentifier(); + + if (stratum == 0) { + switch (version) { + case 3: { + switch (static_cast(refID)) { + case ClockSource::DCN: + return "DCN routing protocol"; + case ClockSource::NIST: + return "NIST public modem"; + case ClockSource::TSP: + return "TSP time protocol"; + case ClockSource::DTS: + return "Digital Time Service"; + default: + return "Unknown"; } - case 4: - { - switch (static_cast(refID)) - { - case KissODeath::ACST: - return "The association belongs to a anycast server"; - case KissODeath::AUTH: - return "Server authentication failed"; - case KissODeath::AUTO: - return "Autokey sequence failed"; - case KissODeath::BCST: - return "The association belongs to a broadcast server"; - case KissODeath::CRYP: - return "Cryptographic authentication or identification failed"; - case KissODeath::DENY: - return "Access denied by remote server"; - case KissODeath::DROP: - return "Lost peer in symmetric mode"; - case KissODeath::RSTR: - return "Access denied due to local policy"; - case KissODeath::INIT: - return "The association has not yet synchronized for the first time"; - case KissODeath::MCST: - return "The association belongs to a manycast server"; - case KissODeath::NKEY: - return "No key found. Either the key was never installed or is not trusted"; - case KissODeath::RATE: - return "Rate exceeded. The server has temporarily denied access because the client exceeded the rate " - "threshold"; - case KissODeath::RMOT: - return "Somebody is tinkering with the association from a remote host running ntpdc. Not to worry " - "unless some rascal has stolen your keys"; - case KissODeath::STEP: - return "A step change in system time has occurred, but the association has not yet resynchronized"; - default: - { - char arrBuff[5] = {static_cast((refID >> 24) & 0xFF), static_cast((refID >> 16) & 0xFF), - static_cast((refID >> 8) & 0xFF), static_cast((refID) & 0xFF), '\0'}; - return arrBuff; - } - } + } + case 4: { + switch (static_cast(refID)) { + case KissODeath::ACST: + return "The association belongs to a anycast server"; + case KissODeath::AUTH: + return "Server authentication failed"; + case KissODeath::AUTO: + return "Autokey sequence failed"; + case KissODeath::BCST: + return "The association belongs to a broadcast server"; + case KissODeath::CRYP: + return "Cryptographic authentication or identification failed"; + case KissODeath::DENY: + return "Access denied by remote server"; + case KissODeath::DROP: + return "Lost peer in symmetric mode"; + case KissODeath::RSTR: + return "Access denied due to local policy"; + case KissODeath::INIT: + return "The association has not yet synchronized for the first time"; + case KissODeath::MCST: + return "The association belongs to a manycast server"; + case KissODeath::NKEY: + return "No key found. Either the key was never installed or is not " + "trusted"; + case KissODeath::RATE: + return "Rate exceeded. The server has temporarily denied access " + "because the client exceeded the rate " + "threshold"; + case KissODeath::RMOT: + return "Somebody is tinkering with the association from a remote host " + "running ntpdc. Not to worry " + "unless some rascal has stolen your keys"; + case KissODeath::STEP: + return "A step change in system time has occurred, but the association " + "has not yet resynchronized"; + default: { + char arrBuff[5] = {static_cast((refID >> 24) & 0xFF), + static_cast((refID >> 16) & 0xFF), + static_cast((refID >> 8) & 0xFF), + static_cast((refID)&0xFF), '\0'}; + return arrBuff; } } } - else if (stratum == 1) - { - switch (version) - { - case 3: - { - switch (static_cast(refID)) - { - case ClockSource::ATOM: - return "Atomic clock"; - case ClockSource::VLF: - return "VLF radio"; - case ClockSource::LORC: - return "LORAN-C radionavigation"; - case ClockSource::GOES: - return "GOES UHF environment satellite"; - case ClockSource::GPS: - return "GPS UHF satellite positioning"; - default: - return "Unknown"; - } - } - case 4: - { - switch (static_cast(refID)) - { - case ClockSource::GOES: - return "Geosynchronous Orbit Environment Satellite"; - case ClockSource::GPS: - return "Global Position System"; - case ClockSource::GAL: - return "Galileo Positioning System"; - case ClockSource::PPS: - return "Generic pulse-per-second"; - case ClockSource::IRIG: - return "Inter-Range Instrumentation Group"; - case ClockSource::WWVB: - return "LF Radio WWVB Ft. Collins, CO 60 kHz"; - case ClockSource::DCF: - return "LF Radio DCF77 Mainflingen, DE 77.5 kHz"; - case ClockSource::HBG: - return "LF Radio HBG Prangins, HB 75 kHz"; - case ClockSource::MSF: - return "LF Radio MSF Anthorn, UK 60 kHz"; - case ClockSource::JJY: - return "LF Radio JJY Fukushima, JP 40 kHz, Saga, JP 60 kHz"; - case ClockSource::LORC: - return "MF Radio LORAN C station, 100 kHz"; - case ClockSource::TDF: - return "MF Radio Allouis, FR 162 kHz"; - case ClockSource::CHU: - return "HF Radio CHU Ottawa, Ontario"; - case ClockSource::WWV: - return "HF Radio WWV Ft. Collins, CO"; - case ClockSource::WWVH: - return "HF Radio WWVH Kauai, HI"; - case ClockSource::NIST: - return "NIST telephone modem"; - case ClockSource::ACTS: - return "NIST telephone modem"; - case ClockSource::USNO: - return "USNO telephone modem"; - case ClockSource::PTB: - return "European telephone modem"; - case ClockSource::MRS: - return "Multi Reference Sources"; - case ClockSource::XFAC: - return "Inter Face Association Changed"; - case ClockSource::STEP: - return "Step time change"; - case ClockSource::GOOG: - return "Google NTP servers"; - case ClockSource::DCFa: - return "Meinberg DCF77 with amplitude modulation"; - case ClockSource::DCFp: - return "Meinberg DCF77 with phase modulation)/pseudo random phase modulation"; - case ClockSource::GPSs: - return "Meinberg GPS (with shared memory access)"; - case ClockSource::GPSi: - return "Meinberg GPS (with interrupt based access)"; - case ClockSource::GLNs: - return "Meinberg GPS/GLONASS (with shared memory access)"; - case ClockSource::GLNi: - return "Meinberg GPS/GLONASS (with interrupt based access)"; - case ClockSource::LCL: - return "Meinberg Undisciplined local clock"; - case ClockSource::LOCL: - return "Meinberg Undisciplined local clock"; - default: - return "Unknown"; - } + } + } else if (stratum == 1) { + switch (version) { + case 3: { + switch (static_cast(refID)) { + case ClockSource::ATOM: + return "Atomic clock"; + case ClockSource::VLF: + return "VLF radio"; + case ClockSource::LORC: + return "LORAN-C radionavigation"; + case ClockSource::GOES: + return "GOES UHF environment satellite"; + case ClockSource::GPS: + return "GPS UHF satellite positioning"; + default: + return "Unknown"; } + } + case 4: { + switch (static_cast(refID)) { + case ClockSource::GOES: + return "Geosynchronous Orbit Environment Satellite"; + case ClockSource::GPS: + return "Global Position System"; + case ClockSource::GAL: + return "Galileo Positioning System"; + case ClockSource::PPS: + return "Generic pulse-per-second"; + case ClockSource::IRIG: + return "Inter-Range Instrumentation Group"; + case ClockSource::WWVB: + return "LF Radio WWVB Ft. Collins, CO 60 kHz"; + case ClockSource::DCF: + return "LF Radio DCF77 Mainflingen, DE 77.5 kHz"; + case ClockSource::HBG: + return "LF Radio HBG Prangins, HB 75 kHz"; + case ClockSource::MSF: + return "LF Radio MSF Anthorn, UK 60 kHz"; + case ClockSource::JJY: + return "LF Radio JJY Fukushima, JP 40 kHz, Saga, JP 60 kHz"; + case ClockSource::LORC: + return "MF Radio LORAN C station, 100 kHz"; + case ClockSource::TDF: + return "MF Radio Allouis, FR 162 kHz"; + case ClockSource::CHU: + return "HF Radio CHU Ottawa, Ontario"; + case ClockSource::WWV: + return "HF Radio WWV Ft. Collins, CO"; + case ClockSource::WWVH: + return "HF Radio WWVH Kauai, HI"; + case ClockSource::NIST: + return "NIST telephone modem"; + case ClockSource::ACTS: + return "NIST telephone modem"; + case ClockSource::USNO: + return "USNO telephone modem"; + case ClockSource::PTB: + return "European telephone modem"; + case ClockSource::MRS: + return "Multi Reference Sources"; + case ClockSource::XFAC: + return "Inter Face Association Changed"; + case ClockSource::STEP: + return "Step time change"; + case ClockSource::GOOG: + return "Google NTP servers"; + case ClockSource::DCFa: + return "Meinberg DCF77 with amplitude modulation"; + case ClockSource::DCFp: + return "Meinberg DCF77 with phase modulation)/pseudo random phase " + "modulation"; + case ClockSource::GPSs: + return "Meinberg GPS (with shared memory access)"; + case ClockSource::GPSi: + return "Meinberg GPS (with interrupt based access)"; + case ClockSource::GLNs: + return "Meinberg GPS/GLONASS (with shared memory access)"; + case ClockSource::GLNi: + return "Meinberg GPS/GLONASS (with interrupt based access)"; + case ClockSource::LCL: + return "Meinberg Undisciplined local clock"; + case ClockSource::LOCL: + return "Meinberg Undisciplined local clock"; + default: + return "Unknown"; } } - else - { - // TODO: Support IPv6 cases for NTPv4, it equals to MD5 hash of first four octets of IPv6 address - - pcpp::IPv4Address addr(getReferenceIdentifier()); - return addr.toString(); } + } else { + // TODO: Support IPv6 cases for NTPv4, it equals to MD5 hash of first four + // octets of IPv6 address - PCPP_LOG_ERROR("Unknown Stratum type"); - return std::string(); + pcpp::IPv4Address addr(getReferenceIdentifier()); + return addr.toString(); } - uint64_t NtpLayer::getReferenceTimestamp() const - { - return getNtpHeader()->referenceTimestamp; - } - - void NtpLayer::setReferenceTimestamp(uint64_t val) - { - getNtpHeader()->referenceTimestamp = val; - } + PCPP_LOG_ERROR("Unknown Stratum type"); + return std::string(); +} - double NtpLayer::getReferenceTimestampInSecs() const - { - return convertFromTimestampFormat(getReferenceTimestamp()); - } +uint64_t NtpLayer::getReferenceTimestamp() const { + return getNtpHeader()->referenceTimestamp; +} - void NtpLayer::setReferenceTimestampInSecs(double val) - { - getNtpHeader()->referenceTimestamp = convertToTimestampFormat(val); - } +void NtpLayer::setReferenceTimestamp(uint64_t val) { + getNtpHeader()->referenceTimestamp = val; +} - std::string NtpLayer::getReferenceTimestampAsString() - { - return convertToIsoFormat(getReferenceTimestamp()); - } +double NtpLayer::getReferenceTimestampInSecs() const { + return convertFromTimestampFormat(getReferenceTimestamp()); +} - uint64_t NtpLayer::getOriginTimestamp() const - { - return getNtpHeader()->originTimestamp; - } +void NtpLayer::setReferenceTimestampInSecs(double val) { + getNtpHeader()->referenceTimestamp = convertToTimestampFormat(val); +} - void NtpLayer::setOriginTimestamp(uint64_t val) - { - getNtpHeader()->originTimestamp = val; - } +std::string NtpLayer::getReferenceTimestampAsString() { + return convertToIsoFormat(getReferenceTimestamp()); +} - double NtpLayer::getOriginTimestampInSecs() const - { - return convertFromTimestampFormat(getOriginTimestamp()); - } +uint64_t NtpLayer::getOriginTimestamp() const { + return getNtpHeader()->originTimestamp; +} - void NtpLayer::setOriginTimestampInSecs(double val) - { - getNtpHeader()->originTimestamp = convertToTimestampFormat(val); - } +void NtpLayer::setOriginTimestamp(uint64_t val) { + getNtpHeader()->originTimestamp = val; +} - std::string NtpLayer::getOriginTimestampAsString() - { - return convertToIsoFormat(getOriginTimestamp()); - } +double NtpLayer::getOriginTimestampInSecs() const { + return convertFromTimestampFormat(getOriginTimestamp()); +} - uint64_t NtpLayer::getReceiveTimestamp() const - { - return getNtpHeader()->receiveTimestamp; - } +void NtpLayer::setOriginTimestampInSecs(double val) { + getNtpHeader()->originTimestamp = convertToTimestampFormat(val); +} - void NtpLayer::setReceiveTimestamp(uint64_t val) - { - getNtpHeader()->receiveTimestamp = val; - } +std::string NtpLayer::getOriginTimestampAsString() { + return convertToIsoFormat(getOriginTimestamp()); +} - double NtpLayer::getReceiveTimestampInSecs() const - { - return convertFromTimestampFormat(getReceiveTimestamp()); - } +uint64_t NtpLayer::getReceiveTimestamp() const { + return getNtpHeader()->receiveTimestamp; +} - void NtpLayer::setReceiveTimestampInSecs(double val) - { - getNtpHeader()->receiveTimestamp = convertToTimestampFormat(val); - } +void NtpLayer::setReceiveTimestamp(uint64_t val) { + getNtpHeader()->receiveTimestamp = val; +} - std::string NtpLayer::getReceiveTimestampAsString() - { - return convertToIsoFormat(getReceiveTimestamp()); - } +double NtpLayer::getReceiveTimestampInSecs() const { + return convertFromTimestampFormat(getReceiveTimestamp()); +} - uint64_t NtpLayer::getTransmitTimestamp() const - { - return getNtpHeader()->transmitTimestamp; - } +void NtpLayer::setReceiveTimestampInSecs(double val) { + getNtpHeader()->receiveTimestamp = convertToTimestampFormat(val); +} - void NtpLayer::setTransmitTimestamp(uint64_t val) - { - getNtpHeader()->transmitTimestamp = val; - } +std::string NtpLayer::getReceiveTimestampAsString() { + return convertToIsoFormat(getReceiveTimestamp()); +} - double NtpLayer::getTransmitTimestampInSecs() const - { - return convertFromTimestampFormat(getTransmitTimestamp()); - } +uint64_t NtpLayer::getTransmitTimestamp() const { + return getNtpHeader()->transmitTimestamp; +} - void NtpLayer::setTransmitTimestampInSecs(double val) - { - getNtpHeader()->transmitTimestamp = convertToTimestampFormat(val); - } +void NtpLayer::setTransmitTimestamp(uint64_t val) { + getNtpHeader()->transmitTimestamp = val; +} - std::string NtpLayer::getTransmitTimestampAsString() - { - return convertToIsoFormat(getTransmitTimestamp()); - } +double NtpLayer::getTransmitTimestampInSecs() const { + return convertFromTimestampFormat(getTransmitTimestamp()); +} - uint32_t NtpLayer::getKeyID() const - { - switch (getVersion()) - { - case 3: - { - if (m_DataLen < (sizeof(ntp_header) + sizeof(ntp_v3_auth))) - return 0; +void NtpLayer::setTransmitTimestampInSecs(double val) { + getNtpHeader()->transmitTimestamp = convertToTimestampFormat(val); +} - ntp_v3_auth *header = (ntp_v3_auth *)(m_Data + sizeof(ntp_header)); - return header->keyID; - } - case 4: - { - // TODO: Add support for extension fields - if (m_DataLen == (sizeof(ntp_header) + sizeof(ntp_v4_auth_md5))) - { - ntp_v4_auth_md5 *header = (ntp_v4_auth_md5 *)(m_Data + m_DataLen - sizeof(ntp_v4_auth_md5)); - return header->keyID; - } - if (m_DataLen == (sizeof(ntp_header) + sizeof(ntp_v4_auth_sha1))) - { - ntp_v4_auth_sha1 *header = (ntp_v4_auth_sha1 *)(m_Data + m_DataLen - sizeof(ntp_v4_auth_sha1)); - return header->keyID; - } +std::string NtpLayer::getTransmitTimestampAsString() { + return convertToIsoFormat(getTransmitTimestamp()); +} - PCPP_LOG_ERROR("NTP authentication parsing with extension fields are not supported"); - return 0; - } - default: - { - PCPP_LOG_ERROR("NTP version not supported"); +uint32_t NtpLayer::getKeyID() const { + switch (getVersion()) { + case 3: { + if (m_DataLen < (sizeof(ntp_header) + sizeof(ntp_v3_auth))) return 0; + + ntp_v3_auth* header = (ntp_v3_auth*)(m_Data + sizeof(ntp_header)); + return header->keyID; + } + case 4: { + // TODO: Add support for extension fields + if (m_DataLen == (sizeof(ntp_header) + sizeof(ntp_v4_auth_md5))) { + ntp_v4_auth_md5* header = + (ntp_v4_auth_md5*)(m_Data + m_DataLen - sizeof(ntp_v4_auth_md5)); + return header->keyID; } + if (m_DataLen == (sizeof(ntp_header) + sizeof(ntp_v4_auth_sha1))) { + ntp_v4_auth_sha1* header = + (ntp_v4_auth_sha1*)(m_Data + m_DataLen - sizeof(ntp_v4_auth_sha1)); + return header->keyID; } - } - std::string NtpLayer::getDigest() const - { - switch (getVersion()) - { - case 3: - { - if (m_DataLen < (sizeof(ntp_header) + sizeof(ntp_v3_auth))) - return std::string(); - - ntp_v3_auth *header = (ntp_v3_auth *)(m_Data + sizeof(ntp_header)); - return byteArrayToHexString(header->dgst, 8); - } - case 4: - { - if (m_DataLen == (sizeof(ntp_header) + sizeof(ntp_v4_auth_md5))) - { - ntp_v4_auth_md5 *header = (ntp_v4_auth_md5 *)(m_Data + m_DataLen - sizeof(ntp_v4_auth_md5)); - return byteArrayToHexString(header->dgst, 16); - } - if (m_DataLen == (sizeof(ntp_header) + sizeof(ntp_v4_auth_sha1))) - { - ntp_v4_auth_sha1 *header = (ntp_v4_auth_sha1 *)(m_Data + m_DataLen - sizeof(ntp_v4_auth_sha1)); - return byteArrayToHexString(header->dgst, 20); - } + PCPP_LOG_ERROR( + "NTP authentication parsing with extension fields are not supported"); + return 0; + } + default: { + PCPP_LOG_ERROR("NTP version not supported"); + return 0; + } + } +} - PCPP_LOG_ERROR("NTP authentication parsing with extension fields are not supported"); +std::string NtpLayer::getDigest() const { + switch (getVersion()) { + case 3: { + if (m_DataLen < (sizeof(ntp_header) + sizeof(ntp_v3_auth))) return std::string(); + + ntp_v3_auth* header = (ntp_v3_auth*)(m_Data + sizeof(ntp_header)); + return byteArrayToHexString(header->dgst, 8); + } + case 4: { + if (m_DataLen == (sizeof(ntp_header) + sizeof(ntp_v4_auth_md5))) { + ntp_v4_auth_md5* header = + (ntp_v4_auth_md5*)(m_Data + m_DataLen - sizeof(ntp_v4_auth_md5)); + return byteArrayToHexString(header->dgst, 16); } - default: - PCPP_LOG_ERROR("NTP version not supported"); - return std::string(); + if (m_DataLen == (sizeof(ntp_header) + sizeof(ntp_v4_auth_sha1))) { + ntp_v4_auth_sha1* header = + (ntp_v4_auth_sha1*)(m_Data + m_DataLen - sizeof(ntp_v4_auth_sha1)); + return byteArrayToHexString(header->dgst, 20); } + + PCPP_LOG_ERROR( + "NTP authentication parsing with extension fields are not supported"); + return std::string(); } + default: + PCPP_LOG_ERROR("NTP version not supported"); + return std::string(); + } +} - double NtpLayer::convertFromShortFormat(const uint32_t val) - { - double integerPart = netToHost16(val & 0xFFFF); - double fractionPart = netToHost16(((val & 0xFFFF0000) >> 16)) / NTP_FRIC; +double NtpLayer::convertFromShortFormat(const uint32_t val) { + double integerPart = netToHost16(val & 0xFFFF); + double fractionPart = netToHost16(((val & 0xFFFF0000) >> 16)) / NTP_FRIC; - return integerPart + fractionPart; - } + return integerPart + fractionPart; +} - double NtpLayer::convertFromTimestampFormat(const uint64_t val) - { - double integerPart = netToHost32(val & 0xFFFFFFFF); - double fractionPart = netToHost32(((val & 0xFFFFFFFF00000000) >> 32)) / NTP_FRAC; +double NtpLayer::convertFromTimestampFormat(const uint64_t val) { + double integerPart = netToHost32(val & 0xFFFFFFFF); + double fractionPart = + netToHost32(((val & 0xFFFFFFFF00000000) >> 32)) / NTP_FRAC; - // TODO: Return integer and fraction parts as struct to increase precision - // Offset change should be done here because of overflow - return integerPart + fractionPart - EPOCH_OFFSET; - } + // TODO: Return integer and fraction parts as struct to increase precision + // Offset change should be done here because of overflow + return integerPart + fractionPart - EPOCH_OFFSET; +} - uint32_t NtpLayer::convertToShortFormat(const double val) - { - double integerPart; - double fractionPart = modf(val, &integerPart); +uint32_t NtpLayer::convertToShortFormat(const double val) { + double integerPart; + double fractionPart = modf(val, &integerPart); - // Cast values to 16bit - uint32_t integerPartInt = hostToNet16(integerPart); - uint32_t fractionPartInt = hostToNet16(fractionPart * NTP_FRIC); + // Cast values to 16bit + uint32_t integerPartInt = hostToNet16(integerPart); + uint32_t fractionPartInt = hostToNet16(fractionPart * NTP_FRIC); - return integerPartInt | (fractionPartInt << 16); - } + return integerPartInt | (fractionPartInt << 16); +} - uint64_t NtpLayer::convertToTimestampFormat(const double val) - { - double integerPart; - double fractionPart = modf(val, &integerPart); +uint64_t NtpLayer::convertToTimestampFormat(const double val) { + double integerPart; + double fractionPart = modf(val, &integerPart); - // Cast values to 32bit - uint64_t integerPartInt = hostToNet32(integerPart + EPOCH_OFFSET); - uint64_t fractionPartInt = hostToNet32(fractionPart * NTP_FRAC); + // Cast values to 32bit + uint64_t integerPartInt = hostToNet32(integerPart + EPOCH_OFFSET); + uint64_t fractionPartInt = hostToNet32(fractionPart * NTP_FRAC); - return integerPartInt | (fractionPartInt << 32); - } + return integerPartInt | (fractionPartInt << 32); +} - std::string NtpLayer::convertToIsoFormat(const double timestamp) - { - double integerPart; - double fractionPart = modf(timestamp, &integerPart); +std::string NtpLayer::convertToIsoFormat(const double timestamp) { + double integerPart; + double fractionPart = modf(timestamp, &integerPart); - struct tm *timer; - time_t timeStruct = integerPart; + struct tm* timer; + time_t timeStruct = integerPart; #if defined(_WIN32) - if (timeStruct < 0) - timeStruct = 0; - timer = gmtime(&timeStruct); + if (timeStruct < 0) + timeStruct = 0; + timer = gmtime(&timeStruct); #else - struct tm timer_r; - timer = gmtime_r(&timeStruct, &timer_r); + struct tm timer_r; + timer = gmtime_r(&timeStruct, &timer_r); - if (timer != nullptr) - timer = &timer_r; + if (timer != nullptr) + timer = &timer_r; #endif - if (timer == nullptr) - { - PCPP_LOG_ERROR("Can't convert time"); - return std::string(); - } - char buffer[50], bufferFraction[15]; - strftime(buffer, sizeof(buffer) - sizeof(bufferFraction), "%Y-%m-%dT%H:%M:%S", timer); + if (timer == nullptr) { + PCPP_LOG_ERROR("Can't convert time"); + return std::string(); + } + char buffer[50], bufferFraction[15]; + strftime(buffer, sizeof(buffer) - sizeof(bufferFraction), "%Y-%m-%dT%H:%M:%S", + timer); - snprintf(bufferFraction, sizeof(bufferFraction), "%.04lfZ", fabs(fractionPart)); - strncat(buffer, &bufferFraction[1], sizeof(bufferFraction)); + snprintf(bufferFraction, sizeof(bufferFraction), "%.04lfZ", + fabs(fractionPart)); + strncat(buffer, &bufferFraction[1], sizeof(bufferFraction)); - return std::string(buffer); - } + return std::string(buffer); +} - std::string NtpLayer::convertToIsoFormat(const uint64_t timestampInNTPformat) - { - return convertToIsoFormat(convertFromTimestampFormat(timestampInNTPformat)); - } +std::string NtpLayer::convertToIsoFormat(const uint64_t timestampInNTPformat) { + return convertToIsoFormat(convertFromTimestampFormat(timestampInNTPformat)); +} - bool NtpLayer::isDataValid(const uint8_t *data, size_t dataSize) - { - return data && dataSize >= sizeof(ntp_header); - } +bool NtpLayer::isDataValid(const uint8_t* data, size_t dataSize) { + return data && dataSize >= sizeof(ntp_header); +} - std::string NtpLayer::toString() const - { - return std::string("NTP Layer v") + std::to_string(getVersion()) + ", Mode: " + getModeString(); - } +std::string NtpLayer::toString() const { + return std::string("NTP Layer v") + std::to_string(getVersion()) + + ", Mode: " + getModeString(); } +} // namespace pcpp diff --git a/Packet++/src/NullLoopbackLayer.cpp b/Packet++/src/NullLoopbackLayer.cpp index 5f6b95b3e5..d8f42caf8c 100644 --- a/Packet++/src/NullLoopbackLayer.cpp +++ b/Packet++/src/NullLoopbackLayer.cpp @@ -5,103 +5,94 @@ #include "PayloadLayer.h" #include -namespace pcpp -{ +namespace pcpp { #define BSWAP16(x) (((x) >> 8) | ((x) << 8)) -#define BSWAP32(x) (((x) >> 24) | (((x) & 0x00FF0000) >> 8) \ - | (((x) & 0x0000FF00) << 8) | ((x) << 24)) +#define BSWAP32(x) \ + (((x) >> 24) | (((x)&0x00FF0000) >> 8) | (((x)&0x0000FF00) << 8) | \ + ((x) << 24)) #define IEEE_802_3_MAX_LEN 0x5dc -NullLoopbackLayer::NullLoopbackLayer(uint32_t family) -{ - const size_t dataLen = sizeof(uint32_t); - m_DataLen = dataLen; - m_Data = new uint8_t[dataLen]; - memset(m_Data, 0, dataLen); - m_Protocol = NULL_LOOPBACK; +NullLoopbackLayer::NullLoopbackLayer(uint32_t family) { + const size_t dataLen = sizeof(uint32_t); + m_DataLen = dataLen; + m_Data = new uint8_t[dataLen]; + memset(m_Data, 0, dataLen); + m_Protocol = NULL_LOOPBACK; - setFamily(family); + setFamily(family); } -uint32_t NullLoopbackLayer::getFamily() const -{ - uint32_t family = *(uint32_t*)m_Data; - if ((family & 0xFFFF0000) != 0) - { - if ((family & 0xFF000000) == 0 && (family & 0x00FF0000) < 0x00060000) - { - family >>= 16; - } - else - { - family = BSWAP32(family); - } - } - else if ((family & 0x000000FF) == 0 && (family & 0x0000FF00) < 0x00000600) - { - family = BSWAP16(family & 0xFFFF); - } +uint32_t NullLoopbackLayer::getFamily() const { + uint32_t family = *(uint32_t*)m_Data; + if ((family & 0xFFFF0000) != 0) { + if ((family & 0xFF000000) == 0 && (family & 0x00FF0000) < 0x00060000) { + family >>= 16; + } else { + family = BSWAP32(family); + } + } else if ((family & 0x000000FF) == 0 && (family & 0x0000FF00) < 0x00000600) { + family = BSWAP16(family & 0xFFFF); + } - return family; + return family; } -void NullLoopbackLayer::setFamily(uint32_t family) -{ - *m_Data = family; -} +void NullLoopbackLayer::setFamily(uint32_t family) { *m_Data = family; } -void NullLoopbackLayer::parseNextLayer() -{ - uint8_t* payload = m_Data + sizeof(uint32_t); - size_t payloadLen = m_DataLen - sizeof(uint32_t); +void NullLoopbackLayer::parseNextLayer() { + uint8_t* payload = m_Data + sizeof(uint32_t); + size_t payloadLen = m_DataLen - sizeof(uint32_t); - uint32_t family = getFamily(); - if (family > IEEE_802_3_MAX_LEN) - { - uint16_t ethType = (uint16_t)family; - switch (ethType) - { - case PCPP_ETHERTYPE_IP: - m_NextLayer = IPv4Layer::isDataValid(payload, payloadLen) - ? static_cast(new IPv4Layer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - return; - case PCPP_ETHERTYPE_IPV6: - m_NextLayer = IPv6Layer::isDataValid(payload, payloadLen) - ? static_cast(new IPv6Layer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - return; - default: - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - return; - } - } + uint32_t family = getFamily(); + if (family > IEEE_802_3_MAX_LEN) { + uint16_t ethType = (uint16_t)family; + switch (ethType) { + case PCPP_ETHERTYPE_IP: + m_NextLayer = + IPv4Layer::isDataValid(payload, payloadLen) + ? static_cast( + new IPv4Layer(payload, payloadLen, this, m_Packet)) + : static_cast( + new PayloadLayer(payload, payloadLen, this, m_Packet)); + return; + case PCPP_ETHERTYPE_IPV6: + m_NextLayer = + IPv6Layer::isDataValid(payload, payloadLen) + ? static_cast( + new IPv6Layer(payload, payloadLen, this, m_Packet)) + : static_cast( + new PayloadLayer(payload, payloadLen, this, m_Packet)); + return; + default: + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + return; + } + } - switch (family) - { - case PCPP_BSD_AF_INET: - m_NextLayer = IPv4Layer::isDataValid(payload, payloadLen) - ? static_cast(new IPv4Layer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case PCPP_BSD_AF_INET6_BSD: - case PCPP_BSD_AF_INET6_FREEBSD: - case PCPP_BSD_AF_INET6_DARWIN: - m_NextLayer = IPv6Layer::isDataValid(payload, payloadLen) - ? static_cast(new IPv6Layer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - default: - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - } + switch (family) { + case PCPP_BSD_AF_INET: + m_NextLayer = IPv4Layer::isDataValid(payload, payloadLen) + ? static_cast( + new IPv4Layer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer( + payload, payloadLen, this, m_Packet)); + break; + case PCPP_BSD_AF_INET6_BSD: + case PCPP_BSD_AF_INET6_FREEBSD: + case PCPP_BSD_AF_INET6_DARWIN: + m_NextLayer = IPv6Layer::isDataValid(payload, payloadLen) + ? static_cast( + new IPv6Layer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer( + payload, payloadLen, this, m_Packet)); + break; + default: + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + } } +std::string NullLoopbackLayer::toString() const { return "Null/Loopback"; } -std::string NullLoopbackLayer::toString() const -{ - return "Null/Loopback"; -} - -} +} // namespace pcpp diff --git a/Packet++/src/PPPoELayer.cpp b/Packet++/src/PPPoELayer.cpp index 34a6d856b5..44fb377792 100644 --- a/Packet++/src/PPPoELayer.cpp +++ b/Packet++/src/PPPoELayer.cpp @@ -1,437 +1,433 @@ #define LOG_MODULE PacketLogModulePPPoELayer #include "PPPoELayer.h" +#include "EndianPortable.h" #include "IPv4Layer.h" #include "IPv6Layer.h" -#include "PayloadLayer.h" #include "Logger.h" +#include "PayloadLayer.h" #include #include -#include "EndianPortable.h" -namespace pcpp -{ +namespace pcpp { /// PPPoELayer /// ~~~~~~~~~~ -PPPoELayer::PPPoELayer(uint8_t version, uint8_t type, PPPoELayer::PPPoECode code, uint16_t sessionId, size_t additionalBytesToAllocate) -{ - const size_t dataLen = sizeof(pppoe_header) + additionalBytesToAllocate; - m_DataLen = dataLen; - m_Data = new uint8_t[dataLen]; - memset(m_Data, 0, dataLen); - - pppoe_header* pppoeHdr = getPPPoEHeader(); - pppoeHdr->version = (version & 0xf); - pppoeHdr->type = (type & 0x0f); - pppoeHdr->code = code; - pppoeHdr->sessionId = htobe16(sessionId); - pppoeHdr->payloadLength = 0; +PPPoELayer::PPPoELayer(uint8_t version, uint8_t type, + PPPoELayer::PPPoECode code, uint16_t sessionId, + size_t additionalBytesToAllocate) { + const size_t dataLen = sizeof(pppoe_header) + additionalBytesToAllocate; + m_DataLen = dataLen; + m_Data = new uint8_t[dataLen]; + memset(m_Data, 0, dataLen); + + pppoe_header* pppoeHdr = getPPPoEHeader(); + pppoeHdr->version = (version & 0xf); + pppoeHdr->type = (type & 0x0f); + pppoeHdr->code = code; + pppoeHdr->sessionId = htobe16(sessionId); + pppoeHdr->payloadLength = 0; } -void PPPoELayer::computeCalculateFields() -{ - pppoe_header* pppoeHdr = (pppoe_header*)m_Data; - pppoeHdr->payloadLength = htobe16(m_DataLen - sizeof(pppoe_header)); +void PPPoELayer::computeCalculateFields() { + pppoe_header* pppoeHdr = (pppoe_header*)m_Data; + pppoeHdr->payloadLength = htobe16(m_DataLen - sizeof(pppoe_header)); } - - /// PPPoESessionLayer /// ~~~~~~~~~~~~~~~~~ - -void PPPoESessionLayer::parseNextLayer() -{ - size_t headerLen = getHeaderLen(); - if (m_DataLen <= headerLen) - return; - - uint8_t* payload = m_Data + headerLen; - size_t payloadLen = m_DataLen - headerLen; - - switch (getPPPNextProtocol()) - { - case PCPP_PPP_IP: - m_NextLayer = IPv4Layer::isDataValid(payload, payloadLen) - ? static_cast(new IPv4Layer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case PCPP_PPP_IPV6: - m_NextLayer = IPv6Layer::isDataValid(payload, payloadLen) - ? static_cast(new IPv6Layer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - default: - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - break; - } - +void PPPoESessionLayer::parseNextLayer() { + size_t headerLen = getHeaderLen(); + if (m_DataLen <= headerLen) + return; + + uint8_t* payload = m_Data + headerLen; + size_t payloadLen = m_DataLen - headerLen; + + switch (getPPPNextProtocol()) { + case PCPP_PPP_IP: + m_NextLayer = IPv4Layer::isDataValid(payload, payloadLen) + ? static_cast( + new IPv4Layer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer( + payload, payloadLen, this, m_Packet)); + break; + case PCPP_PPP_IPV6: + m_NextLayer = IPv6Layer::isDataValid(payload, payloadLen) + ? static_cast( + new IPv6Layer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer( + payload, payloadLen, this, m_Packet)); + break; + default: + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + break; + } } -uint16_t PPPoESessionLayer::getPPPNextProtocol() const -{ - if (m_DataLen < getHeaderLen()) - { - PCPP_LOG_ERROR("ERROR: size of layer is smaller then PPPoE session header"); - return 0; - } +uint16_t PPPoESessionLayer::getPPPNextProtocol() const { + if (m_DataLen < getHeaderLen()) { + PCPP_LOG_ERROR("ERROR: size of layer is smaller then PPPoE session header"); + return 0; + } - uint16_t pppNextProto = *(uint16_t*)(m_Data + sizeof(pppoe_header)); - return be16toh(pppNextProto); + uint16_t pppNextProto = *(uint16_t*)(m_Data + sizeof(pppoe_header)); + return be16toh(pppNextProto); } -void PPPoESessionLayer::setPPPNextProtocol(uint16_t nextProtocol) -{ - if (m_DataLen < getHeaderLen()) - { - PCPP_LOG_ERROR("ERROR: size of layer is smaller then PPPoE session header"); - return; - } +void PPPoESessionLayer::setPPPNextProtocol(uint16_t nextProtocol) { + if (m_DataLen < getHeaderLen()) { + PCPP_LOG_ERROR("ERROR: size of layer is smaller then PPPoE session header"); + return; + } - uint16_t* pppProto = (uint16_t*)(m_Data + sizeof(pppoe_header)); - *pppProto = htobe16(nextProtocol); + uint16_t* pppProto = (uint16_t*)(m_Data + sizeof(pppoe_header)); + *pppProto = htobe16(nextProtocol); } -std::map createPPPNextProtoToStringMap() -{ - std::map tempMap; - tempMap[PCPP_PPP_PADDING] = "Padding Protocol"; - tempMap[PCPP_PPP_ROHC_SCID] = "ROHC small-CID"; - tempMap[PCPP_PPP_ROHC_LCID] = "ROHC large-CID"; - tempMap[PCPP_PPP_IP] = "Internet Protocol version 4"; - tempMap[PCPP_PPP_OSI] = "OSI Network Layer"; - tempMap[PCPP_PPP_XNSIDP] = "Xerox NS IDP"; - tempMap[PCPP_PPP_DEC4] = "DECnet Phase IV"; - tempMap[PCPP_PPP_AT] = "Appletalk"; - tempMap[PCPP_PPP_IPX] = "Novell IPX"; - tempMap[PCPP_PPP_VJC_COMP] = "Van Jacobson Compressed TCP/IP"; - tempMap[PCPP_PPP_VJC_UNCOMP] = "Van Jacobson Uncompressed TCP/IP"; - tempMap[PCPP_PPP_BCP] = "Bridging PDU"; - tempMap[PCPP_PPP_ST] = "Stream Protocol (ST-II)"; - tempMap[PCPP_PPP_VINES] = "Banyan Vines"; - tempMap[PCPP_PPP_AT_EDDP] = "AppleTalk EDDP"; - tempMap[PCPP_PPP_AT_SB] = "AppleTalk SmartBuffered"; - tempMap[PCPP_PPP_MP] = "Multi-Link"; - tempMap[PCPP_PPP_NB] = "NETBIOS Framing"; - tempMap[PCPP_PPP_CISCO] = "Cisco Systems"; - tempMap[PCPP_PPP_ASCOM] = "Ascom Timeplex"; - tempMap[PCPP_PPP_LBLB] = "Fujitsu Link Backup and Load Balancing (LBLB)"; - tempMap[PCPP_PPP_RL] = "DCA Remote Lan"; - tempMap[PCPP_PPP_SDTP] = "Serial Data Transport Protocol (PPP-SDTP)"; - tempMap[PCPP_PPP_LLC] = "SNA over 802.2"; - tempMap[PCPP_PPP_SNA] = "SNA"; - tempMap[PCPP_PPP_IPV6HC] = "IPv6 Header Compression "; - tempMap[PCPP_PPP_KNX] = "KNX Bridging Data"; - tempMap[PCPP_PPP_ENCRYPT] = "Encryption"; - tempMap[PCPP_PPP_ILE] = "Individual Link Encryption"; - tempMap[PCPP_PPP_IPV6] = "Internet Protocol version 6"; - tempMap[PCPP_PPP_MUX] = "PPP Muxing"; - tempMap[PCPP_PPP_VSNP] = "Vendor-Specific Network Protocol (VSNP)"; - tempMap[PCPP_PPP_TNP] = "TRILL Network Protocol (TNP)"; - tempMap[PCPP_PPP_RTP_FH] = "RTP IPHC Full Header"; - tempMap[PCPP_PPP_RTP_CTCP] = "RTP IPHC Compressed TCP"; - tempMap[PCPP_PPP_RTP_CNTCP] = "RTP IPHC Compressed Non TCP"; - tempMap[PCPP_PPP_RTP_CUDP8] = "RTP IPHC Compressed UDP 8"; - tempMap[PCPP_PPP_RTP_CRTP8] = "RTP IPHC Compressed RTP 8"; - tempMap[PCPP_PPP_STAMPEDE] = "Stampede Bridging"; - tempMap[PCPP_PPP_MPPLUS] = "MP+ Protocol"; - tempMap[PCPP_PPP_NTCITS_IPI] = "NTCITS IPI"; - tempMap[PCPP_PPP_ML_SLCOMP] = "Single link compression in multilink"; - tempMap[PCPP_PPP_COMP] = "Compressed datagram"; - tempMap[PCPP_PPP_STP_HELLO] = "802.1d Hello Packets"; - tempMap[PCPP_PPP_IBM_SR] = "IBM Source Routing BPDU"; - tempMap[PCPP_PPP_DEC_LB] = "DEC LANBridge100 Spanning Tree"; - tempMap[PCPP_PPP_CDP] = "Cisco Discovery Protocol"; - tempMap[PCPP_PPP_NETCS] = "Netcs Twin Routing"; - tempMap[PCPP_PPP_STP] = "STP - Scheduled Transfer Protocol"; - tempMap[PCPP_PPP_EDP] = "EDP - Extreme Discovery Protocol"; - tempMap[PCPP_PPP_OSCP] = "Optical Supervisory Channel Protocol (OSCP)"; - tempMap[PCPP_PPP_OSCP2] = "Optical Supervisory Channel Protocol (OSCP)"; - tempMap[PCPP_PPP_LUXCOM] = "Luxcom"; - tempMap[PCPP_PPP_SIGMA] = "Sigma Network Systems"; - tempMap[PCPP_PPP_ACSP] = "Apple Client Server Protocol"; - tempMap[PCPP_PPP_MPLS_UNI] = "MPLS Unicast"; - tempMap[PCPP_PPP_MPLS_MULTI] = "MPLS Multicast"; - tempMap[PCPP_PPP_P12844] = "IEEE p1284.4 standard - data packets"; - tempMap[PCPP_PPP_TETRA] = "ETSI TETRA Network Protocol Type 1"; - tempMap[PCPP_PPP_MFTP] = "Multichannel Flow Treatment Protocol"; - tempMap[PCPP_PPP_RTP_CTCPND] = "RTP IPHC Compressed TCP No Delta"; - tempMap[PCPP_PPP_RTP_CS] = "RTP IPHC Context State"; - tempMap[PCPP_PPP_RTP_CUDP16] = "RTP IPHC Compressed UDP 16"; - tempMap[PCPP_PPP_RTP_CRDP16] = "RTP IPHC Compressed RTP 16"; - tempMap[PCPP_PPP_CCCP] = "Cray Communications Control Protocol"; - tempMap[PCPP_PPP_CDPD_MNRP] = "CDPD Mobile Network Registration Protocol"; - tempMap[PCPP_PPP_EXPANDAP] = "Expand accelerator protocol"; - tempMap[PCPP_PPP_ODSICP] = "ODSICP NCP"; - tempMap[PCPP_PPP_DOCSIS] = "DOCSIS DLL"; - tempMap[PCPP_PPP_CETACEANNDP] = "Cetacean Network Detection Protocol"; - tempMap[PCPP_PPP_LZS] = "Stacker LZS"; - tempMap[PCPP_PPP_REFTEK] = "RefTek Protocol"; - tempMap[PCPP_PPP_FC] = "Fibre Channel"; - tempMap[PCPP_PPP_EMIT] = "EMIT Protocols"; - tempMap[PCPP_PPP_VSP] = "Vendor-Specific Protocol (VSP)"; - tempMap[PCPP_PPP_TLSP] = "TRILL Link State Protocol (TLSP)"; - tempMap[PCPP_PPP_IPCP] = "Internet Protocol Control Protocol"; - tempMap[PCPP_PPP_OSINLCP] = "OSI Network Layer Control Protocol"; - tempMap[PCPP_PPP_XNSIDPCP] = "Xerox NS IDP Control Protocol"; - tempMap[PCPP_PPP_DECNETCP] = "DECnet Phase IV Control Protocol"; - tempMap[PCPP_PPP_ATCP] = "AppleTalk Control Protocol"; - tempMap[PCPP_PPP_IPXCP] = "Novell IPX Control Protocol"; - tempMap[PCPP_PPP_BRIDGENCP] = "Bridging NCP"; - tempMap[PCPP_PPP_SPCP] = "Stream Protocol Control Protocol"; - tempMap[PCPP_PPP_BVCP] = "Banyan Vines Control Protocol"; - tempMap[PCPP_PPP_MLCP] = "Multi-Link Control Protocol"; - tempMap[PCPP_PPP_NBCP] = "NETBIOS Framing Control Protocol"; - tempMap[PCPP_PPP_CISCOCP] = "Cisco Systems Control Protocol"; - tempMap[PCPP_PPP_ASCOMCP] = "Ascom Timeplex"; - tempMap[PCPP_PPP_LBLBCP] = "Fujitsu LBLB Control Protocol"; - tempMap[PCPP_PPP_RLNCP] = "DCA Remote Lan Network Control Protocol (RLNCP)"; - tempMap[PCPP_PPP_SDCP] = "Serial Data Control Protocol (PPP-SDCP)"; - tempMap[PCPP_PPP_LLCCP] = "SNA over 802.2 Control Protocol"; - tempMap[PCPP_PPP_SNACP] = "SNA Control Protocol"; - tempMap[PCPP_PPP_IP6HCCP] = "IP6 Header Compression Control Protocol"; - tempMap[PCPP_PPP_KNXCP] = "KNX Bridging Control Protocol"; - tempMap[PCPP_PPP_ECP] = "Encryption Control Protocol"; - tempMap[PCPP_PPP_ILECP] = "Individual Link Encryption Control Protocol"; - tempMap[PCPP_PPP_IPV6CP] = "IPv6 Control Protocol"; - tempMap[PCPP_PPP_MUXCP] = "PPP Muxing Control Protocol"; - tempMap[PCPP_PPP_VSNCP] = "Vendor-Specific Network Control Protocol (VSNCP)"; - tempMap[PCPP_PPP_TNCP] = "TRILL Network Control Protocol"; - tempMap[PCPP_PPP_STAMPEDECP] = "Stampede Bridging Control Protocol"; - tempMap[PCPP_PPP_MPPCP] = "MP+ Control Protocol"; - tempMap[PCPP_PPP_IPICP] = "NTCITS IPI Control Protocol"; - tempMap[PCPP_PPP_SLCC] = "Single link compression in multilink control"; - tempMap[PCPP_PPP_CCP] = "Compression Control Protocol"; - tempMap[PCPP_PPP_CDPCP] = "Cisco Discovery Protocol Control Protocol"; - tempMap[PCPP_PPP_NETCSCP] = "Netcs Twin Routing"; - tempMap[PCPP_PPP_STPCP] = "STP - Control Protocol"; - tempMap[PCPP_PPP_EDPCP] = "EDPCP - Extreme Discovery Protocol Control Protocol"; - tempMap[PCPP_PPP_ACSPC] = "Apple Client Server Protocol Control"; - tempMap[PCPP_PPP_MPLSCP] = "MPLS Control Protocol"; - tempMap[PCPP_PPP_P12844CP] = "IEEE p1284.4 standard - Protocol Control"; - tempMap[PCPP_PPP_TETRACP] = "ETSI TETRA TNP1 Control Protocol"; - tempMap[PCPP_PPP_MFTPCP] = "Multichannel Flow Treatment Protocol"; - tempMap[PCPP_PPP_LCP] = "Link Control Protocol"; - tempMap[PCPP_PPP_PAP] = "Password Authentication Protocol"; - tempMap[PCPP_PPP_LQR] = "Link Quality Report"; - tempMap[PCPP_PPP_SPAP] = "Shiva Password Authentication Protocol"; - tempMap[PCPP_PPP_CBCP] = "Callback Control Protocol (CBCP)"; - tempMap[PCPP_PPP_BACP] = "BACP Bandwidth Allocation Control Protocol"; - tempMap[PCPP_PPP_BAP] = "BAP Bandwidth Allocation Protocol"; - tempMap[PCPP_PPP_VSAP] = "Vendor-Specific Authentication Protocol (VSAP)"; - tempMap[PCPP_PPP_CONTCP] = "Container Control Protocol"; - tempMap[PCPP_PPP_CHAP] = "Challenge Handshake Authentication Protocol"; - tempMap[PCPP_PPP_RSAAP] = "RSA Authentication Protocol"; - tempMap[PCPP_PPP_EAP] = "Extensible Authentication Protocol"; - tempMap[PCPP_PPP_SIEP] = "Mitsubishi Security Information Exchange Protocol (SIEP)"; - tempMap[PCPP_PPP_SBAP] = "Stampede Bridging Authorization Protocol"; - tempMap[PCPP_PPP_PRPAP] = "Proprietary Authentication Protocol"; - tempMap[PCPP_PPP_PRPAP2] = "Proprietary Authentication Protocol"; - tempMap[PCPP_PPP_PRPNIAP] = "Proprietary Node ID Authentication Protocol"; - return tempMap; +std::map createPPPNextProtoToStringMap() { + std::map tempMap; + tempMap[PCPP_PPP_PADDING] = "Padding Protocol"; + tempMap[PCPP_PPP_ROHC_SCID] = "ROHC small-CID"; + tempMap[PCPP_PPP_ROHC_LCID] = "ROHC large-CID"; + tempMap[PCPP_PPP_IP] = "Internet Protocol version 4"; + tempMap[PCPP_PPP_OSI] = "OSI Network Layer"; + tempMap[PCPP_PPP_XNSIDP] = "Xerox NS IDP"; + tempMap[PCPP_PPP_DEC4] = "DECnet Phase IV"; + tempMap[PCPP_PPP_AT] = "Appletalk"; + tempMap[PCPP_PPP_IPX] = "Novell IPX"; + tempMap[PCPP_PPP_VJC_COMP] = "Van Jacobson Compressed TCP/IP"; + tempMap[PCPP_PPP_VJC_UNCOMP] = "Van Jacobson Uncompressed TCP/IP"; + tempMap[PCPP_PPP_BCP] = "Bridging PDU"; + tempMap[PCPP_PPP_ST] = "Stream Protocol (ST-II)"; + tempMap[PCPP_PPP_VINES] = "Banyan Vines"; + tempMap[PCPP_PPP_AT_EDDP] = "AppleTalk EDDP"; + tempMap[PCPP_PPP_AT_SB] = "AppleTalk SmartBuffered"; + tempMap[PCPP_PPP_MP] = "Multi-Link"; + tempMap[PCPP_PPP_NB] = "NETBIOS Framing"; + tempMap[PCPP_PPP_CISCO] = "Cisco Systems"; + tempMap[PCPP_PPP_ASCOM] = "Ascom Timeplex"; + tempMap[PCPP_PPP_LBLB] = "Fujitsu Link Backup and Load Balancing (LBLB)"; + tempMap[PCPP_PPP_RL] = "DCA Remote Lan"; + tempMap[PCPP_PPP_SDTP] = "Serial Data Transport Protocol (PPP-SDTP)"; + tempMap[PCPP_PPP_LLC] = "SNA over 802.2"; + tempMap[PCPP_PPP_SNA] = "SNA"; + tempMap[PCPP_PPP_IPV6HC] = "IPv6 Header Compression "; + tempMap[PCPP_PPP_KNX] = "KNX Bridging Data"; + tempMap[PCPP_PPP_ENCRYPT] = "Encryption"; + tempMap[PCPP_PPP_ILE] = "Individual Link Encryption"; + tempMap[PCPP_PPP_IPV6] = "Internet Protocol version 6"; + tempMap[PCPP_PPP_MUX] = "PPP Muxing"; + tempMap[PCPP_PPP_VSNP] = "Vendor-Specific Network Protocol (VSNP)"; + tempMap[PCPP_PPP_TNP] = "TRILL Network Protocol (TNP)"; + tempMap[PCPP_PPP_RTP_FH] = "RTP IPHC Full Header"; + tempMap[PCPP_PPP_RTP_CTCP] = "RTP IPHC Compressed TCP"; + tempMap[PCPP_PPP_RTP_CNTCP] = "RTP IPHC Compressed Non TCP"; + tempMap[PCPP_PPP_RTP_CUDP8] = "RTP IPHC Compressed UDP 8"; + tempMap[PCPP_PPP_RTP_CRTP8] = "RTP IPHC Compressed RTP 8"; + tempMap[PCPP_PPP_STAMPEDE] = "Stampede Bridging"; + tempMap[PCPP_PPP_MPPLUS] = "MP+ Protocol"; + tempMap[PCPP_PPP_NTCITS_IPI] = "NTCITS IPI"; + tempMap[PCPP_PPP_ML_SLCOMP] = "Single link compression in multilink"; + tempMap[PCPP_PPP_COMP] = "Compressed datagram"; + tempMap[PCPP_PPP_STP_HELLO] = "802.1d Hello Packets"; + tempMap[PCPP_PPP_IBM_SR] = "IBM Source Routing BPDU"; + tempMap[PCPP_PPP_DEC_LB] = "DEC LANBridge100 Spanning Tree"; + tempMap[PCPP_PPP_CDP] = "Cisco Discovery Protocol"; + tempMap[PCPP_PPP_NETCS] = "Netcs Twin Routing"; + tempMap[PCPP_PPP_STP] = "STP - Scheduled Transfer Protocol"; + tempMap[PCPP_PPP_EDP] = "EDP - Extreme Discovery Protocol"; + tempMap[PCPP_PPP_OSCP] = "Optical Supervisory Channel Protocol (OSCP)"; + tempMap[PCPP_PPP_OSCP2] = "Optical Supervisory Channel Protocol (OSCP)"; + tempMap[PCPP_PPP_LUXCOM] = "Luxcom"; + tempMap[PCPP_PPP_SIGMA] = "Sigma Network Systems"; + tempMap[PCPP_PPP_ACSP] = "Apple Client Server Protocol"; + tempMap[PCPP_PPP_MPLS_UNI] = "MPLS Unicast"; + tempMap[PCPP_PPP_MPLS_MULTI] = "MPLS Multicast"; + tempMap[PCPP_PPP_P12844] = "IEEE p1284.4 standard - data packets"; + tempMap[PCPP_PPP_TETRA] = "ETSI TETRA Network Protocol Type 1"; + tempMap[PCPP_PPP_MFTP] = "Multichannel Flow Treatment Protocol"; + tempMap[PCPP_PPP_RTP_CTCPND] = "RTP IPHC Compressed TCP No Delta"; + tempMap[PCPP_PPP_RTP_CS] = "RTP IPHC Context State"; + tempMap[PCPP_PPP_RTP_CUDP16] = "RTP IPHC Compressed UDP 16"; + tempMap[PCPP_PPP_RTP_CRDP16] = "RTP IPHC Compressed RTP 16"; + tempMap[PCPP_PPP_CCCP] = "Cray Communications Control Protocol"; + tempMap[PCPP_PPP_CDPD_MNRP] = "CDPD Mobile Network Registration Protocol"; + tempMap[PCPP_PPP_EXPANDAP] = "Expand accelerator protocol"; + tempMap[PCPP_PPP_ODSICP] = "ODSICP NCP"; + tempMap[PCPP_PPP_DOCSIS] = "DOCSIS DLL"; + tempMap[PCPP_PPP_CETACEANNDP] = "Cetacean Network Detection Protocol"; + tempMap[PCPP_PPP_LZS] = "Stacker LZS"; + tempMap[PCPP_PPP_REFTEK] = "RefTek Protocol"; + tempMap[PCPP_PPP_FC] = "Fibre Channel"; + tempMap[PCPP_PPP_EMIT] = "EMIT Protocols"; + tempMap[PCPP_PPP_VSP] = "Vendor-Specific Protocol (VSP)"; + tempMap[PCPP_PPP_TLSP] = "TRILL Link State Protocol (TLSP)"; + tempMap[PCPP_PPP_IPCP] = "Internet Protocol Control Protocol"; + tempMap[PCPP_PPP_OSINLCP] = "OSI Network Layer Control Protocol"; + tempMap[PCPP_PPP_XNSIDPCP] = "Xerox NS IDP Control Protocol"; + tempMap[PCPP_PPP_DECNETCP] = "DECnet Phase IV Control Protocol"; + tempMap[PCPP_PPP_ATCP] = "AppleTalk Control Protocol"; + tempMap[PCPP_PPP_IPXCP] = "Novell IPX Control Protocol"; + tempMap[PCPP_PPP_BRIDGENCP] = "Bridging NCP"; + tempMap[PCPP_PPP_SPCP] = "Stream Protocol Control Protocol"; + tempMap[PCPP_PPP_BVCP] = "Banyan Vines Control Protocol"; + tempMap[PCPP_PPP_MLCP] = "Multi-Link Control Protocol"; + tempMap[PCPP_PPP_NBCP] = "NETBIOS Framing Control Protocol"; + tempMap[PCPP_PPP_CISCOCP] = "Cisco Systems Control Protocol"; + tempMap[PCPP_PPP_ASCOMCP] = "Ascom Timeplex"; + tempMap[PCPP_PPP_LBLBCP] = "Fujitsu LBLB Control Protocol"; + tempMap[PCPP_PPP_RLNCP] = "DCA Remote Lan Network Control Protocol (RLNCP)"; + tempMap[PCPP_PPP_SDCP] = "Serial Data Control Protocol (PPP-SDCP)"; + tempMap[PCPP_PPP_LLCCP] = "SNA over 802.2 Control Protocol"; + tempMap[PCPP_PPP_SNACP] = "SNA Control Protocol"; + tempMap[PCPP_PPP_IP6HCCP] = "IP6 Header Compression Control Protocol"; + tempMap[PCPP_PPP_KNXCP] = "KNX Bridging Control Protocol"; + tempMap[PCPP_PPP_ECP] = "Encryption Control Protocol"; + tempMap[PCPP_PPP_ILECP] = "Individual Link Encryption Control Protocol"; + tempMap[PCPP_PPP_IPV6CP] = "IPv6 Control Protocol"; + tempMap[PCPP_PPP_MUXCP] = "PPP Muxing Control Protocol"; + tempMap[PCPP_PPP_VSNCP] = "Vendor-Specific Network Control Protocol (VSNCP)"; + tempMap[PCPP_PPP_TNCP] = "TRILL Network Control Protocol"; + tempMap[PCPP_PPP_STAMPEDECP] = "Stampede Bridging Control Protocol"; + tempMap[PCPP_PPP_MPPCP] = "MP+ Control Protocol"; + tempMap[PCPP_PPP_IPICP] = "NTCITS IPI Control Protocol"; + tempMap[PCPP_PPP_SLCC] = "Single link compression in multilink control"; + tempMap[PCPP_PPP_CCP] = "Compression Control Protocol"; + tempMap[PCPP_PPP_CDPCP] = "Cisco Discovery Protocol Control Protocol"; + tempMap[PCPP_PPP_NETCSCP] = "Netcs Twin Routing"; + tempMap[PCPP_PPP_STPCP] = "STP - Control Protocol"; + tempMap[PCPP_PPP_EDPCP] = + "EDPCP - Extreme Discovery Protocol Control Protocol"; + tempMap[PCPP_PPP_ACSPC] = "Apple Client Server Protocol Control"; + tempMap[PCPP_PPP_MPLSCP] = "MPLS Control Protocol"; + tempMap[PCPP_PPP_P12844CP] = "IEEE p1284.4 standard - Protocol Control"; + tempMap[PCPP_PPP_TETRACP] = "ETSI TETRA TNP1 Control Protocol"; + tempMap[PCPP_PPP_MFTPCP] = "Multichannel Flow Treatment Protocol"; + tempMap[PCPP_PPP_LCP] = "Link Control Protocol"; + tempMap[PCPP_PPP_PAP] = "Password Authentication Protocol"; + tempMap[PCPP_PPP_LQR] = "Link Quality Report"; + tempMap[PCPP_PPP_SPAP] = "Shiva Password Authentication Protocol"; + tempMap[PCPP_PPP_CBCP] = "Callback Control Protocol (CBCP)"; + tempMap[PCPP_PPP_BACP] = "BACP Bandwidth Allocation Control Protocol"; + tempMap[PCPP_PPP_BAP] = "BAP Bandwidth Allocation Protocol"; + tempMap[PCPP_PPP_VSAP] = "Vendor-Specific Authentication Protocol (VSAP)"; + tempMap[PCPP_PPP_CONTCP] = "Container Control Protocol"; + tempMap[PCPP_PPP_CHAP] = "Challenge Handshake Authentication Protocol"; + tempMap[PCPP_PPP_RSAAP] = "RSA Authentication Protocol"; + tempMap[PCPP_PPP_EAP] = "Extensible Authentication Protocol"; + tempMap[PCPP_PPP_SIEP] = + "Mitsubishi Security Information Exchange Protocol (SIEP)"; + tempMap[PCPP_PPP_SBAP] = "Stampede Bridging Authorization Protocol"; + tempMap[PCPP_PPP_PRPAP] = "Proprietary Authentication Protocol"; + tempMap[PCPP_PPP_PRPAP2] = "Proprietary Authentication Protocol"; + tempMap[PCPP_PPP_PRPNIAP] = "Proprietary Node ID Authentication Protocol"; + return tempMap; } -const std::map PPPNextProtoToString = createPPPNextProtoToStringMap(); - -std::string PPPoESessionLayer::toString() const -{ - std::map::const_iterator iter = PPPNextProtoToString.find(getPPPNextProtocol()); - std::string nextProtocol; - if (iter != PPPNextProtoToString.end()) - nextProtocol = iter->second; - else - { - std::ostringstream stream; - stream << "Unknown (0x" << std::hex << getPPPNextProtocol() << ")"; - nextProtocol = stream.str(); - } - - return "PPP-over-Ethernet Session (followed by '" + nextProtocol + "')"; +const std::map PPPNextProtoToString = + createPPPNextProtoToStringMap(); + +std::string PPPoESessionLayer::toString() const { + std::map::const_iterator iter = + PPPNextProtoToString.find(getPPPNextProtocol()); + std::string nextProtocol; + if (iter != PPPNextProtoToString.end()) + nextProtocol = iter->second; + else { + std::ostringstream stream; + stream << "Unknown (0x" << std::hex << getPPPNextProtocol() << ")"; + nextProtocol = stream.str(); + } + + return "PPP-over-Ethernet Session (followed by '" + nextProtocol + "')"; } - - /// PPPoEDiscoveryLayer /// ~~~~~~~~~~~~~~~~~~~ +PPPoEDiscoveryLayer::PPPoETagTypes +PPPoEDiscoveryLayer::PPPoETag::getType() const { + if (m_Data == nullptr) + return PPPoEDiscoveryLayer::PPPoETagTypes::PPPOE_TAG_EOL; -PPPoEDiscoveryLayer::PPPoETagTypes PPPoEDiscoveryLayer::PPPoETag::getType() const -{ - if (m_Data == nullptr) - return PPPoEDiscoveryLayer::PPPoETagTypes::PPPOE_TAG_EOL; - - return (PPPoEDiscoveryLayer::PPPoETagTypes)be16toh(m_Data->recordType); + return (PPPoEDiscoveryLayer::PPPoETagTypes)be16toh(m_Data->recordType); } -size_t PPPoEDiscoveryLayer::PPPoETag::getTotalSize() const -{ - if (m_Data == nullptr) - return 0; +size_t PPPoEDiscoveryLayer::PPPoETag::getTotalSize() const { + if (m_Data == nullptr) + return 0; - return 2*sizeof(uint16_t) + be16toh(m_Data->recordLen); + return 2 * sizeof(uint16_t) + be16toh(m_Data->recordLen); } -size_t PPPoEDiscoveryLayer::PPPoETag::getDataSize() const -{ - if (m_Data == nullptr) - return 0; +size_t PPPoEDiscoveryLayer::PPPoETag::getDataSize() const { + if (m_Data == nullptr) + return 0; - return be16toh(m_Data->recordLen); + return be16toh(m_Data->recordLen); } -PPPoEDiscoveryLayer::PPPoETag PPPoEDiscoveryLayer::PPPoETagBuilder::build() const -{ - size_t tagSize = 2*sizeof(uint16_t) + m_RecValueLen; - uint8_t* recordBuffer = new uint8_t[tagSize]; - uint16_t tagTypeVal = htobe16(static_cast(m_RecType)); - uint16_t tagLength = htobe16(static_cast(m_RecValueLen)); - memcpy(recordBuffer, &tagTypeVal, sizeof(uint16_t)); - memcpy(recordBuffer + sizeof(uint16_t), &tagLength, sizeof(uint16_t)); - if (tagLength > 0 && m_RecValue != nullptr) - memcpy(recordBuffer + 2*sizeof(uint16_t), m_RecValue, m_RecValueLen); - - return PPPoEDiscoveryLayer::PPPoETag(recordBuffer); +PPPoEDiscoveryLayer::PPPoETag +PPPoEDiscoveryLayer::PPPoETagBuilder::build() const { + size_t tagSize = 2 * sizeof(uint16_t) + m_RecValueLen; + uint8_t* recordBuffer = new uint8_t[tagSize]; + uint16_t tagTypeVal = htobe16(static_cast(m_RecType)); + uint16_t tagLength = htobe16(static_cast(m_RecValueLen)); + memcpy(recordBuffer, &tagTypeVal, sizeof(uint16_t)); + memcpy(recordBuffer + sizeof(uint16_t), &tagLength, sizeof(uint16_t)); + if (tagLength > 0 && m_RecValue != nullptr) + memcpy(recordBuffer + 2 * sizeof(uint16_t), m_RecValue, m_RecValueLen); + + return PPPoEDiscoveryLayer::PPPoETag(recordBuffer); } -PPPoEDiscoveryLayer::PPPoETag PPPoEDiscoveryLayer::getTag(PPPoEDiscoveryLayer::PPPoETagTypes tagType) const -{ - return m_TagReader.getTLVRecord(static_cast(tagType), getTagBasePtr(), m_DataLen - sizeof(pppoe_header)); +PPPoEDiscoveryLayer::PPPoETag +PPPoEDiscoveryLayer::getTag(PPPoEDiscoveryLayer::PPPoETagTypes tagType) const { + return m_TagReader.getTLVRecord(static_cast(tagType), + getTagBasePtr(), + m_DataLen - sizeof(pppoe_header)); } -PPPoEDiscoveryLayer::PPPoETag PPPoEDiscoveryLayer::getFirstTag() const -{ - return m_TagReader.getFirstTLVRecord(getTagBasePtr(), m_DataLen - sizeof(pppoe_header)); +PPPoEDiscoveryLayer::PPPoETag PPPoEDiscoveryLayer::getFirstTag() const { + return m_TagReader.getFirstTLVRecord(getTagBasePtr(), + m_DataLen - sizeof(pppoe_header)); } -PPPoEDiscoveryLayer::PPPoETag PPPoEDiscoveryLayer::getNextTag(const PPPoEDiscoveryLayer::PPPoETag& tag) const -{ - return m_TagReader.getNextTLVRecord(const_cast(tag), getTagBasePtr(), m_DataLen - sizeof(pppoe_header)); +PPPoEDiscoveryLayer::PPPoETag PPPoEDiscoveryLayer::getNextTag( + const PPPoEDiscoveryLayer::PPPoETag& tag) const { + return m_TagReader.getNextTLVRecord( + const_cast(tag), getTagBasePtr(), + m_DataLen - sizeof(pppoe_header)); } -int PPPoEDiscoveryLayer::getTagCount() const -{ - return m_TagReader.getTLVRecordCount(getTagBasePtr(), m_DataLen - sizeof(pppoe_header)); +int PPPoEDiscoveryLayer::getTagCount() const { + return m_TagReader.getTLVRecordCount(getTagBasePtr(), + m_DataLen - sizeof(pppoe_header)); } -PPPoEDiscoveryLayer::PPPoETag PPPoEDiscoveryLayer::addTagAt(const PPPoETagBuilder& tagBuilder, int offset) -{ - PPPoETag newTag = tagBuilder.build(); - if (newTag.isNull()) - { - PCPP_LOG_ERROR("Cannot build new tag of type " << (int)newTag.getType()); - return newTag; - } +PPPoEDiscoveryLayer::PPPoETag +PPPoEDiscoveryLayer::addTagAt(const PPPoETagBuilder& tagBuilder, int offset) { + PPPoETag newTag = tagBuilder.build(); + if (newTag.isNull()) { + PCPP_LOG_ERROR("Cannot build new tag of type " << (int)newTag.getType()); + return newTag; + } - size_t sizeToExtend = newTag.getTotalSize(); + size_t sizeToExtend = newTag.getTotalSize(); - if (!extendLayer(offset, sizeToExtend)) - { - PCPP_LOG_ERROR("Could not extend PPPoEDiscoveryLayer in [" << sizeToExtend << "] bytes"); - newTag.purgeRecordData(); - return PPPoETag(nullptr); - } + if (!extendLayer(offset, sizeToExtend)) { + PCPP_LOG_ERROR("Could not extend PPPoEDiscoveryLayer in [" << sizeToExtend + << "] bytes"); + newTag.purgeRecordData(); + return PPPoETag(nullptr); + } - memcpy(m_Data + offset, newTag.getRecordBasePtr(), newTag.getTotalSize()); + memcpy(m_Data + offset, newTag.getRecordBasePtr(), newTag.getTotalSize()); - uint8_t* newTagPtr = m_Data + offset; + uint8_t* newTagPtr = m_Data + offset; - m_TagReader.changeTLVRecordCount(1); + m_TagReader.changeTLVRecordCount(1); - newTag.purgeRecordData(); + newTag.purgeRecordData(); - getPPPoEHeader()->payloadLength += htobe16(sizeToExtend); + getPPPoEHeader()->payloadLength += htobe16(sizeToExtend); - return PPPoETag(newTagPtr); + return PPPoETag(newTagPtr); } -PPPoEDiscoveryLayer::PPPoETag PPPoEDiscoveryLayer::addTagAfter(const PPPoETagBuilder& tagBuilder, PPPoETagTypes prevTagType) -{ - int offset = 0; +PPPoEDiscoveryLayer::PPPoETag +PPPoEDiscoveryLayer::addTagAfter(const PPPoETagBuilder& tagBuilder, + PPPoETagTypes prevTagType) { + int offset = 0; - PPPoETag prevTag = getTag(prevTagType); + PPPoETag prevTag = getTag(prevTagType); - if (prevTag.isNull()) - { - offset = getHeaderLen(); - } - else - { - offset = prevTag.getRecordBasePtr() + prevTag.getTotalSize() - m_Data; - } + if (prevTag.isNull()) { + offset = getHeaderLen(); + } else { + offset = prevTag.getRecordBasePtr() + prevTag.getTotalSize() - m_Data; + } - return addTagAt(tagBuilder, offset); + return addTagAt(tagBuilder, offset); } -PPPoEDiscoveryLayer::PPPoETag PPPoEDiscoveryLayer::addTag(const PPPoETagBuilder& tagBuilder) -{ - return addTagAt(tagBuilder, getHeaderLen()); +PPPoEDiscoveryLayer::PPPoETag +PPPoEDiscoveryLayer::addTag(const PPPoETagBuilder& tagBuilder) { + return addTagAt(tagBuilder, getHeaderLen()); } -size_t PPPoEDiscoveryLayer::getHeaderLen() const -{ - size_t payloadLen = sizeof(pppoe_header) + be16toh(getPPPoEHeader()->payloadLength); - if (payloadLen > m_DataLen) - return m_DataLen; +size_t PPPoEDiscoveryLayer::getHeaderLen() const { + size_t payloadLen = + sizeof(pppoe_header) + be16toh(getPPPoEHeader()->payloadLength); + if (payloadLen > m_DataLen) + return m_DataLen; - return payloadLen; + return payloadLen; } -bool PPPoEDiscoveryLayer::removeTag(PPPoEDiscoveryLayer::PPPoETagTypes tagType) -{ - PPPoEDiscoveryLayer::PPPoETag tagToRemove = getTag(tagType); - if (tagToRemove.isNull()) - { - PCPP_LOG_ERROR("Couldn't find tag"); - return false; - } +bool PPPoEDiscoveryLayer::removeTag( + PPPoEDiscoveryLayer::PPPoETagTypes tagType) { + PPPoEDiscoveryLayer::PPPoETag tagToRemove = getTag(tagType); + if (tagToRemove.isNull()) { + PCPP_LOG_ERROR("Couldn't find tag"); + return false; + } - int offset = tagToRemove.getRecordBasePtr() - m_Data; + int offset = tagToRemove.getRecordBasePtr() - m_Data; - uint16_t tagTotalSize = tagToRemove.getTotalSize(); + uint16_t tagTotalSize = tagToRemove.getTotalSize(); - if (!shortenLayer(offset, tagTotalSize)) - { - return false; - } + if (!shortenLayer(offset, tagTotalSize)) { + return false; + } - m_TagReader.changeTLVRecordCount(-1); + m_TagReader.changeTLVRecordCount(-1); - getPPPoEHeader()->payloadLength -= htobe16(tagTotalSize); - return true; + getPPPoEHeader()->payloadLength -= htobe16(tagTotalSize); + return true; } -bool PPPoEDiscoveryLayer::removeAllTags() -{ - size_t tagCount = getTagCount(); - int offset = sizeof(pppoe_header); - if (!shortenLayer(offset, m_DataLen-offset)) - { - return false; - } - m_TagReader.changeTLVRecordCount(0-tagCount); - getPPPoEHeader()->payloadLength = 0; - return true; +bool PPPoEDiscoveryLayer::removeAllTags() { + size_t tagCount = getTagCount(); + int offset = sizeof(pppoe_header); + if (!shortenLayer(offset, m_DataLen - offset)) { + return false; + } + m_TagReader.changeTLVRecordCount(0 - tagCount); + getPPPoEHeader()->payloadLength = 0; + return true; } -std::string PPPoEDiscoveryLayer::codeToString(PPPoECode code) const -{ - switch (code) - { - case PPPoELayer::PPPOE_CODE_SESSION:return std::string("PPPoE Session"); - case PPPoELayer::PPPOE_CODE_PADO: return std::string("PADO"); - case PPPoELayer::PPPOE_CODE_PADI: return std::string("PADI"); - case PPPoELayer::PPPOE_CODE_PADG: return std::string("PADG"); - case PPPoELayer::PPPOE_CODE_PADC: return std::string("PADC"); - case PPPoELayer::PPPOE_CODE_PADQ: return std::string("PADQ"); - case PPPoELayer::PPPOE_CODE_PADR: return std::string("PADR"); - case PPPoELayer::PPPOE_CODE_PADS: return std::string("PADS"); - case PPPoELayer::PPPOE_CODE_PADT: return std::string("PADT"); - case PPPoELayer::PPPOE_CODE_PADM: return std::string("PADM"); - case PPPoELayer::PPPOE_CODE_PADN: return std::string("PADN"); - default: return std::string("Unknown PPPoE code"); - } +std::string PPPoEDiscoveryLayer::codeToString(PPPoECode code) const { + switch (code) { + case PPPoELayer::PPPOE_CODE_SESSION: + return std::string("PPPoE Session"); + case PPPoELayer::PPPOE_CODE_PADO: + return std::string("PADO"); + case PPPoELayer::PPPOE_CODE_PADI: + return std::string("PADI"); + case PPPoELayer::PPPOE_CODE_PADG: + return std::string("PADG"); + case PPPoELayer::PPPOE_CODE_PADC: + return std::string("PADC"); + case PPPoELayer::PPPOE_CODE_PADQ: + return std::string("PADQ"); + case PPPoELayer::PPPOE_CODE_PADR: + return std::string("PADR"); + case PPPoELayer::PPPOE_CODE_PADS: + return std::string("PADS"); + case PPPoELayer::PPPOE_CODE_PADT: + return std::string("PADT"); + case PPPoELayer::PPPOE_CODE_PADM: + return std::string("PADM"); + case PPPoELayer::PPPOE_CODE_PADN: + return std::string("PADN"); + default: + return std::string("Unknown PPPoE code"); + } } } // namespace pcpp diff --git a/Packet++/src/Packet.cpp b/Packet++/src/Packet.cpp index 339efeba5c..32bfdfedaf 100644 --- a/Packet++/src/Packet.cpp +++ b/Packet++/src/Packet.cpp @@ -1,816 +1,767 @@ #define LOG_MODULE PacketLogModulePacket #include "Packet.h" -#include "EthLayer.h" +#include "EndianPortable.h" #include "EthDot3Layer.h" -#include "SllLayer.h" -#include "Sll2Layer.h" -#include "NflogLayer.h" -#include "NullLoopbackLayer.h" +#include "EthLayer.h" #include "IPv4Layer.h" #include "IPv6Layer.h" -#include "PayloadLayer.h" -#include "PacketTrailerLayer.h" #include "Logger.h" -#include "EndianPortable.h" +#include "NflogLayer.h" +#include "NullLoopbackLayer.h" +#include "PacketTrailerLayer.h" +#include "PayloadLayer.h" +#include "Sll2Layer.h" +#include "SllLayer.h" +#include #include #include -#include #ifdef _MSC_VER -#include #include "SystemUtils.h" +#include #endif - -namespace pcpp -{ - -Packet::Packet(size_t maxPacketLen) : - m_RawPacket(nullptr), - m_FirstLayer(nullptr), - m_LastLayer(nullptr), - m_ProtocolTypes(UnknownProtocol), - m_MaxPacketLen(maxPacketLen), - m_FreeRawPacket(true), - m_CanReallocateData(true) -{ - timeval time; - gettimeofday(&time, nullptr); - uint8_t* data = new uint8_t[maxPacketLen]; - memset(data, 0, maxPacketLen); - m_RawPacket = new RawPacket(data, 0, time, true, LINKTYPE_ETHERNET); +namespace pcpp { + +Packet::Packet(size_t maxPacketLen) + : m_RawPacket(nullptr), m_FirstLayer(nullptr), m_LastLayer(nullptr), + m_ProtocolTypes(UnknownProtocol), m_MaxPacketLen(maxPacketLen), + m_FreeRawPacket(true), m_CanReallocateData(true) { + timeval time; + gettimeofday(&time, nullptr); + uint8_t* data = new uint8_t[maxPacketLen]; + memset(data, 0, maxPacketLen); + m_RawPacket = new RawPacket(data, 0, time, true, LINKTYPE_ETHERNET); } -Packet::Packet(uint8_t* buffer, size_t bufferSize) : - m_RawPacket(nullptr), - m_FirstLayer(nullptr), - m_LastLayer(nullptr), - m_ProtocolTypes(UnknownProtocol), - m_MaxPacketLen(bufferSize), - m_FreeRawPacket(true), - m_CanReallocateData(false) -{ - timeval time; - gettimeofday(&time, nullptr); - memset(buffer, 0, bufferSize); - m_RawPacket = new RawPacket(buffer, 0, time, false, LINKTYPE_ETHERNET); +Packet::Packet(uint8_t* buffer, size_t bufferSize) + : m_RawPacket(nullptr), m_FirstLayer(nullptr), m_LastLayer(nullptr), + m_ProtocolTypes(UnknownProtocol), m_MaxPacketLen(bufferSize), + m_FreeRawPacket(true), m_CanReallocateData(false) { + timeval time; + gettimeofday(&time, nullptr); + memset(buffer, 0, bufferSize); + m_RawPacket = new RawPacket(buffer, 0, time, false, LINKTYPE_ETHERNET); } -void Packet::setRawPacket(RawPacket* rawPacket, bool freeRawPacket, ProtocolType parseUntil, OsiModelLayer parseUntilLayer) -{ - destructPacketData(); - - m_FirstLayer = nullptr; - m_LastLayer = nullptr; - m_ProtocolTypes = UnknownProtocol; - m_MaxPacketLen = rawPacket->getRawDataLen(); - m_FreeRawPacket = freeRawPacket; - m_RawPacket = rawPacket; - m_CanReallocateData = true; - if (m_RawPacket == nullptr) - return; - - LinkLayerType linkType = m_RawPacket->getLinkLayerType(); - - m_FirstLayer = createFirstLayer(linkType); - - m_LastLayer = m_FirstLayer; - Layer* curLayer = m_FirstLayer; - while (curLayer != nullptr && (curLayer->getProtocol() & parseUntil) == 0 && curLayer->getOsiModelLayer() <= parseUntilLayer) - { - m_ProtocolTypes |= curLayer->getProtocol(); - curLayer->parseNextLayer(); - curLayer->m_IsAllocatedInPacket = true; - curLayer = curLayer->getNextLayer(); - if (curLayer != nullptr) - m_LastLayer = curLayer; - } - - if (curLayer != nullptr && (curLayer->getProtocol() & parseUntil) != 0) - { - m_ProtocolTypes |= curLayer->getProtocol(); - curLayer->m_IsAllocatedInPacket = true; - } - - if (curLayer != nullptr && curLayer->getOsiModelLayer() > parseUntilLayer) - { - m_LastLayer = curLayer->getPrevLayer(); - delete curLayer; - m_LastLayer->m_NextLayer = nullptr; - } - - if (m_LastLayer != nullptr && parseUntil == UnknownProtocol && parseUntilLayer == OsiModelLayerUnknown) - { - // find if there is data left in the raw packet that doesn't belong to any layer. In that case it's probably a packet trailer. - // create a PacketTrailerLayer layer and add it at the end of the packet - int trailerLen = (int)((m_RawPacket->getRawData() + m_RawPacket->getRawDataLen()) - (m_LastLayer->getData() + m_LastLayer->getDataLen())); - if (trailerLen > 0) - { - PacketTrailerLayer* trailerLayer = new PacketTrailerLayer( - (uint8_t*)(m_LastLayer->getData() + m_LastLayer->getDataLen()), - trailerLen, - m_LastLayer, - this); - - trailerLayer->m_IsAllocatedInPacket = true; - m_LastLayer->setNextLayer(trailerLayer); - m_LastLayer = trailerLayer; - m_ProtocolTypes |= trailerLayer->getProtocol(); - } - } +void Packet::setRawPacket(RawPacket* rawPacket, bool freeRawPacket, + ProtocolType parseUntil, + OsiModelLayer parseUntilLayer) { + destructPacketData(); + + m_FirstLayer = nullptr; + m_LastLayer = nullptr; + m_ProtocolTypes = UnknownProtocol; + m_MaxPacketLen = rawPacket->getRawDataLen(); + m_FreeRawPacket = freeRawPacket; + m_RawPacket = rawPacket; + m_CanReallocateData = true; + if (m_RawPacket == nullptr) + return; + + LinkLayerType linkType = m_RawPacket->getLinkLayerType(); + + m_FirstLayer = createFirstLayer(linkType); + + m_LastLayer = m_FirstLayer; + Layer* curLayer = m_FirstLayer; + while (curLayer != nullptr && (curLayer->getProtocol() & parseUntil) == 0 && + curLayer->getOsiModelLayer() <= parseUntilLayer) { + m_ProtocolTypes |= curLayer->getProtocol(); + curLayer->parseNextLayer(); + curLayer->m_IsAllocatedInPacket = true; + curLayer = curLayer->getNextLayer(); + if (curLayer != nullptr) + m_LastLayer = curLayer; + } + + if (curLayer != nullptr && (curLayer->getProtocol() & parseUntil) != 0) { + m_ProtocolTypes |= curLayer->getProtocol(); + curLayer->m_IsAllocatedInPacket = true; + } + + if (curLayer != nullptr && curLayer->getOsiModelLayer() > parseUntilLayer) { + m_LastLayer = curLayer->getPrevLayer(); + delete curLayer; + m_LastLayer->m_NextLayer = nullptr; + } + + if (m_LastLayer != nullptr && parseUntil == UnknownProtocol && + parseUntilLayer == OsiModelLayerUnknown) { + // find if there is data left in the raw packet that doesn't belong to any + // layer. In that case it's probably a packet trailer. create a + // PacketTrailerLayer layer and add it at the end of the packet + int trailerLen = + (int)((m_RawPacket->getRawData() + m_RawPacket->getRawDataLen()) - + (m_LastLayer->getData() + m_LastLayer->getDataLen())); + if (trailerLen > 0) { + PacketTrailerLayer* trailerLayer = new PacketTrailerLayer( + (uint8_t*)(m_LastLayer->getData() + m_LastLayer->getDataLen()), + trailerLen, m_LastLayer, this); + + trailerLayer->m_IsAllocatedInPacket = true; + m_LastLayer->setNextLayer(trailerLayer); + m_LastLayer = trailerLayer; + m_ProtocolTypes |= trailerLayer->getProtocol(); + } + } } -Packet::Packet(RawPacket* rawPacket, bool freeRawPacket, ProtocolType parseUntil, OsiModelLayer parseUntilLayer) -{ - m_FreeRawPacket = false; - m_RawPacket = nullptr; - m_FirstLayer = nullptr; - setRawPacket(rawPacket, freeRawPacket, parseUntil, parseUntilLayer); +Packet::Packet(RawPacket* rawPacket, bool freeRawPacket, + ProtocolType parseUntil, OsiModelLayer parseUntilLayer) { + m_FreeRawPacket = false; + m_RawPacket = nullptr; + m_FirstLayer = nullptr; + setRawPacket(rawPacket, freeRawPacket, parseUntil, parseUntilLayer); } -Packet::Packet(RawPacket* rawPacket, ProtocolType parseUntil) -{ - m_FreeRawPacket = false; - m_RawPacket = nullptr; - m_FirstLayer = nullptr; - setRawPacket(rawPacket, false, parseUntil, OsiModelLayerUnknown); +Packet::Packet(RawPacket* rawPacket, ProtocolType parseUntil) { + m_FreeRawPacket = false; + m_RawPacket = nullptr; + m_FirstLayer = nullptr; + setRawPacket(rawPacket, false, parseUntil, OsiModelLayerUnknown); } -Packet::Packet(RawPacket* rawPacket, OsiModelLayer parseUntilLayer) -{ - m_FreeRawPacket = false; - m_RawPacket = nullptr; - m_FirstLayer = nullptr; - setRawPacket(rawPacket, false, UnknownProtocol, parseUntilLayer); +Packet::Packet(RawPacket* rawPacket, OsiModelLayer parseUntilLayer) { + m_FreeRawPacket = false; + m_RawPacket = nullptr; + m_FirstLayer = nullptr; + setRawPacket(rawPacket, false, UnknownProtocol, parseUntilLayer); } -void Packet::destructPacketData() -{ - Layer* curLayer = m_FirstLayer; - while (curLayer != nullptr) - { - Layer* nextLayer = curLayer->getNextLayer(); - if (curLayer->m_IsAllocatedInPacket) - delete curLayer; - curLayer = nextLayer; - } - - if (m_RawPacket != nullptr && m_FreeRawPacket) - { - delete m_RawPacket; - } +void Packet::destructPacketData() { + Layer* curLayer = m_FirstLayer; + while (curLayer != nullptr) { + Layer* nextLayer = curLayer->getNextLayer(); + if (curLayer->m_IsAllocatedInPacket) + delete curLayer; + curLayer = nextLayer; + } + + if (m_RawPacket != nullptr && m_FreeRawPacket) { + delete m_RawPacket; + } } -Packet& Packet::operator=(const Packet& other) -{ - destructPacketData(); +Packet& Packet::operator=(const Packet& other) { + destructPacketData(); - copyDataFrom(other); + copyDataFrom(other); - return *this; + return *this; } -void Packet::copyDataFrom(const Packet& other) -{ - m_RawPacket = new RawPacket(*(other.m_RawPacket)); - m_FreeRawPacket = true; - m_MaxPacketLen = other.m_MaxPacketLen; - m_ProtocolTypes = other.m_ProtocolTypes; - m_FirstLayer = createFirstLayer(m_RawPacket->getLinkLayerType()); - m_LastLayer = m_FirstLayer; - m_CanReallocateData = true; - Layer* curLayer = m_FirstLayer; - while (curLayer != nullptr) - { - curLayer->parseNextLayer(); - curLayer->m_IsAllocatedInPacket = true; - curLayer = curLayer->getNextLayer(); - if (curLayer != nullptr) - m_LastLayer = curLayer; - } +void Packet::copyDataFrom(const Packet& other) { + m_RawPacket = new RawPacket(*(other.m_RawPacket)); + m_FreeRawPacket = true; + m_MaxPacketLen = other.m_MaxPacketLen; + m_ProtocolTypes = other.m_ProtocolTypes; + m_FirstLayer = createFirstLayer(m_RawPacket->getLinkLayerType()); + m_LastLayer = m_FirstLayer; + m_CanReallocateData = true; + Layer* curLayer = m_FirstLayer; + while (curLayer != nullptr) { + curLayer->parseNextLayer(); + curLayer->m_IsAllocatedInPacket = true; + curLayer = curLayer->getNextLayer(); + if (curLayer != nullptr) + m_LastLayer = curLayer; + } } -void Packet::reallocateRawData(size_t newSize) -{ - PCPP_LOG_DEBUG("Allocating packet to new size: " << newSize); - - // allocate a new array with size newSize - m_MaxPacketLen = newSize; - - // set the new array to RawPacket - if (!m_RawPacket->reallocateData(m_MaxPacketLen)) - { - PCPP_LOG_ERROR("Couldn't reallocate data of raw packet to " << m_MaxPacketLen << " bytes"); - return; - } - - // set all data pointers in layers to the new array address - const uint8_t* dataPtr = m_RawPacket->getRawData(); - - Layer* curLayer = m_FirstLayer; - while (curLayer != nullptr) - { - PCPP_LOG_DEBUG("Setting new data pointer to layer '" << typeid(curLayer).name() << "'"); - curLayer->m_Data = (uint8_t*)dataPtr; - dataPtr += curLayer->getHeaderLen(); - curLayer = curLayer->getNextLayer(); - } +void Packet::reallocateRawData(size_t newSize) { + PCPP_LOG_DEBUG("Allocating packet to new size: " << newSize); + + // allocate a new array with size newSize + m_MaxPacketLen = newSize; + + // set the new array to RawPacket + if (!m_RawPacket->reallocateData(m_MaxPacketLen)) { + PCPP_LOG_ERROR("Couldn't reallocate data of raw packet to " + << m_MaxPacketLen << " bytes"); + return; + } + + // set all data pointers in layers to the new array address + const uint8_t* dataPtr = m_RawPacket->getRawData(); + + Layer* curLayer = m_FirstLayer; + while (curLayer != nullptr) { + PCPP_LOG_DEBUG("Setting new data pointer to layer '" + << typeid(curLayer).name() << "'"); + curLayer->m_Data = (uint8_t*)dataPtr; + dataPtr += curLayer->getHeaderLen(); + curLayer = curLayer->getNextLayer(); + } } -bool Packet::insertLayer(Layer* prevLayer, Layer* newLayer, bool ownInPacket) -{ - if (newLayer == nullptr) - { - PCPP_LOG_ERROR("Layer to add is NULL"); - return false; - } - - if (newLayer->isAllocatedToPacket()) - { - PCPP_LOG_ERROR("Layer is already allocated to another packet. Cannot use layer in more than one packet"); - return false; - } - - if (prevLayer != nullptr && prevLayer->getProtocol() == PacketTrailer) - { - PCPP_LOG_ERROR("Cannot insert layer after packet trailer"); - return false; - } - - size_t newLayerHeaderLen = newLayer->getHeaderLen(); - if (m_RawPacket->getRawDataLen() + newLayerHeaderLen > m_MaxPacketLen) - { - if (!m_CanReallocateData) - { - PCPP_LOG_ERROR("With the new layer the packet will exceed the size of the pre-allocated buffer: " << m_MaxPacketLen << " bytes"); - return false; - } - // reallocate to maximum value of: twice the max size of the packet or max size + new required length - if (m_RawPacket->getRawDataLen() + newLayerHeaderLen > m_MaxPacketLen*2) - reallocateRawData(m_RawPacket->getRawDataLen() + newLayerHeaderLen + m_MaxPacketLen); - else - reallocateRawData(m_MaxPacketLen*2); - } - - // insert layer data to raw packet - int indexToInsertData = 0; - if (prevLayer != nullptr) - indexToInsertData = prevLayer->m_Data + prevLayer->getHeaderLen() - m_RawPacket->getRawData(); - m_RawPacket->insertData(indexToInsertData, newLayer->m_Data, newLayerHeaderLen); - - //delete previous layer data - delete[] newLayer->m_Data; - - // add layer to layers linked list - if (prevLayer != nullptr) - { - newLayer->setNextLayer(prevLayer->getNextLayer()); - newLayer->setPrevLayer(prevLayer); - prevLayer->setNextLayer(newLayer); - } - else //prevLayer == NULL - { - newLayer->setNextLayer(m_FirstLayer); - if (m_FirstLayer != nullptr) - m_FirstLayer->setPrevLayer(newLayer); - m_FirstLayer = newLayer; - } - - if (newLayer->getNextLayer() == nullptr) - m_LastLayer = newLayer; - else - newLayer->getNextLayer()->setPrevLayer(newLayer); - - // assign layer with this packet only - newLayer->m_Packet = this; - - // Set flag to indicate if new layer is allocated to packet. - if(ownInPacket) - newLayer->m_IsAllocatedInPacket = true; - - // re-calculate all layers data ptr and data length - - // first, get ptr and data length of the raw packet - const uint8_t* dataPtr = m_RawPacket->getRawData(); - size_t dataLen = (size_t)m_RawPacket->getRawDataLen(); - - // if a packet trailer exists, get its length - size_t packetTrailerLen = 0; - if (m_LastLayer != nullptr && m_LastLayer->getProtocol() == PacketTrailer) - packetTrailerLen = m_LastLayer->getDataLen(); - - // go over all layers from the first layer to the last layer and set the data ptr and data length for each one - Layer* curLayer = m_FirstLayer; - while (curLayer != nullptr) - { - // set data ptr to layer - curLayer->m_Data = (uint8_t*)dataPtr; - - // there is an assumption here that the packet trailer, if exists, corresponds to the L2 (data link) layers. - // so if there is a packet trailer and this layer is L2 (data link), set its data length to contain the whole data, including the - // packet trailer. If this layer is L3-7, exclude the packet trailer from its data length - if (curLayer->getOsiModelLayer() == OsiModelDataLinkLayer) - curLayer->m_DataLen = dataLen; - else - curLayer->m_DataLen = dataLen - packetTrailerLen; - - // advance data ptr and data length - dataPtr += curLayer->getHeaderLen(); - dataLen -= curLayer->getHeaderLen(); - - // move to next layer - curLayer = curLayer->getNextLayer(); - } - - // add layer protocol to protocol collection - m_ProtocolTypes |= newLayer->getProtocol(); - return true; +bool Packet::insertLayer(Layer* prevLayer, Layer* newLayer, bool ownInPacket) { + if (newLayer == nullptr) { + PCPP_LOG_ERROR("Layer to add is NULL"); + return false; + } + + if (newLayer->isAllocatedToPacket()) { + PCPP_LOG_ERROR("Layer is already allocated to another packet. Cannot use " + "layer in more than one packet"); + return false; + } + + if (prevLayer != nullptr && prevLayer->getProtocol() == PacketTrailer) { + PCPP_LOG_ERROR("Cannot insert layer after packet trailer"); + return false; + } + + size_t newLayerHeaderLen = newLayer->getHeaderLen(); + if (m_RawPacket->getRawDataLen() + newLayerHeaderLen > m_MaxPacketLen) { + if (!m_CanReallocateData) { + PCPP_LOG_ERROR("With the new layer the packet will exceed the size of " + "the pre-allocated buffer: " + << m_MaxPacketLen << " bytes"); + return false; + } + // reallocate to maximum value of: twice the max size of the packet or max + // size + new required length + if (m_RawPacket->getRawDataLen() + newLayerHeaderLen > m_MaxPacketLen * 2) + reallocateRawData(m_RawPacket->getRawDataLen() + newLayerHeaderLen + + m_MaxPacketLen); + else + reallocateRawData(m_MaxPacketLen * 2); + } + + // insert layer data to raw packet + int indexToInsertData = 0; + if (prevLayer != nullptr) + indexToInsertData = prevLayer->m_Data + prevLayer->getHeaderLen() - + m_RawPacket->getRawData(); + m_RawPacket->insertData(indexToInsertData, newLayer->m_Data, + newLayerHeaderLen); + + // delete previous layer data + delete[] newLayer->m_Data; + + // add layer to layers linked list + if (prevLayer != nullptr) { + newLayer->setNextLayer(prevLayer->getNextLayer()); + newLayer->setPrevLayer(prevLayer); + prevLayer->setNextLayer(newLayer); + } else // prevLayer == NULL + { + newLayer->setNextLayer(m_FirstLayer); + if (m_FirstLayer != nullptr) + m_FirstLayer->setPrevLayer(newLayer); + m_FirstLayer = newLayer; + } + + if (newLayer->getNextLayer() == nullptr) + m_LastLayer = newLayer; + else + newLayer->getNextLayer()->setPrevLayer(newLayer); + + // assign layer with this packet only + newLayer->m_Packet = this; + + // Set flag to indicate if new layer is allocated to packet. + if (ownInPacket) + newLayer->m_IsAllocatedInPacket = true; + + // re-calculate all layers data ptr and data length + + // first, get ptr and data length of the raw packet + const uint8_t* dataPtr = m_RawPacket->getRawData(); + size_t dataLen = (size_t)m_RawPacket->getRawDataLen(); + + // if a packet trailer exists, get its length + size_t packetTrailerLen = 0; + if (m_LastLayer != nullptr && m_LastLayer->getProtocol() == PacketTrailer) + packetTrailerLen = m_LastLayer->getDataLen(); + + // go over all layers from the first layer to the last layer and set the data + // ptr and data length for each one + Layer* curLayer = m_FirstLayer; + while (curLayer != nullptr) { + // set data ptr to layer + curLayer->m_Data = (uint8_t*)dataPtr; + + // there is an assumption here that the packet trailer, if exists, + // corresponds to the L2 (data link) layers. so if there is a packet trailer + // and this layer is L2 (data link), set its data length to contain the + // whole data, including the packet trailer. If this layer is L3-7, exclude + // the packet trailer from its data length + if (curLayer->getOsiModelLayer() == OsiModelDataLinkLayer) + curLayer->m_DataLen = dataLen; + else + curLayer->m_DataLen = dataLen - packetTrailerLen; + + // advance data ptr and data length + dataPtr += curLayer->getHeaderLen(); + dataLen -= curLayer->getHeaderLen(); + + // move to next layer + curLayer = curLayer->getNextLayer(); + } + + // add layer protocol to protocol collection + m_ProtocolTypes |= newLayer->getProtocol(); + return true; } -bool Packet::removeLayer(ProtocolType layerType, int index) -{ - Layer* layerToRemove = getLayerOfType(layerType, index); - - if (layerToRemove != nullptr) - { - return removeLayer(layerToRemove, true); - } - else - { - PCPP_LOG_ERROR("Layer of the requested type was not found in packet"); - return false; - } +bool Packet::removeLayer(ProtocolType layerType, int index) { + Layer* layerToRemove = getLayerOfType(layerType, index); + + if (layerToRemove != nullptr) { + return removeLayer(layerToRemove, true); + } else { + PCPP_LOG_ERROR("Layer of the requested type was not found in packet"); + return false; + } } -bool Packet::removeFirstLayer() -{ - Layer* firstLayer = getFirstLayer(); - if (firstLayer == nullptr) - { - PCPP_LOG_ERROR("Packet has no layers"); - return false; - } +bool Packet::removeFirstLayer() { + Layer* firstLayer = getFirstLayer(); + if (firstLayer == nullptr) { + PCPP_LOG_ERROR("Packet has no layers"); + return false; + } - return removeLayer(firstLayer, true); + return removeLayer(firstLayer, true); } -bool Packet::removeLastLayer() -{ - Layer* lastLayer = getLastLayer(); - if (lastLayer == nullptr) - { - PCPP_LOG_ERROR("Packet has no layers"); - return false; - } +bool Packet::removeLastLayer() { + Layer* lastLayer = getLastLayer(); + if (lastLayer == nullptr) { + PCPP_LOG_ERROR("Packet has no layers"); + return false; + } - return removeLayer(lastLayer, true); + return removeLayer(lastLayer, true); } -bool Packet::removeAllLayersAfter(Layer* layer) -{ - Layer* curLayer = layer->getNextLayer(); - while (curLayer != nullptr) - { - Layer* tempLayer = curLayer->getNextLayer(); - if (!removeLayer(curLayer, true)) - return false; - curLayer = tempLayer; - } - - return true; +bool Packet::removeAllLayersAfter(Layer* layer) { + Layer* curLayer = layer->getNextLayer(); + while (curLayer != nullptr) { + Layer* tempLayer = curLayer->getNextLayer(); + if (!removeLayer(curLayer, true)) + return false; + curLayer = tempLayer; + } + + return true; } -Layer* Packet::detachLayer(ProtocolType layerType, int index) -{ - Layer* layerToDetach = getLayerOfType(layerType, index); - - if (layerToDetach != nullptr) - { - if (removeLayer(layerToDetach, false)) - return layerToDetach; - else - return nullptr; - } - else - { - PCPP_LOG_ERROR("Layer of the requested type was not found in packet"); - return nullptr; - } +Layer* Packet::detachLayer(ProtocolType layerType, int index) { + Layer* layerToDetach = getLayerOfType(layerType, index); + + if (layerToDetach != nullptr) { + if (removeLayer(layerToDetach, false)) + return layerToDetach; + else + return nullptr; + } else { + PCPP_LOG_ERROR("Layer of the requested type was not found in packet"); + return nullptr; + } } -bool Packet::removeLayer(Layer* layer, bool tryToDelete) -{ - if (layer == nullptr) - { - PCPP_LOG_ERROR("Layer is NULL"); - return false; - } - - // verify layer is allocated to a packet - if (!layer->isAllocatedToPacket()) - { - PCPP_LOG_ERROR("Layer isn't allocated to any packet"); - return false; - } - - // verify layer is allocated to *this* packet - Layer* curLayer = layer; - while (curLayer->m_PrevLayer != nullptr) - curLayer = curLayer->m_PrevLayer; - if (curLayer != m_FirstLayer) - { - PCPP_LOG_ERROR("Layer isn't allocated to this packet"); - return false; - } - - // before removing the layer's data, copy it so it can be later assigned as the removed layer's data - size_t headerLen = layer->getHeaderLen(); - size_t layerOldDataSize = headerLen; - uint8_t* layerOldData = new uint8_t[layerOldDataSize]; - memcpy(layerOldData, layer->m_Data, layerOldDataSize); - - // remove data from raw packet - size_t numOfBytesToRemove = headerLen; - int indexOfDataToRemove = layer->m_Data - m_RawPacket->getRawData(); - if (!m_RawPacket->removeData(indexOfDataToRemove, numOfBytesToRemove)) - { - PCPP_LOG_ERROR("Couldn't remove data from packet"); - delete [] layerOldData; - return false; - } - - // remove layer from layers linked list - if (layer->m_PrevLayer != nullptr) - layer->m_PrevLayer->setNextLayer(layer->m_NextLayer); - if (layer->m_NextLayer != nullptr) - layer->m_NextLayer->setPrevLayer(layer->m_PrevLayer); - - // take care of head and tail ptrs - if (m_FirstLayer == layer) - m_FirstLayer = layer->m_NextLayer; - if (m_LastLayer == layer) - m_LastLayer = layer->m_PrevLayer; - layer->setNextLayer(nullptr); - layer->setPrevLayer(nullptr); - - // get packet trailer len if exists - size_t packetTrailerLen = 0; - if (m_LastLayer != nullptr && m_LastLayer->getProtocol() == PacketTrailer) - packetTrailerLen = m_LastLayer->getDataLen(); - - // re-calculate all layers data ptr and data length - - // first, get ptr and data length of the raw packet - const uint8_t* dataPtr = m_RawPacket->getRawData(); - size_t dataLen = (size_t)m_RawPacket->getRawDataLen(); - - curLayer = m_FirstLayer; - - // a flag to be set if there is another layer in this packet with the same protocol - bool anotherLayerWithSameProtocolExists = false; - - // go over all layers from the first layer to the last layer and set the data ptr and data length for each one - while (curLayer != nullptr) - { - // set data ptr to layer - curLayer->m_Data = (uint8_t*)dataPtr; - - // there is an assumption here that the packet trailer, if exists, corresponds to the L2 (data link) layers. - // so if there is a packet trailer and this layer is L2 (data link), set its data length to contain the whole data, including the - // packet trailer. If this layer is L3-7, exclude the packet trailer from its data length - if (curLayer->getOsiModelLayer() == OsiModelDataLinkLayer) - curLayer->m_DataLen = dataLen; - else - curLayer->m_DataLen = dataLen - packetTrailerLen; - - // check if current layer's protocol is the same as removed layer protocol and set the flag accordingly - if (curLayer->getProtocol() == layer->getProtocol()) - anotherLayerWithSameProtocolExists = true; - - // advance data ptr and data length - dataPtr += curLayer->getHeaderLen(); - dataLen -= curLayer->getHeaderLen(); - - // move to next layer - curLayer = curLayer->getNextLayer(); - } - - // remove layer protocol from protocol list if necessary - if (!anotherLayerWithSameProtocolExists) - m_ProtocolTypes &= ~((uint64_t)layer->getProtocol()); - - // if layer was allocated by this packet and tryToDelete flag is set, delete it - if (tryToDelete && layer->m_IsAllocatedInPacket) - { - delete layer; - delete [] layerOldData; - } - // if layer was not allocated by this packet or the tryToDelete is not set, detach it from the packet so it can be reused - else - { - layer->m_Packet = nullptr; - layer->m_Data = layerOldData; - layer->m_DataLen = layerOldDataSize; - } - - return true; +bool Packet::removeLayer(Layer* layer, bool tryToDelete) { + if (layer == nullptr) { + PCPP_LOG_ERROR("Layer is NULL"); + return false; + } + + // verify layer is allocated to a packet + if (!layer->isAllocatedToPacket()) { + PCPP_LOG_ERROR("Layer isn't allocated to any packet"); + return false; + } + + // verify layer is allocated to *this* packet + Layer* curLayer = layer; + while (curLayer->m_PrevLayer != nullptr) + curLayer = curLayer->m_PrevLayer; + if (curLayer != m_FirstLayer) { + PCPP_LOG_ERROR("Layer isn't allocated to this packet"); + return false; + } + + // before removing the layer's data, copy it so it can be later assigned as + // the removed layer's data + size_t headerLen = layer->getHeaderLen(); + size_t layerOldDataSize = headerLen; + uint8_t* layerOldData = new uint8_t[layerOldDataSize]; + memcpy(layerOldData, layer->m_Data, layerOldDataSize); + + // remove data from raw packet + size_t numOfBytesToRemove = headerLen; + int indexOfDataToRemove = layer->m_Data - m_RawPacket->getRawData(); + if (!m_RawPacket->removeData(indexOfDataToRemove, numOfBytesToRemove)) { + PCPP_LOG_ERROR("Couldn't remove data from packet"); + delete[] layerOldData; + return false; + } + + // remove layer from layers linked list + if (layer->m_PrevLayer != nullptr) + layer->m_PrevLayer->setNextLayer(layer->m_NextLayer); + if (layer->m_NextLayer != nullptr) + layer->m_NextLayer->setPrevLayer(layer->m_PrevLayer); + + // take care of head and tail ptrs + if (m_FirstLayer == layer) + m_FirstLayer = layer->m_NextLayer; + if (m_LastLayer == layer) + m_LastLayer = layer->m_PrevLayer; + layer->setNextLayer(nullptr); + layer->setPrevLayer(nullptr); + + // get packet trailer len if exists + size_t packetTrailerLen = 0; + if (m_LastLayer != nullptr && m_LastLayer->getProtocol() == PacketTrailer) + packetTrailerLen = m_LastLayer->getDataLen(); + + // re-calculate all layers data ptr and data length + + // first, get ptr and data length of the raw packet + const uint8_t* dataPtr = m_RawPacket->getRawData(); + size_t dataLen = (size_t)m_RawPacket->getRawDataLen(); + + curLayer = m_FirstLayer; + + // a flag to be set if there is another layer in this packet with the same + // protocol + bool anotherLayerWithSameProtocolExists = false; + + // go over all layers from the first layer to the last layer and set the data + // ptr and data length for each one + while (curLayer != nullptr) { + // set data ptr to layer + curLayer->m_Data = (uint8_t*)dataPtr; + + // there is an assumption here that the packet trailer, if exists, + // corresponds to the L2 (data link) layers. so if there is a packet trailer + // and this layer is L2 (data link), set its data length to contain the + // whole data, including the packet trailer. If this layer is L3-7, exclude + // the packet trailer from its data length + if (curLayer->getOsiModelLayer() == OsiModelDataLinkLayer) + curLayer->m_DataLen = dataLen; + else + curLayer->m_DataLen = dataLen - packetTrailerLen; + + // check if current layer's protocol is the same as removed layer protocol + // and set the flag accordingly + if (curLayer->getProtocol() == layer->getProtocol()) + anotherLayerWithSameProtocolExists = true; + + // advance data ptr and data length + dataPtr += curLayer->getHeaderLen(); + dataLen -= curLayer->getHeaderLen(); + + // move to next layer + curLayer = curLayer->getNextLayer(); + } + + // remove layer protocol from protocol list if necessary + if (!anotherLayerWithSameProtocolExists) + m_ProtocolTypes &= ~((uint64_t)layer->getProtocol()); + + // if layer was allocated by this packet and tryToDelete flag is set, delete + // it + if (tryToDelete && layer->m_IsAllocatedInPacket) { + delete layer; + delete[] layerOldData; + } + // if layer was not allocated by this packet or the tryToDelete is not set, + // detach it from the packet so it can be reused + else { + layer->m_Packet = nullptr; + layer->m_Data = layerOldData; + layer->m_DataLen = layerOldDataSize; + } + + return true; } -Layer* Packet::getLayerOfType(ProtocolType layerType, int index) const -{ - Layer* curLayer = getFirstLayer(); - int curIndex = 0; - while (curLayer != nullptr) - { - if (curLayer->getProtocol() == layerType) - { - if (curIndex < index) - curIndex++; - else - break; - } - curLayer = curLayer->getNextLayer(); - } - - return curLayer; +Layer* Packet::getLayerOfType(ProtocolType layerType, int index) const { + Layer* curLayer = getFirstLayer(); + int curIndex = 0; + while (curLayer != nullptr) { + if (curLayer->getProtocol() == layerType) { + if (curIndex < index) + curIndex++; + else + break; + } + curLayer = curLayer->getNextLayer(); + } + + return curLayer; } -bool Packet::extendLayer(Layer* layer, int offsetInLayer, size_t numOfBytesToExtend) -{ - if (layer == nullptr) - { - PCPP_LOG_ERROR("Layer is NULL"); - return false; - } - - // verify layer is allocated to this packet - if (!(layer->m_Packet == this)) - { - PCPP_LOG_ERROR("Layer isn't allocated to this packet"); - return false; - } - - if (m_RawPacket->getRawDataLen() + numOfBytesToExtend > m_MaxPacketLen) - { - if (!m_CanReallocateData) - { - PCPP_LOG_ERROR("With the layer extended size the packet will exceed the size of the pre-allocated buffer: " << m_MaxPacketLen << " bytes"); - return false; - } - // reallocate to maximum value of: twice the max size of the packet or max size + new required length - if (m_RawPacket->getRawDataLen() + numOfBytesToExtend > m_MaxPacketLen*2) - reallocateRawData(m_RawPacket->getRawDataLen() + numOfBytesToExtend + m_MaxPacketLen); - else - reallocateRawData(m_MaxPacketLen*2); - } - - // insert layer data to raw packet - int indexToInsertData = layer->m_Data + offsetInLayer - m_RawPacket->getRawData(); - // passing NULL to insertData will move the data by numOfBytesToExtend - // no new data has to be created for this insertion which saves at least little time - // this move operation occurs on already allocated memory, which is backed by the reallocation if's provided above - // if offsetInLayer == layer->getHeaderLen() insertData will not move any data but only increase the packet size by numOfBytesToExtend - m_RawPacket->insertData(indexToInsertData, nullptr, numOfBytesToExtend); - - // re-calculate all layers data ptr and data length - const uint8_t* dataPtr = m_RawPacket->getRawData(); - - // go over all layers from the first layer to the last layer and set the data ptr and data length for each layer - Layer* curLayer = m_FirstLayer; - bool passedExtendedLayer = false; - while (curLayer != nullptr) - { - // set the data ptr - curLayer->m_Data = (uint8_t*)dataPtr; - - // set a flag if arrived to the layer being extended - if (curLayer->getPrevLayer() == layer) - passedExtendedLayer = true; - - // change the data length only for layers who come before the extended layer. For layers who come after, data length isn't changed - if (!passedExtendedLayer) - curLayer->m_DataLen += numOfBytesToExtend; - - // assuming header length of the layer that requested to be extended hasn't been enlarged yet - size_t headerLen = curLayer->getHeaderLen() + (curLayer == layer ? numOfBytesToExtend : 0); - dataPtr += headerLen; - curLayer = curLayer->getNextLayer(); - } - - return true; +bool Packet::extendLayer(Layer* layer, int offsetInLayer, + size_t numOfBytesToExtend) { + if (layer == nullptr) { + PCPP_LOG_ERROR("Layer is NULL"); + return false; + } + + // verify layer is allocated to this packet + if (!(layer->m_Packet == this)) { + PCPP_LOG_ERROR("Layer isn't allocated to this packet"); + return false; + } + + if (m_RawPacket->getRawDataLen() + numOfBytesToExtend > m_MaxPacketLen) { + if (!m_CanReallocateData) { + PCPP_LOG_ERROR("With the layer extended size the packet will exceed the " + "size of the pre-allocated buffer: " + << m_MaxPacketLen << " bytes"); + return false; + } + // reallocate to maximum value of: twice the max size of the packet or max + // size + new required length + if (m_RawPacket->getRawDataLen() + numOfBytesToExtend > m_MaxPacketLen * 2) + reallocateRawData(m_RawPacket->getRawDataLen() + numOfBytesToExtend + + m_MaxPacketLen); + else + reallocateRawData(m_MaxPacketLen * 2); + } + + // insert layer data to raw packet + int indexToInsertData = + layer->m_Data + offsetInLayer - m_RawPacket->getRawData(); + // passing NULL to insertData will move the data by numOfBytesToExtend + // no new data has to be created for this insertion which saves at least + // little time this move operation occurs on already allocated memory, which + // is backed by the reallocation if's provided above if offsetInLayer == + // layer->getHeaderLen() insertData will not move any data but only increase + // the packet size by numOfBytesToExtend + m_RawPacket->insertData(indexToInsertData, nullptr, numOfBytesToExtend); + + // re-calculate all layers data ptr and data length + const uint8_t* dataPtr = m_RawPacket->getRawData(); + + // go over all layers from the first layer to the last layer and set the data + // ptr and data length for each layer + Layer* curLayer = m_FirstLayer; + bool passedExtendedLayer = false; + while (curLayer != nullptr) { + // set the data ptr + curLayer->m_Data = (uint8_t*)dataPtr; + + // set a flag if arrived to the layer being extended + if (curLayer->getPrevLayer() == layer) + passedExtendedLayer = true; + + // change the data length only for layers who come before the extended + // layer. For layers who come after, data length isn't changed + if (!passedExtendedLayer) + curLayer->m_DataLen += numOfBytesToExtend; + + // assuming header length of the layer that requested to be extended hasn't + // been enlarged yet + size_t headerLen = + curLayer->getHeaderLen() + (curLayer == layer ? numOfBytesToExtend : 0); + dataPtr += headerLen; + curLayer = curLayer->getNextLayer(); + } + + return true; } -bool Packet::shortenLayer(Layer* layer, int offsetInLayer, size_t numOfBytesToShorten) -{ - if (layer == nullptr) - { - PCPP_LOG_ERROR("Layer is NULL"); - return false; - } - - // verify layer is allocated to this packet - if (!(layer->m_Packet == this)) - { - PCPP_LOG_ERROR("Layer isn't allocated to this packet"); - return false; - } - - // remove data from raw packet - int indexOfDataToRemove = layer->m_Data + offsetInLayer - m_RawPacket->getRawData(); - if (!m_RawPacket->removeData(indexOfDataToRemove, numOfBytesToShorten)) - { - PCPP_LOG_ERROR("Couldn't remove data from packet"); - return false; - } - - // re-calculate all layers data ptr and data length - const uint8_t* dataPtr = m_RawPacket->getRawData(); - - // go over all layers from the first layer to the last layer and set the data ptr and data length for each layer - Layer* curLayer = m_FirstLayer; - bool passedExtendedLayer = false; - while (curLayer != nullptr) - { - // set the data ptr - curLayer->m_Data = (uint8_t*)dataPtr; - - // set a flag if arrived to the layer being shortened - if (curLayer->getPrevLayer() == layer) - passedExtendedLayer = true; - - // change the data length only for layers who come before the shortened layer. For layers who come after, data length isn't changed - if (!passedExtendedLayer) - curLayer->m_DataLen -= numOfBytesToShorten; - - // assuming header length of the layer that requested to be extended hasn't been enlarged yet - size_t headerLen = curLayer->getHeaderLen() - (curLayer == layer ? numOfBytesToShorten : 0); - dataPtr += headerLen; - curLayer = curLayer->getNextLayer(); - } - - return true; +bool Packet::shortenLayer(Layer* layer, int offsetInLayer, + size_t numOfBytesToShorten) { + if (layer == nullptr) { + PCPP_LOG_ERROR("Layer is NULL"); + return false; + } + + // verify layer is allocated to this packet + if (!(layer->m_Packet == this)) { + PCPP_LOG_ERROR("Layer isn't allocated to this packet"); + return false; + } + + // remove data from raw packet + int indexOfDataToRemove = + layer->m_Data + offsetInLayer - m_RawPacket->getRawData(); + if (!m_RawPacket->removeData(indexOfDataToRemove, numOfBytesToShorten)) { + PCPP_LOG_ERROR("Couldn't remove data from packet"); + return false; + } + + // re-calculate all layers data ptr and data length + const uint8_t* dataPtr = m_RawPacket->getRawData(); + + // go over all layers from the first layer to the last layer and set the data + // ptr and data length for each layer + Layer* curLayer = m_FirstLayer; + bool passedExtendedLayer = false; + while (curLayer != nullptr) { + // set the data ptr + curLayer->m_Data = (uint8_t*)dataPtr; + + // set a flag if arrived to the layer being shortened + if (curLayer->getPrevLayer() == layer) + passedExtendedLayer = true; + + // change the data length only for layers who come before the shortened + // layer. For layers who come after, data length isn't changed + if (!passedExtendedLayer) + curLayer->m_DataLen -= numOfBytesToShorten; + + // assuming header length of the layer that requested to be extended hasn't + // been enlarged yet + size_t headerLen = curLayer->getHeaderLen() - + (curLayer == layer ? numOfBytesToShorten : 0); + dataPtr += headerLen; + curLayer = curLayer->getNextLayer(); + } + + return true; } -void Packet::computeCalculateFields() -{ - // calculated fields should be calculated from top layer to bottom layer +void Packet::computeCalculateFields() { + // calculated fields should be calculated from top layer to bottom layer - Layer* curLayer = m_LastLayer; - while (curLayer != nullptr) - { - curLayer->computeCalculateFields(); - curLayer = curLayer->getPrevLayer(); - } + Layer* curLayer = m_LastLayer; + while (curLayer != nullptr) { + curLayer->computeCalculateFields(); + curLayer = curLayer->getPrevLayer(); + } } -std::string Packet::printPacketInfo(bool timeAsLocalTime) const -{ - std::ostringstream dataLenStream; - dataLenStream << m_RawPacket->getRawDataLen(); +std::string Packet::printPacketInfo(bool timeAsLocalTime) const { + std::ostringstream dataLenStream; + dataLenStream << m_RawPacket->getRawDataLen(); - // convert raw packet timestamp to printable format - timespec timestamp = m_RawPacket->getPacketTimeStamp(); - time_t nowtime = timestamp.tv_sec; - struct tm *nowtm = nullptr; + // convert raw packet timestamp to printable format + timespec timestamp = m_RawPacket->getPacketTimeStamp(); + time_t nowtime = timestamp.tv_sec; + struct tm* nowtm = nullptr; #if __cplusplus > 199711L && !defined(_WIN32) - // localtime_r and gmtime_r are thread-safe versions of localtime and gmtime, - // but they're defined only in newer compilers (>= C++0x). - // on Windows localtime and gmtime are already thread-safe so there is not need - // to use localtime_r and gmtime_r - struct tm nowtm_r; - if (timeAsLocalTime) - nowtm = localtime_r(&nowtime, &nowtm_r); - else - nowtm = gmtime_r(&nowtime, &nowtm_r); - - if (nowtm != nullptr) - nowtm = &nowtm_r; + // localtime_r and gmtime_r are thread-safe versions of localtime and gmtime, + // but they're defined only in newer compilers (>= C++0x). + // on Windows localtime and gmtime are already thread-safe so there is not + // need to use localtime_r and gmtime_r + struct tm nowtm_r; + if (timeAsLocalTime) + nowtm = localtime_r(&nowtime, &nowtm_r); + else + nowtm = gmtime_r(&nowtime, &nowtm_r); + + if (nowtm != nullptr) + nowtm = &nowtm_r; #else - // on Window compilers localtime and gmtime are already thread safe. - // in old compilers (< C++0x) gmtime_r and localtime_r were not defined so we have to fall back to localtime and gmtime - if (timeAsLocalTime) - nowtm = localtime(&nowtime); - else - nowtm = gmtime(&nowtime); + // on Window compilers localtime and gmtime are already thread safe. + // in old compilers (< C++0x) gmtime_r and localtime_r were not defined so we + // have to fall back to localtime and gmtime + if (timeAsLocalTime) + nowtm = localtime(&nowtime); + else + nowtm = gmtime(&nowtime); #endif - char buf[128]; - if (nowtm != nullptr) - { - char tmbuf[64]; - strftime(tmbuf, sizeof(tmbuf), "%Y-%m-%d %H:%M:%S", nowtm); - snprintf(buf, sizeof(buf), "%s.%09lu", tmbuf, (unsigned long)timestamp.tv_nsec); - } - else - snprintf(buf, sizeof(buf), "0000-00-00 00:00:00.000000000"); - - return "Packet length: " + dataLenStream.str() + " [Bytes], Arrival time: " + std::string(buf); + char buf[128]; + if (nowtm != nullptr) { + char tmbuf[64]; + strftime(tmbuf, sizeof(tmbuf), "%Y-%m-%d %H:%M:%S", nowtm); + snprintf(buf, sizeof(buf), "%s.%09lu", tmbuf, + (unsigned long)timestamp.tv_nsec); + } else + snprintf(buf, sizeof(buf), "0000-00-00 00:00:00.000000000"); + + return "Packet length: " + dataLenStream.str() + + " [Bytes], Arrival time: " + std::string(buf); } -Layer* Packet::createFirstLayer(LinkLayerType linkType) -{ - size_t rawDataLen = (size_t)m_RawPacket->getRawDataLen(); - if (rawDataLen == 0) - return nullptr; - - const uint8_t* rawData = m_RawPacket->getRawData(); - - if (linkType == LINKTYPE_ETHERNET) - { - if (EthLayer::isDataValid(rawData, rawDataLen)) - { - return new EthLayer((uint8_t*)rawData, rawDataLen, this); - } - else if (EthDot3Layer::isDataValid(rawData, rawDataLen)) - { - return new EthDot3Layer((uint8_t*)rawData, rawDataLen, this); - } - else - { - return new PayloadLayer((uint8_t*)rawData, rawDataLen, nullptr, this); - } - } - else if (linkType == LINKTYPE_LINUX_SLL) - { - return new SllLayer((uint8_t*)rawData, rawDataLen, this); - } - else if (linkType == LINKTYPE_LINUX_SLL2 && Sll2Layer::isDataValid(rawData, rawDataLen)) - { - return new Sll2Layer((uint8_t*)rawData, rawDataLen, this); - } - else if (linkType == LINKTYPE_NULL) - { - if (rawDataLen >= sizeof(uint32_t)) - return new NullLoopbackLayer((uint8_t*)rawData, rawDataLen, this); - else // rawDataLen is too small fir Null/Loopback - return new PayloadLayer((uint8_t*)rawData, rawDataLen, nullptr, this); - } - else if (linkType == LINKTYPE_RAW || linkType == LINKTYPE_DLT_RAW1 || linkType == LINKTYPE_DLT_RAW2) - { - uint8_t ipVer = rawData[0] & 0xf0; - if (ipVer == 0x40) - { - return IPv4Layer::isDataValid(rawData, rawDataLen) - ? static_cast(new IPv4Layer((uint8_t*)rawData, rawDataLen, nullptr, this)) - : static_cast(new PayloadLayer((uint8_t*)rawData, rawDataLen, nullptr, this)); - } - else if (ipVer == 0x60) - { - return IPv6Layer::isDataValid(rawData, rawDataLen) - ? static_cast(new IPv6Layer((uint8_t*)rawData, rawDataLen, nullptr, this)) - : static_cast(new PayloadLayer((uint8_t*)rawData, rawDataLen, nullptr, this)); - } - else - { - return new PayloadLayer((uint8_t*)rawData, rawDataLen, nullptr, this); - } - } - else if (linkType == LINKTYPE_IPV4) - { - return IPv4Layer::isDataValid(rawData, rawDataLen) - ? static_cast(new IPv4Layer((uint8_t*)rawData, rawDataLen, nullptr, this)) - : static_cast(new PayloadLayer((uint8_t*)rawData, rawDataLen, nullptr, this)); - } - else if (linkType == LINKTYPE_IPV6) - { - return IPv6Layer::isDataValid(rawData, rawDataLen) - ? static_cast(new IPv6Layer((uint8_t*)rawData, rawDataLen, nullptr, this)) - : static_cast(new PayloadLayer((uint8_t*)rawData, rawDataLen, nullptr, this)); - } - else if (linkType == LINKTYPE_NFLOG) - { - return NflogLayer::isDataValid(rawData, rawDataLen) - ? static_cast(new NflogLayer((uint8_t*)rawData, rawDataLen, this)) - : static_cast(new PayloadLayer((uint8_t*)rawData, rawDataLen, nullptr, this)); - } - - // unknown link type - return new PayloadLayer((uint8_t*)rawData, rawDataLen, nullptr, this); +Layer* Packet::createFirstLayer(LinkLayerType linkType) { + size_t rawDataLen = (size_t)m_RawPacket->getRawDataLen(); + if (rawDataLen == 0) + return nullptr; + + const uint8_t* rawData = m_RawPacket->getRawData(); + + if (linkType == LINKTYPE_ETHERNET) { + if (EthLayer::isDataValid(rawData, rawDataLen)) { + return new EthLayer((uint8_t*)rawData, rawDataLen, this); + } else if (EthDot3Layer::isDataValid(rawData, rawDataLen)) { + return new EthDot3Layer((uint8_t*)rawData, rawDataLen, this); + } else { + return new PayloadLayer((uint8_t*)rawData, rawDataLen, nullptr, this); + } + } else if (linkType == LINKTYPE_LINUX_SLL) { + return new SllLayer((uint8_t*)rawData, rawDataLen, this); + } else if (linkType == LINKTYPE_LINUX_SLL2 && + Sll2Layer::isDataValid(rawData, rawDataLen)) { + return new Sll2Layer((uint8_t*)rawData, rawDataLen, this); + } else if (linkType == LINKTYPE_NULL) { + if (rawDataLen >= sizeof(uint32_t)) + return new NullLoopbackLayer((uint8_t*)rawData, rawDataLen, this); + else // rawDataLen is too small fir Null/Loopback + return new PayloadLayer((uint8_t*)rawData, rawDataLen, nullptr, this); + } else if (linkType == LINKTYPE_RAW || linkType == LINKTYPE_DLT_RAW1 || + linkType == LINKTYPE_DLT_RAW2) { + uint8_t ipVer = rawData[0] & 0xf0; + if (ipVer == 0x40) { + return IPv4Layer::isDataValid(rawData, rawDataLen) + ? static_cast(new IPv4Layer( + (uint8_t*)rawData, rawDataLen, nullptr, this)) + : static_cast(new PayloadLayer( + (uint8_t*)rawData, rawDataLen, nullptr, this)); + } else if (ipVer == 0x60) { + return IPv6Layer::isDataValid(rawData, rawDataLen) + ? static_cast(new IPv6Layer( + (uint8_t*)rawData, rawDataLen, nullptr, this)) + : static_cast(new PayloadLayer( + (uint8_t*)rawData, rawDataLen, nullptr, this)); + } else { + return new PayloadLayer((uint8_t*)rawData, rawDataLen, nullptr, this); + } + } else if (linkType == LINKTYPE_IPV4) { + return IPv4Layer::isDataValid(rawData, rawDataLen) + ? static_cast(new IPv4Layer((uint8_t*)rawData, + rawDataLen, nullptr, this)) + : static_cast(new PayloadLayer( + (uint8_t*)rawData, rawDataLen, nullptr, this)); + } else if (linkType == LINKTYPE_IPV6) { + return IPv6Layer::isDataValid(rawData, rawDataLen) + ? static_cast(new IPv6Layer((uint8_t*)rawData, + rawDataLen, nullptr, this)) + : static_cast(new PayloadLayer( + (uint8_t*)rawData, rawDataLen, nullptr, this)); + } else if (linkType == LINKTYPE_NFLOG) { + return NflogLayer::isDataValid(rawData, rawDataLen) + ? static_cast( + new NflogLayer((uint8_t*)rawData, rawDataLen, this)) + : static_cast(new PayloadLayer( + (uint8_t*)rawData, rawDataLen, nullptr, this)); + } + + // unknown link type + return new PayloadLayer((uint8_t*)rawData, rawDataLen, nullptr, this); } -std::string Packet::toString(bool timeAsLocalTime) const -{ - std::vector stringList; - std::string result; - toStringList(stringList, timeAsLocalTime); - for (std::vector::iterator iter = stringList.begin(); iter != stringList.end(); iter++) - { - result += *iter + '\n'; - } - - return result; +std::string Packet::toString(bool timeAsLocalTime) const { + std::vector stringList; + std::string result; + toStringList(stringList, timeAsLocalTime); + for (std::vector::iterator iter = stringList.begin(); + iter != stringList.end(); iter++) { + result += *iter + '\n'; + } + + return result; } -void Packet::toStringList(std::vector& result, bool timeAsLocalTime) const -{ - result.clear(); - result.push_back(printPacketInfo(timeAsLocalTime)); - Layer* curLayer = m_FirstLayer; - while (curLayer != nullptr) - { - result.push_back(curLayer->toString()); - curLayer = curLayer->getNextLayer(); - } +void Packet::toStringList(std::vector& result, + bool timeAsLocalTime) const { + result.clear(); + result.push_back(printPacketInfo(timeAsLocalTime)); + Layer* curLayer = m_FirstLayer; + while (curLayer != nullptr) { + result.push_back(curLayer->toString()); + curLayer = curLayer->getNextLayer(); + } } } // namespace pcpp diff --git a/Packet++/src/PacketTrailerLayer.cpp b/Packet++/src/PacketTrailerLayer.cpp index 60377ffb04..fe36cbf01e 100644 --- a/Packet++/src/PacketTrailerLayer.cpp +++ b/Packet++/src/PacketTrailerLayer.cpp @@ -1,27 +1,25 @@ #include "PacketTrailerLayer.h" #include "GeneralUtils.h" -#include #include +#include -namespace pcpp -{ +namespace pcpp { -std::string PacketTrailerLayer::getTrailerDataAsHexString() const -{ - return byteArrayToHexString(m_Data, m_DataLen, m_DataLen + 4); +std::string PacketTrailerLayer::getTrailerDataAsHexString() const { + return byteArrayToHexString(m_Data, m_DataLen, m_DataLen + 4); } -std::string PacketTrailerLayer::toString() const -{ - std::ostringstream dataLenStream; - dataLenStream << m_DataLen; +std::string PacketTrailerLayer::toString() const { + std::ostringstream dataLenStream; + dataLenStream << m_DataLen; - std::string trailerStr = byteArrayToHexString(m_Data, m_DataLen, 15); + std::string trailerStr = byteArrayToHexString(m_Data, m_DataLen, 15); - if (m_DataLen > 15) - trailerStr += "..."; + if (m_DataLen > 15) + trailerStr += "..."; - return "Packet Trailer, Data: " + trailerStr + ", Length: " + dataLenStream.str() + " [Bytes]"; + return "Packet Trailer, Data: " + trailerStr + + ", Length: " + dataLenStream.str() + " [Bytes]"; } -} +} // namespace pcpp diff --git a/Packet++/src/PacketUtils.cpp b/Packet++/src/PacketUtils.cpp index 01ea361303..47d147dc2e 100644 --- a/Packet++/src/PacketUtils.cpp +++ b/Packet++/src/PacketUtils.cpp @@ -1,248 +1,236 @@ #include "PacketUtils.h" +#include "EndianPortable.h" #include "IPv4Layer.h" #include "IPv6Layer.h" +#include "Logger.h" #include "TcpLayer.h" #include "UdpLayer.h" -#include "Logger.h" -#include "EndianPortable.h" -namespace pcpp -{ - -uint16_t computeChecksum(ScalarBuffer vec[], size_t vecSize) -{ - uint32_t sum = 0; - for (size_t i = 0; i>16) - { - localSum = (localSum & 0xffff) + (localSum >> 16); - } - PCPP_LOG_DEBUG("Local sum = " << localSum << ", 0x" << std::uppercase << std::hex << localSum); - sum += localSum; - } - - while (sum>>16) - { - sum = (sum & 0xffff) + (sum >> 16); - } - PCPP_LOG_DEBUG("Sum before invert = " << sum << ", 0x" << std::uppercase << std::hex << sum); - - // To obtain the checksum we take the ones' complement of this result - uint16_t result = sum; - result = ~result; - - PCPP_LOG_DEBUG("Calculated checksum = " << sum << ", 0x" << std::uppercase << std::hex << result); - - // We return the result in BigEndian byte order - return htobe16(result); +namespace pcpp { + +uint16_t computeChecksum(ScalarBuffer vec[], size_t vecSize) { + uint32_t sum = 0; + for (size_t i = 0; i < vecSize; i++) { + uint32_t localSum = 0; + + // vec len is in bytes + for (size_t j = 0; j < vec[i].len / 2; j++) { + PCPP_LOG_DEBUG("Value to add = 0x" << std::uppercase << std::hex + << vec[i].buffer[j]); + localSum += vec[i].buffer[j]; + } + PCPP_LOG_DEBUG("Local sum = " << localSum << ", 0x" << std::uppercase + << std::hex << localSum); + + // check if there is one byte left + if (vec[i].len % 2) { + // access to the last byte using an uint8_t pointer + uint8_t* vecBytes = (uint8_t*)vec[i].buffer; + uint8_t lastByte = vecBytes[vec[i].len - 1]; + PCPP_LOG_DEBUG("1 byte left, adding value: 0x" << std::uppercase + << std::hex << lastByte); + // We have read the latest byte manually but this byte should be properly + // interpreted as a 0xFF on LE and a 0xFF00 on BE to have a proper + // checksum computation + localSum += be16toh(lastByte << 8); + + PCPP_LOG_DEBUG("Local sum = " << localSum << ", 0x" << std::uppercase + << std::hex << localSum); + } + + // carry count is added to the sum + while (localSum >> 16) { + localSum = (localSum & 0xffff) + (localSum >> 16); + } + PCPP_LOG_DEBUG("Local sum = " << localSum << ", 0x" << std::uppercase + << std::hex << localSum); + sum += localSum; + } + + while (sum >> 16) { + sum = (sum & 0xffff) + (sum >> 16); + } + PCPP_LOG_DEBUG("Sum before invert = " << sum << ", 0x" << std::uppercase + << std::hex << sum); + + // To obtain the checksum we take the ones' complement of this result + uint16_t result = sum; + result = ~result; + + PCPP_LOG_DEBUG("Calculated checksum = " << sum << ", 0x" << std::uppercase + << std::hex << result); + + // We return the result in BigEndian byte order + return htobe16(result); } -uint16_t computePseudoHdrChecksum(uint8_t *dataPtr, size_t dataLen, IPAddress::AddressType ipAddrType, - uint8_t protocolType, IPAddress srcIPAddress, - IPAddress dstIPAddress) -{ - PCPP_LOG_DEBUG("Compute pseudo header checksum.\n DataLen = " << dataLen << "IPAddrType = " - << ipAddrType << "ProtocolType = " << protocolType << "SrcIP = " << srcIPAddress - << "DstIP = " << dstIPAddress); - - uint16_t checksumRes = 0; - ScalarBuffer vec[2]; - vec[0].buffer = (uint16_t *) dataPtr; - vec[0].len = dataLen; - - if (ipAddrType == IPAddress::IPv4AddressType) - { - uint32_t srcIP = srcIPAddress.getIPv4().toInt(); - uint32_t dstIP = dstIPAddress.getIPv4().toInt(); - uint16_t pseudoHeader[6]; - pseudoHeader[0] = srcIP >> 16; - pseudoHeader[1] = srcIP & 0xFFFF; - pseudoHeader[2] = dstIP >> 16; - pseudoHeader[3] = dstIP & 0xFFFF; - pseudoHeader[4] = 0xffff & htobe16(dataLen); - pseudoHeader[5] = htobe16(0x00ff & protocolType); - vec[1].buffer = pseudoHeader; - vec[1].len = 12; - checksumRes = computeChecksum(vec, 2); - } - else if (ipAddrType == IPAddress::IPv6AddressType) - { - uint16_t pseudoHeader[18]; - srcIPAddress.getIPv6().copyTo((uint8_t *) pseudoHeader); - dstIPAddress.getIPv6().copyTo((uint8_t *) (pseudoHeader + 8)); - pseudoHeader[16] = 0xffff & htobe16(dataLen); - pseudoHeader[17] = htobe16(0x00ff & protocolType); - vec[1].buffer = pseudoHeader; - vec[1].len = 36; - checksumRes = computeChecksum(vec, 2); - } - else - { - PCPP_LOG_ERROR("Compute pseudo header checksum failed, for unknown IPAddrType = " << ipAddrType); - } - - PCPP_LOG_DEBUG("Pseudo header checksum = 0xX" << std::uppercase << std::hex << checksumRes); - - return checksumRes; +uint16_t computePseudoHdrChecksum(uint8_t* dataPtr, size_t dataLen, + IPAddress::AddressType ipAddrType, + uint8_t protocolType, IPAddress srcIPAddress, + IPAddress dstIPAddress) { + PCPP_LOG_DEBUG("Compute pseudo header checksum.\n DataLen = " + << dataLen << "IPAddrType = " << ipAddrType + << "ProtocolType = " << protocolType + << "SrcIP = " << srcIPAddress << "DstIP = " << dstIPAddress); + + uint16_t checksumRes = 0; + ScalarBuffer vec[2]; + vec[0].buffer = (uint16_t*)dataPtr; + vec[0].len = dataLen; + + if (ipAddrType == IPAddress::IPv4AddressType) { + uint32_t srcIP = srcIPAddress.getIPv4().toInt(); + uint32_t dstIP = dstIPAddress.getIPv4().toInt(); + uint16_t pseudoHeader[6]; + pseudoHeader[0] = srcIP >> 16; + pseudoHeader[1] = srcIP & 0xFFFF; + pseudoHeader[2] = dstIP >> 16; + pseudoHeader[3] = dstIP & 0xFFFF; + pseudoHeader[4] = 0xffff & htobe16(dataLen); + pseudoHeader[5] = htobe16(0x00ff & protocolType); + vec[1].buffer = pseudoHeader; + vec[1].len = 12; + checksumRes = computeChecksum(vec, 2); + } else if (ipAddrType == IPAddress::IPv6AddressType) { + uint16_t pseudoHeader[18]; + srcIPAddress.getIPv6().copyTo((uint8_t*)pseudoHeader); + dstIPAddress.getIPv6().copyTo((uint8_t*)(pseudoHeader + 8)); + pseudoHeader[16] = 0xffff & htobe16(dataLen); + pseudoHeader[17] = htobe16(0x00ff & protocolType); + vec[1].buffer = pseudoHeader; + vec[1].len = 36; + checksumRes = computeChecksum(vec, 2); + } else { + PCPP_LOG_ERROR( + "Compute pseudo header checksum failed, for unknown IPAddrType = " + << ipAddrType); + } + + PCPP_LOG_DEBUG("Pseudo header checksum = 0xX" << std::uppercase << std::hex + << checksumRes); + + return checksumRes; } static const uint32_t FNV_PRIME = 16777619u; static const uint32_t OFFSET_BASIS = 2166136261u; -uint32_t fnvHash(ScalarBuffer vec[], size_t vecSize) -{ - uint32_t hash = OFFSET_BASIS; - for (size_t i = 0; i < vecSize; ++i) - { - for (size_t j = 0; j < vec[i].len; ++j) - { - hash *= FNV_PRIME; - hash ^= vec[i].buffer[j]; - } - } - return hash; +uint32_t fnvHash(ScalarBuffer vec[], size_t vecSize) { + uint32_t hash = OFFSET_BASIS; + for (size_t i = 0; i < vecSize; ++i) { + for (size_t j = 0; j < vec[i].len; ++j) { + hash *= FNV_PRIME; + hash ^= vec[i].buffer[j]; + } + } + return hash; } -uint32_t fnvHash(uint8_t* buffer, size_t bufSize) -{ - ScalarBuffer scalarBuf; - scalarBuf.buffer = buffer; - scalarBuf.len = bufSize; - return fnvHash(&scalarBuf, 1); +uint32_t fnvHash(uint8_t* buffer, size_t bufSize) { + ScalarBuffer scalarBuf; + scalarBuf.buffer = buffer; + scalarBuf.len = bufSize; + return fnvHash(&scalarBuf, 1); } -uint32_t hash5Tuple(Packet* packet, bool const& directionUnique) -{ - if (!packet->isPacketOfType(IPv4) && !packet->isPacketOfType(IPv6)) - return 0; - - if (packet->isPacketOfType(ICMP)) - return 0; - - if (!(packet->isPacketOfType(TCP)) && (!packet->isPacketOfType(UDP))) - return 0; - - ScalarBuffer vec[5]; - - uint16_t portSrc = 0; - uint16_t portDst = 0; - int srcPosition = 0; - - TcpLayer* tcpLayer = packet->getLayerOfType(true); // lookup in reverse order - if (tcpLayer != nullptr) - { - portSrc = tcpLayer->getTcpHeader()->portSrc; - portDst = tcpLayer->getTcpHeader()->portDst; - } - else - { - UdpLayer* udpLayer = packet->getLayerOfType(true); - portSrc = udpLayer->getUdpHeader()->portSrc; - portDst = udpLayer->getUdpHeader()->portDst; - } - - if( ! directionUnique) - { - if (portDst < portSrc) - srcPosition = 1; - } - - vec[0 + srcPosition].buffer = (uint8_t*)&portSrc; - vec[0 + srcPosition].len = 2; - vec[1 - srcPosition].buffer = (uint8_t*)&portDst; - vec[1 - srcPosition].len = 2; - - - IPv4Layer* ipv4Layer = packet->getLayerOfType(); - if (ipv4Layer != nullptr) - { - if (portSrc == portDst && ipv4Layer->getIPv4Header()->ipDst < ipv4Layer->getIPv4Header()->ipSrc) - srcPosition = 1; - - vec[2 + srcPosition].buffer = (uint8_t*)&ipv4Layer->getIPv4Header()->ipSrc; - vec[2 + srcPosition].len = 4; - vec[3 - srcPosition].buffer = (uint8_t*)&ipv4Layer->getIPv4Header()->ipDst; - vec[3 - srcPosition].len = 4; - vec[4].buffer = &(ipv4Layer->getIPv4Header()->protocol); - vec[4].len = 1; - } - else - { - IPv6Layer* ipv6Layer = packet->getLayerOfType(); - if (portSrc == portDst && (uint64_t)ipv6Layer->getIPv6Header()->ipDst < (uint64_t)ipv6Layer->getIPv6Header()->ipSrc) - srcPosition = 1; - - vec[2 + srcPosition].buffer = ipv6Layer->getIPv6Header()->ipSrc; - vec[2 + srcPosition].len = 16; - vec[3 - srcPosition].buffer = ipv6Layer->getIPv6Header()->ipDst; - vec[3 - srcPosition].len = 16; - vec[4].buffer = &(ipv6Layer->getIPv6Header()->nextHeader); - vec[4].len = 1; - } - - return pcpp::fnvHash(vec, 5); +uint32_t hash5Tuple(Packet* packet, bool const& directionUnique) { + if (!packet->isPacketOfType(IPv4) && !packet->isPacketOfType(IPv6)) + return 0; + + if (packet->isPacketOfType(ICMP)) + return 0; + + if (!(packet->isPacketOfType(TCP)) && (!packet->isPacketOfType(UDP))) + return 0; + + ScalarBuffer vec[5]; + + uint16_t portSrc = 0; + uint16_t portDst = 0; + int srcPosition = 0; + + TcpLayer* tcpLayer = + packet->getLayerOfType(true); // lookup in reverse order + if (tcpLayer != nullptr) { + portSrc = tcpLayer->getTcpHeader()->portSrc; + portDst = tcpLayer->getTcpHeader()->portDst; + } else { + UdpLayer* udpLayer = packet->getLayerOfType(true); + portSrc = udpLayer->getUdpHeader()->portSrc; + portDst = udpLayer->getUdpHeader()->portDst; + } + + if (!directionUnique) { + if (portDst < portSrc) + srcPosition = 1; + } + + vec[0 + srcPosition].buffer = (uint8_t*)&portSrc; + vec[0 + srcPosition].len = 2; + vec[1 - srcPosition].buffer = (uint8_t*)&portDst; + vec[1 - srcPosition].len = 2; + + IPv4Layer* ipv4Layer = packet->getLayerOfType(); + if (ipv4Layer != nullptr) { + if (portSrc == portDst && + ipv4Layer->getIPv4Header()->ipDst < ipv4Layer->getIPv4Header()->ipSrc) + srcPosition = 1; + + vec[2 + srcPosition].buffer = (uint8_t*)&ipv4Layer->getIPv4Header()->ipSrc; + vec[2 + srcPosition].len = 4; + vec[3 - srcPosition].buffer = (uint8_t*)&ipv4Layer->getIPv4Header()->ipDst; + vec[3 - srcPosition].len = 4; + vec[4].buffer = &(ipv4Layer->getIPv4Header()->protocol); + vec[4].len = 1; + } else { + IPv6Layer* ipv6Layer = packet->getLayerOfType(); + if (portSrc == portDst && (uint64_t)ipv6Layer->getIPv6Header()->ipDst < + (uint64_t)ipv6Layer->getIPv6Header()->ipSrc) + srcPosition = 1; + + vec[2 + srcPosition].buffer = ipv6Layer->getIPv6Header()->ipSrc; + vec[2 + srcPosition].len = 16; + vec[3 - srcPosition].buffer = ipv6Layer->getIPv6Header()->ipDst; + vec[3 - srcPosition].len = 16; + vec[4].buffer = &(ipv6Layer->getIPv6Header()->nextHeader); + vec[4].len = 1; + } + + return pcpp::fnvHash(vec, 5); } - -uint32_t hash2Tuple(Packet* packet) -{ - if (!packet->isPacketOfType(IPv4) && !packet->isPacketOfType(IPv6)) - return 0; - - ScalarBuffer vec[2]; - - IPv4Layer* ipv4Layer = packet->getLayerOfType(); - if (ipv4Layer != nullptr) - { - int srcPosition = 0; - if (ipv4Layer->getIPv4Header()->ipDst < ipv4Layer->getIPv4Header()->ipSrc) - srcPosition = 1; - - vec[0 + srcPosition].buffer = (uint8_t*)&ipv4Layer->getIPv4Header()->ipSrc; - vec[0 + srcPosition].len = 4; - vec[1 - srcPosition].buffer = (uint8_t*)&ipv4Layer->getIPv4Header()->ipDst; - vec[1 - srcPosition].len = 4; - } - else - { - IPv6Layer* ipv6Layer = packet->getLayerOfType(); - int srcPosition = 0; - if ((uint64_t)ipv6Layer->getIPv6Header()->ipDst < (uint64_t)ipv6Layer->getIPv6Header()->ipSrc - && (uint64_t)(ipv6Layer->getIPv6Header()->ipDst+8) < (uint64_t)(ipv6Layer->getIPv6Header()->ipSrc+8)) - srcPosition = 1; - - vec[0 + srcPosition].buffer = ipv6Layer->getIPv6Header()->ipSrc; - vec[0 + srcPosition].len = 16; - vec[1 - srcPosition].buffer = ipv6Layer->getIPv6Header()->ipDst; - vec[1 - srcPosition].len = 16; - } - - return pcpp::fnvHash(vec, 2); +uint32_t hash2Tuple(Packet* packet) { + if (!packet->isPacketOfType(IPv4) && !packet->isPacketOfType(IPv6)) + return 0; + + ScalarBuffer vec[2]; + + IPv4Layer* ipv4Layer = packet->getLayerOfType(); + if (ipv4Layer != nullptr) { + int srcPosition = 0; + if (ipv4Layer->getIPv4Header()->ipDst < ipv4Layer->getIPv4Header()->ipSrc) + srcPosition = 1; + + vec[0 + srcPosition].buffer = (uint8_t*)&ipv4Layer->getIPv4Header()->ipSrc; + vec[0 + srcPosition].len = 4; + vec[1 - srcPosition].buffer = (uint8_t*)&ipv4Layer->getIPv4Header()->ipDst; + vec[1 - srcPosition].len = 4; + } else { + IPv6Layer* ipv6Layer = packet->getLayerOfType(); + int srcPosition = 0; + if ((uint64_t)ipv6Layer->getIPv6Header()->ipDst < + (uint64_t)ipv6Layer->getIPv6Header()->ipSrc && + (uint64_t)(ipv6Layer->getIPv6Header()->ipDst + 8) < + (uint64_t)(ipv6Layer->getIPv6Header()->ipSrc + 8)) + srcPosition = 1; + + vec[0 + srcPosition].buffer = ipv6Layer->getIPv6Header()->ipSrc; + vec[0 + srcPosition].len = 16; + vec[1 - srcPosition].buffer = ipv6Layer->getIPv6Header()->ipDst; + vec[1 - srcPosition].len = 16; + } + + return pcpp::fnvHash(vec, 2); } -} // namespace pcpp +} // namespace pcpp diff --git a/Packet++/src/PayloadLayer.cpp b/Packet++/src/PayloadLayer.cpp index ce79f79228..31eea1303f 100644 --- a/Packet++/src/PayloadLayer.cpp +++ b/Packet++/src/PayloadLayer.cpp @@ -2,57 +2,50 @@ #include "PayloadLayer.h" #include "GeneralUtils.h" -#include #include +#include -namespace pcpp -{ +namespace pcpp { -PayloadLayer::PayloadLayer(const uint8_t* data, size_t dataLen, bool) : Layer() -{ - m_Data = new uint8_t[dataLen]; - memcpy(m_Data, data, dataLen); - m_DataLen = dataLen; - m_Protocol = GenericPayload; +PayloadLayer::PayloadLayer(const uint8_t* data, size_t dataLen, bool) + : Layer() { + m_Data = new uint8_t[dataLen]; + memcpy(m_Data, data, dataLen); + m_DataLen = dataLen; + m_Protocol = GenericPayload; } -PayloadLayer::PayloadLayer(const std::string& payloadAsHexStream) -{ - m_DataLen = payloadAsHexStream.length() / 2; - m_Data = new uint8_t[m_DataLen]; - m_Protocol = GenericPayload; - if (hexStringToByteArray(payloadAsHexStream, m_Data, m_DataLen) == 0) - { - delete [] m_Data; - m_Data = nullptr; - m_DataLen = 0; - } +PayloadLayer::PayloadLayer(const std::string& payloadAsHexStream) { + m_DataLen = payloadAsHexStream.length() / 2; + m_Data = new uint8_t[m_DataLen]; + m_Protocol = GenericPayload; + if (hexStringToByteArray(payloadAsHexStream, m_Data, m_DataLen) == 0) { + delete[] m_Data; + m_Data = nullptr; + m_DataLen = 0; + } } -void PayloadLayer::setPayload(const uint8_t* newPayload, size_t newPayloadLength) -{ - if (newPayloadLength < m_DataLen) - { - // shorten payload layer - shortenLayer(newPayloadLength, m_DataLen - newPayloadLength); - } - else if (newPayloadLength > m_DataLen) - { - // extend payload layer - extendLayer(m_DataLen, newPayloadLength - m_DataLen); - } - - // and copy data to layer - // this is also executed if the newPayloadLength == m_DataLen - memcpy(m_Data, newPayload, newPayloadLength); +void PayloadLayer::setPayload(const uint8_t* newPayload, + size_t newPayloadLength) { + if (newPayloadLength < m_DataLen) { + // shorten payload layer + shortenLayer(newPayloadLength, m_DataLen - newPayloadLength); + } else if (newPayloadLength > m_DataLen) { + // extend payload layer + extendLayer(m_DataLen, newPayloadLength - m_DataLen); + } + + // and copy data to layer + // this is also executed if the newPayloadLength == m_DataLen + memcpy(m_Data, newPayload, newPayloadLength); } -std::string PayloadLayer::toString() const -{ - std::ostringstream dataLenStream; - dataLenStream << m_DataLen; +std::string PayloadLayer::toString() const { + std::ostringstream dataLenStream; + dataLenStream << m_DataLen; - return "Payload Layer, Data length: " + dataLenStream.str() + " [Bytes]"; + return "Payload Layer, Data length: " + dataLenStream.str() + " [Bytes]"; } } // namespace pcpp diff --git a/Packet++/src/RadiusLayer.cpp b/Packet++/src/RadiusLayer.cpp index 4d4d58f77d..a7d763de51 100644 --- a/Packet++/src/RadiusLayer.cpp +++ b/Packet++/src/RadiusLayer.cpp @@ -1,258 +1,239 @@ #define LOG_MODULE PacketLogModuleRadiusLayer #include "RadiusLayer.h" -#include "Logger.h" #include "GeneralUtils.h" +#include "Logger.h" -#include -#include #include "EndianPortable.h" +#include +#include -namespace pcpp -{ - -RadiusAttribute RadiusAttributeBuilder::build() const -{ - size_t recSize = m_RecValueLen+2; - uint8_t* recordBuffer = new uint8_t[recSize]; - memset(recordBuffer, 0, recSize); - recordBuffer[0] = static_cast(m_RecType); - recordBuffer[1] = static_cast(recSize); - if (m_RecValueLen > 0) - memcpy(recordBuffer+2, m_RecValue, m_RecValueLen); - - return RadiusAttribute(recordBuffer); -} - -RadiusLayer::RadiusLayer(uint8_t code, uint8_t id, const uint8_t* authenticator, uint8_t authenticatorArrSize) -{ - m_DataLen = sizeof(radius_header); - m_Data = new uint8_t[m_DataLen]; - memset(m_Data, 0, m_DataLen); - m_Protocol = Radius; - - radius_header* hdr = getRadiusHeader(); - hdr->code = code; - hdr->id = id; - hdr->length = htobe16(sizeof(radius_header)); - if (authenticatorArrSize == 0 || authenticator == nullptr) - return; - if (authenticatorArrSize > 16) - authenticatorArrSize = 16; - memcpy(hdr->authenticator, authenticator, authenticatorArrSize); -} - -RadiusLayer::RadiusLayer(uint8_t code, uint8_t id, const std::string &authenticator) -{ - m_DataLen = sizeof(radius_header); - m_Data = new uint8_t[m_DataLen]; - memset(m_Data, 0, m_DataLen); - m_Protocol = Radius; - - radius_header* hdr = getRadiusHeader(); - hdr->code = code; - hdr->id = id; - hdr->length = htobe16(sizeof(radius_header)); - setAuthenticatorValue(authenticator); -} - -RadiusAttribute RadiusLayer::addAttrAt(const RadiusAttributeBuilder& attrBuilder, int offset) -{ - RadiusAttribute newAttr = attrBuilder.build(); - if (newAttr.isNull()) - { - PCPP_LOG_ERROR("Cannot build new attribute of type " << (int)newAttr.getType()); - return newAttr; - } - - size_t sizeToExtend = newAttr.getTotalSize(); - if (!extendLayer(offset, sizeToExtend)) - { - PCPP_LOG_ERROR("Could not extend RadiusLayer in [" << newAttr.getTotalSize() << "] bytes"); - newAttr.purgeRecordData(); - return RadiusAttribute(nullptr); - } - - memcpy(m_Data + offset, newAttr.getRecordBasePtr(), newAttr.getTotalSize()); +namespace pcpp { + +RadiusAttribute RadiusAttributeBuilder::build() const { + size_t recSize = m_RecValueLen + 2; + uint8_t* recordBuffer = new uint8_t[recSize]; + memset(recordBuffer, 0, recSize); + recordBuffer[0] = static_cast(m_RecType); + recordBuffer[1] = static_cast(recSize); + if (m_RecValueLen > 0) + memcpy(recordBuffer + 2, m_RecValue, m_RecValueLen); + + return RadiusAttribute(recordBuffer); +} + +RadiusLayer::RadiusLayer(uint8_t code, uint8_t id, const uint8_t* authenticator, + uint8_t authenticatorArrSize) { + m_DataLen = sizeof(radius_header); + m_Data = new uint8_t[m_DataLen]; + memset(m_Data, 0, m_DataLen); + m_Protocol = Radius; + + radius_header* hdr = getRadiusHeader(); + hdr->code = code; + hdr->id = id; + hdr->length = htobe16(sizeof(radius_header)); + if (authenticatorArrSize == 0 || authenticator == nullptr) + return; + if (authenticatorArrSize > 16) + authenticatorArrSize = 16; + memcpy(hdr->authenticator, authenticator, authenticatorArrSize); +} + +RadiusLayer::RadiusLayer(uint8_t code, uint8_t id, + const std::string& authenticator) { + m_DataLen = sizeof(radius_header); + m_Data = new uint8_t[m_DataLen]; + memset(m_Data, 0, m_DataLen); + m_Protocol = Radius; + + radius_header* hdr = getRadiusHeader(); + hdr->code = code; + hdr->id = id; + hdr->length = htobe16(sizeof(radius_header)); + setAuthenticatorValue(authenticator); +} + +RadiusAttribute +RadiusLayer::addAttrAt(const RadiusAttributeBuilder& attrBuilder, int offset) { + RadiusAttribute newAttr = attrBuilder.build(); + if (newAttr.isNull()) { + PCPP_LOG_ERROR("Cannot build new attribute of type " + << (int)newAttr.getType()); + return newAttr; + } + + size_t sizeToExtend = newAttr.getTotalSize(); + if (!extendLayer(offset, sizeToExtend)) { + PCPP_LOG_ERROR("Could not extend RadiusLayer in [" << newAttr.getTotalSize() + << "] bytes"); + newAttr.purgeRecordData(); + return RadiusAttribute(nullptr); + } + + memcpy(m_Data + offset, newAttr.getRecordBasePtr(), newAttr.getTotalSize()); - uint8_t* newAttrPtr = m_Data + offset; + uint8_t* newAttrPtr = m_Data + offset; - m_AttributeReader.changeTLVRecordCount(1); + m_AttributeReader.changeTLVRecordCount(1); - newAttr.purgeRecordData(); + newAttr.purgeRecordData(); - getRadiusHeader()->length = htobe16(m_DataLen); + getRadiusHeader()->length = htobe16(m_DataLen); - return RadiusAttribute(newAttrPtr); + return RadiusAttribute(newAttrPtr); } -std::string RadiusLayer::getAuthenticatorValue() const -{ - return byteArrayToHexString(getRadiusHeader()->authenticator, 16); +std::string RadiusLayer::getAuthenticatorValue() const { + return byteArrayToHexString(getRadiusHeader()->authenticator, 16); } -void RadiusLayer::setAuthenticatorValue(const std::string& authValue) -{ - hexStringToByteArray(authValue, getRadiusHeader()->authenticator, 16); +void RadiusLayer::setAuthenticatorValue(const std::string& authValue) { + hexStringToByteArray(authValue, getRadiusHeader()->authenticator, 16); } -std::string RadiusLayer::getRadiusMessageString(uint8_t radiusMessageCode) -{ - switch (radiusMessageCode) - { - case 1: - return "Access-Request"; - case 2: - return "Access-Accept"; - case 3: - return "Access-Reject"; - case 4: - return "Accounting-Request"; - case 5: - return "Accounting-Response"; - case 11: - return "Access-Challenge"; - case 12: - return "Status-Server"; - case 13: - return "Status-Client"; - case 40: - return "Disconnect-Request"; - case 41: - return "Disconnect-ACK"; - case 42: - return "Disconnect-NAK"; - case 43: - return "CoA-Request"; - case 44: - return "CoA-ACK"; - case 45: - return "CoA-NAK"; - case 255: - return "Reserved"; - default: - return "Unknown"; - } +std::string RadiusLayer::getRadiusMessageString(uint8_t radiusMessageCode) { + switch (radiusMessageCode) { + case 1: + return "Access-Request"; + case 2: + return "Access-Accept"; + case 3: + return "Access-Reject"; + case 4: + return "Accounting-Request"; + case 5: + return "Accounting-Response"; + case 11: + return "Access-Challenge"; + case 12: + return "Status-Server"; + case 13: + return "Status-Client"; + case 40: + return "Disconnect-Request"; + case 41: + return "Disconnect-ACK"; + case 42: + return "Disconnect-NAK"; + case 43: + return "CoA-Request"; + case 44: + return "CoA-ACK"; + case 45: + return "CoA-NAK"; + case 255: + return "Reserved"; + default: + return "Unknown"; + } } -size_t RadiusLayer::getHeaderLen() const -{ - uint16_t len = be16toh(getRadiusHeader()->length); - if (len > m_DataLen) - return m_DataLen; +size_t RadiusLayer::getHeaderLen() const { + uint16_t len = be16toh(getRadiusHeader()->length); + if (len > m_DataLen) + return m_DataLen; - return len; + return len; } -void RadiusLayer::computeCalculateFields() -{ - getRadiusHeader()->length = htobe16(m_DataLen); +void RadiusLayer::computeCalculateFields() { + getRadiusHeader()->length = htobe16(m_DataLen); } -std::string RadiusLayer::toString() const -{ - std::ostringstream str; - str << "RADIUS Layer, " << - RadiusLayer::getRadiusMessageString(getRadiusHeader()->code) << - "(" << - (int)getRadiusHeader()->code << - "), " - "Id=" << - (int)getRadiusHeader()->id << - ", " << - "Length=" << - be16toh(getRadiusHeader()->length); - - return str.str(); +std::string RadiusLayer::toString() const { + std::ostringstream str; + str << "RADIUS Layer, " + << RadiusLayer::getRadiusMessageString(getRadiusHeader()->code) << "(" + << (int)getRadiusHeader()->code + << "), " + "Id=" + << (int)getRadiusHeader()->id << ", " + << "Length=" << be16toh(getRadiusHeader()->length); + + return str.str(); } -RadiusAttribute RadiusLayer::getFirstAttribute() const -{ - return m_AttributeReader.getFirstTLVRecord(getAttributesBasePtr(), getHeaderLen() - sizeof(radius_header)); +RadiusAttribute RadiusLayer::getFirstAttribute() const { + return m_AttributeReader.getFirstTLVRecord( + getAttributesBasePtr(), getHeaderLen() - sizeof(radius_header)); } -RadiusAttribute RadiusLayer::getNextAttribute(RadiusAttribute& attr) const -{ - return m_AttributeReader.getNextTLVRecord(attr, getAttributesBasePtr(), getHeaderLen() - sizeof(radius_header)); +RadiusAttribute RadiusLayer::getNextAttribute(RadiusAttribute& attr) const { + return m_AttributeReader.getNextTLVRecord( + attr, getAttributesBasePtr(), getHeaderLen() - sizeof(radius_header)); } -RadiusAttribute RadiusLayer::getAttribute(uint8_t attributeType) const -{ - return m_AttributeReader.getTLVRecord(attributeType, getAttributesBasePtr(), getHeaderLen() - sizeof(radius_header)); +RadiusAttribute RadiusLayer::getAttribute(uint8_t attributeType) const { + return m_AttributeReader.getTLVRecord(attributeType, getAttributesBasePtr(), + getHeaderLen() - sizeof(radius_header)); } -size_t RadiusLayer::getAttributeCount() const -{ - return m_AttributeReader.getTLVRecordCount(getAttributesBasePtr(), getHeaderLen() - sizeof(radius_header)); +size_t RadiusLayer::getAttributeCount() const { + return m_AttributeReader.getTLVRecordCount( + getAttributesBasePtr(), getHeaderLen() - sizeof(radius_header)); } -RadiusAttribute RadiusLayer::addAttribute(const RadiusAttributeBuilder& attrBuilder) -{ - int offset = getHeaderLen(); - return addAttrAt(attrBuilder, offset); +RadiusAttribute +RadiusLayer::addAttribute(const RadiusAttributeBuilder& attrBuilder) { + int offset = getHeaderLen(); + return addAttrAt(attrBuilder, offset); } -RadiusAttribute RadiusLayer::addAttributeAfter(const RadiusAttributeBuilder& attrBuilder, uint8_t prevAttrType) -{ - int offset = 0; +RadiusAttribute +RadiusLayer::addAttributeAfter(const RadiusAttributeBuilder& attrBuilder, + uint8_t prevAttrType) { + int offset = 0; - RadiusAttribute prevAttr = getAttribute(prevAttrType); + RadiusAttribute prevAttr = getAttribute(prevAttrType); - if (prevAttr.isNull()) - { - offset = getHeaderLen(); - } - else - { - offset = prevAttr.getRecordBasePtr() + prevAttr.getTotalSize() - m_Data; - } + if (prevAttr.isNull()) { + offset = getHeaderLen(); + } else { + offset = prevAttr.getRecordBasePtr() + prevAttr.getTotalSize() - m_Data; + } - return addAttrAt(attrBuilder, offset); + return addAttrAt(attrBuilder, offset); } -bool RadiusLayer::removeAttribute(uint8_t attrType) -{ - RadiusAttribute attrToRemove = getAttribute(attrType); - if (attrToRemove.isNull()) - { - return false; - } +bool RadiusLayer::removeAttribute(uint8_t attrType) { + RadiusAttribute attrToRemove = getAttribute(attrType); + if (attrToRemove.isNull()) { + return false; + } - int offset = attrToRemove.getRecordBasePtr() - m_Data; + int offset = attrToRemove.getRecordBasePtr() - m_Data; - if (!shortenLayer(offset, attrToRemove.getTotalSize())) - { - return false; - } + if (!shortenLayer(offset, attrToRemove.getTotalSize())) { + return false; + } - m_AttributeReader.changeTLVRecordCount(-1); - getRadiusHeader()->length = htobe16(m_DataLen); + m_AttributeReader.changeTLVRecordCount(-1); + getRadiusHeader()->length = htobe16(m_DataLen); - return true; + return true; } -bool RadiusLayer::removeAllAttributes() -{ - int offset = sizeof(radius_header); +bool RadiusLayer::removeAllAttributes() { + int offset = sizeof(radius_header); - if (!shortenLayer(offset, getHeaderLen()-offset)) - return false; + if (!shortenLayer(offset, getHeaderLen() - offset)) + return false; - m_AttributeReader.changeTLVRecordCount(0-getAttributeCount()); + m_AttributeReader.changeTLVRecordCount(0 - getAttributeCount()); - getRadiusHeader()->length = htobe16(m_DataLen); + getRadiusHeader()->length = htobe16(m_DataLen); - return true; + return true; } -bool RadiusLayer::isDataValid(const uint8_t* udpData, size_t udpDataLen) -{ - if(udpData != nullptr && udpDataLen >= sizeof(radius_header)) - { - const radius_header* radHdr = reinterpret_cast(udpData); - size_t radLen = be16toh(radHdr->length); - return radLen >= sizeof(radius_header) && radLen <= udpDataLen; - } - return false; +bool RadiusLayer::isDataValid(const uint8_t* udpData, size_t udpDataLen) { + if (udpData != nullptr && udpDataLen >= sizeof(radius_header)) { + const radius_header* radHdr = + reinterpret_cast(udpData); + size_t radLen = be16toh(radHdr->length); + return radLen >= sizeof(radius_header) && radLen <= udpDataLen; + } + return false; } -} +} // namespace pcpp diff --git a/Packet++/src/RawPacket.cpp b/Packet++/src/RawPacket.cpp index 8ad52d520f..10705aad14 100644 --- a/Packet++/src/RawPacket.cpp +++ b/Packet++/src/RawPacket.cpp @@ -1,314 +1,303 @@ #define LOG_MODULE PacketLogModuleRawPacket #include "RawPacket.h" -#include #include "Logger.h" #include "TimespecTimeval.h" +#include -namespace pcpp -{ - -void RawPacket::init(bool deleteRawDataAtDestructor) -{ - m_RawData = nullptr; - m_RawDataLen = 0; - m_FrameLength = 0; - m_DeleteRawDataAtDestructor = deleteRawDataAtDestructor; - m_RawPacketSet = false; - m_LinkLayerType = LINKTYPE_ETHERNET; -} +namespace pcpp { -RawPacket::RawPacket(const uint8_t* pRawData, int rawDataLen, timeval timestamp, bool deleteRawDataAtDestructor, LinkLayerType layerType) -{ - timespec nsec_time; - TIMEVAL_TO_TIMESPEC(×tamp, &nsec_time); - init(deleteRawDataAtDestructor); - setRawData(pRawData, rawDataLen, nsec_time, layerType); +void RawPacket::init(bool deleteRawDataAtDestructor) { + m_RawData = nullptr; + m_RawDataLen = 0; + m_FrameLength = 0; + m_DeleteRawDataAtDestructor = deleteRawDataAtDestructor; + m_RawPacketSet = false; + m_LinkLayerType = LINKTYPE_ETHERNET; } -RawPacket::RawPacket(const uint8_t* pRawData, int rawDataLen, timespec timestamp, bool deleteRawDataAtDestructor, LinkLayerType layerType) -{ - init(deleteRawDataAtDestructor); - setRawData(pRawData, rawDataLen, timestamp, layerType); +RawPacket::RawPacket(const uint8_t* pRawData, int rawDataLen, timeval timestamp, + bool deleteRawDataAtDestructor, LinkLayerType layerType) { + timespec nsec_time; + TIMEVAL_TO_TIMESPEC(×tamp, &nsec_time); + init(deleteRawDataAtDestructor); + setRawData(pRawData, rawDataLen, nsec_time, layerType); } -RawPacket::RawPacket() -{ - init(); +RawPacket::RawPacket(const uint8_t* pRawData, int rawDataLen, + timespec timestamp, bool deleteRawDataAtDestructor, + LinkLayerType layerType) { + init(deleteRawDataAtDestructor); + setRawData(pRawData, rawDataLen, timestamp, layerType); } -RawPacket::~RawPacket() -{ - if (m_DeleteRawDataAtDestructor) - { - delete[] m_RawData; - } +RawPacket::RawPacket() { init(); } + +RawPacket::~RawPacket() { + if (m_DeleteRawDataAtDestructor) { + delete[] m_RawData; + } } -RawPacket::RawPacket(const RawPacket& other) -{ - m_RawData = nullptr; - copyDataFrom(other, true); +RawPacket::RawPacket(const RawPacket& other) { + m_RawData = nullptr; + copyDataFrom(other, true); } -RawPacket& RawPacket::operator=(const RawPacket& other) -{ - if (this != &other) - { - if (m_RawData != nullptr) - delete [] m_RawData; +RawPacket& RawPacket::operator=(const RawPacket& other) { + if (this != &other) { + if (m_RawData != nullptr) + delete[] m_RawData; - m_RawPacketSet = false; + m_RawPacketSet = false; - copyDataFrom(other, true); - } + copyDataFrom(other, true); + } - return *this; + return *this; } +void RawPacket::copyDataFrom(const RawPacket& other, bool allocateData) { + if (!other.m_RawPacketSet) + return; -void RawPacket::copyDataFrom(const RawPacket& other, bool allocateData) -{ - if (!other.m_RawPacketSet) - return; + m_TimeStamp = other.m_TimeStamp; - m_TimeStamp = other.m_TimeStamp; + if (allocateData) { + m_DeleteRawDataAtDestructor = true; + m_RawData = new uint8_t[other.m_RawDataLen]; + m_RawDataLen = other.m_RawDataLen; + } - if (allocateData) - { - m_DeleteRawDataAtDestructor = true; - m_RawData = new uint8_t[other.m_RawDataLen]; - m_RawDataLen = other.m_RawDataLen; - } - - memcpy(m_RawData, other.m_RawData, other.m_RawDataLen); - m_LinkLayerType = other.m_LinkLayerType; - m_FrameLength = other.m_FrameLength; - m_RawPacketSet = true; + memcpy(m_RawData, other.m_RawData, other.m_RawDataLen); + m_LinkLayerType = other.m_LinkLayerType; + m_FrameLength = other.m_FrameLength; + m_RawPacketSet = true; } -bool RawPacket::setRawData(const uint8_t* pRawData, int rawDataLen, timeval timestamp, LinkLayerType layerType, int frameLength) -{ - timespec nsec_time; - TIMEVAL_TO_TIMESPEC(×tamp, &nsec_time); - return setRawData(pRawData, rawDataLen, nsec_time, layerType, frameLength); +bool RawPacket::setRawData(const uint8_t* pRawData, int rawDataLen, + timeval timestamp, LinkLayerType layerType, + int frameLength) { + timespec nsec_time; + TIMEVAL_TO_TIMESPEC(×tamp, &nsec_time); + return setRawData(pRawData, rawDataLen, nsec_time, layerType, frameLength); } -bool RawPacket::setRawData(const uint8_t* pRawData, int rawDataLen, timespec timestamp, LinkLayerType layerType, int frameLength) -{ - if(frameLength == -1) - frameLength = rawDataLen; - m_FrameLength = frameLength; - if (m_RawData != nullptr && m_DeleteRawDataAtDestructor) - { - delete[] m_RawData; - } - - m_RawData = (uint8_t*)pRawData; - m_RawDataLen = rawDataLen; - m_TimeStamp = timestamp; - m_RawPacketSet = true; - m_LinkLayerType = layerType; - return true; +bool RawPacket::setRawData(const uint8_t* pRawData, int rawDataLen, + timespec timestamp, LinkLayerType layerType, + int frameLength) { + if (frameLength == -1) + frameLength = rawDataLen; + m_FrameLength = frameLength; + if (m_RawData != nullptr && m_DeleteRawDataAtDestructor) { + delete[] m_RawData; + } + + m_RawData = (uint8_t*)pRawData; + m_RawDataLen = rawDataLen; + m_TimeStamp = timestamp; + m_RawPacketSet = true; + m_LinkLayerType = layerType; + return true; } -bool RawPacket::initWithRawData(const uint8_t* pRawData, int rawDataLen, timespec timestamp, LinkLayerType layerType) -{ - init(false); - return setRawData(pRawData, rawDataLen, timestamp, layerType); +bool RawPacket::initWithRawData(const uint8_t* pRawData, int rawDataLen, + timespec timestamp, LinkLayerType layerType) { + init(false); + return setRawData(pRawData, rawDataLen, timestamp, layerType); } -void RawPacket::clear() -{ - if (m_RawData != nullptr) - delete[] m_RawData; +void RawPacket::clear() { + if (m_RawData != nullptr) + delete[] m_RawData; - m_RawData = nullptr; - m_RawDataLen = 0; - m_FrameLength = 0; - m_RawPacketSet = false; + m_RawData = nullptr; + m_RawDataLen = 0; + m_FrameLength = 0; + m_RawPacketSet = false; } -void RawPacket::appendData(const uint8_t* dataToAppend, size_t dataToAppendLen) -{ - memcpy((uint8_t*)m_RawData + m_RawDataLen, dataToAppend, dataToAppendLen); - m_RawDataLen += dataToAppendLen; - m_FrameLength = m_RawDataLen; +void RawPacket::appendData(const uint8_t* dataToAppend, + size_t dataToAppendLen) { + memcpy((uint8_t*)m_RawData + m_RawDataLen, dataToAppend, dataToAppendLen); + m_RawDataLen += dataToAppendLen; + m_FrameLength = m_RawDataLen; } -void RawPacket::insertData(int atIndex, const uint8_t* dataToInsert, size_t dataToInsertLen) -{ - // memmove copies data as if there was an intermediate buffer in between - so it allows for copying processes on overlapping src/dest ptrs - // if insertData is called with atIndex == m_RawDataLen, then no data is being moved. The data of the raw packet is still extended by dataToInsertLen - memmove((uint8_t*)m_RawData + atIndex + dataToInsertLen, (uint8_t*)m_RawData + atIndex, m_RawDataLen - atIndex); - - if (dataToInsert != nullptr) - { - // insert data - memcpy((uint8_t*)m_RawData + atIndex, dataToInsert, dataToInsertLen); - } - - m_RawDataLen += dataToInsertLen; - m_FrameLength = m_RawDataLen; +void RawPacket::insertData(int atIndex, const uint8_t* dataToInsert, + size_t dataToInsertLen) { + // memmove copies data as if there was an intermediate buffer in between - so + // it allows for copying processes on overlapping src/dest ptrs if insertData + // is called with atIndex == m_RawDataLen, then no data is being moved. The + // data of the raw packet is still extended by dataToInsertLen + memmove((uint8_t*)m_RawData + atIndex + dataToInsertLen, + (uint8_t*)m_RawData + atIndex, m_RawDataLen - atIndex); + + if (dataToInsert != nullptr) { + // insert data + memcpy((uint8_t*)m_RawData + atIndex, dataToInsert, dataToInsertLen); + } + + m_RawDataLen += dataToInsertLen; + m_FrameLength = m_RawDataLen; } -bool RawPacket::reallocateData(size_t newBufferLength) -{ - if ((int)newBufferLength == m_RawDataLen) - return true; +bool RawPacket::reallocateData(size_t newBufferLength) { + if ((int)newBufferLength == m_RawDataLen) + return true; - if ((int)newBufferLength < m_RawDataLen) - { - PCPP_LOG_ERROR("Cannot reallocate raw packet to a smaller size. Current data length: " << m_RawDataLen << "; requested length: " << newBufferLength); - return false; - } + if ((int)newBufferLength < m_RawDataLen) { + PCPP_LOG_ERROR( + "Cannot reallocate raw packet to a smaller size. Current data length: " + << m_RawDataLen << "; requested length: " << newBufferLength); + return false; + } - uint8_t* newBuffer = new uint8_t[newBufferLength]; - memset(newBuffer, 0, newBufferLength); - memcpy(newBuffer, m_RawData, m_RawDataLen); - if (m_DeleteRawDataAtDestructor) - delete [] m_RawData; + uint8_t* newBuffer = new uint8_t[newBufferLength]; + memset(newBuffer, 0, newBufferLength); + memcpy(newBuffer, m_RawData, m_RawDataLen); + if (m_DeleteRawDataAtDestructor) + delete[] m_RawData; - m_DeleteRawDataAtDestructor = true; - m_RawData = newBuffer; + m_DeleteRawDataAtDestructor = true; + m_RawData = newBuffer; - return true; + return true; } -bool RawPacket::removeData(int atIndex, size_t numOfBytesToRemove) -{ - if ((atIndex + (int)numOfBytesToRemove) > m_RawDataLen) - { - PCPP_LOG_ERROR("Remove section is out of raw packet bound"); - return false; - } - - // only move data if we are removing data somewhere in the layer, not at the end of the last layer - // this is so that resizing of the last layer can occur fast by just reducing the fictional length of the packet (m_RawDataLen) by the given amount - if((atIndex + (int)numOfBytesToRemove) != m_RawDataLen) - // memmove copies data as if there was an intermediate buffer in between - so it allows for copying processes on overlapping src/dest ptrs - memmove((uint8_t*)m_RawData + atIndex, (uint8_t*)m_RawData + atIndex + numOfBytesToRemove, m_RawDataLen - (atIndex + numOfBytesToRemove)); - - m_RawDataLen -= numOfBytesToRemove; - m_FrameLength = m_RawDataLen; - return true; +bool RawPacket::removeData(int atIndex, size_t numOfBytesToRemove) { + if ((atIndex + (int)numOfBytesToRemove) > m_RawDataLen) { + PCPP_LOG_ERROR("Remove section is out of raw packet bound"); + return false; + } + + // only move data if we are removing data somewhere in the layer, not at the + // end of the last layer this is so that resizing of the last layer can occur + // fast by just reducing the fictional length of the packet (m_RawDataLen) by + // the given amount + if ((atIndex + (int)numOfBytesToRemove) != m_RawDataLen) + // memmove copies data as if there was an intermediate buffer in between - + // so it allows for copying processes on overlapping src/dest ptrs + memmove((uint8_t*)m_RawData + atIndex, + (uint8_t*)m_RawData + atIndex + numOfBytesToRemove, + m_RawDataLen - (atIndex + numOfBytesToRemove)); + + m_RawDataLen -= numOfBytesToRemove; + m_FrameLength = m_RawDataLen; + return true; } -bool RawPacket::setPacketTimeStamp(timeval timestamp) -{ - timespec nsec_time; - TIMEVAL_TO_TIMESPEC(×tamp, &nsec_time); - return setPacketTimeStamp(nsec_time); +bool RawPacket::setPacketTimeStamp(timeval timestamp) { + timespec nsec_time; + TIMEVAL_TO_TIMESPEC(×tamp, &nsec_time); + return setPacketTimeStamp(nsec_time); } -bool RawPacket::setPacketTimeStamp(timespec timestamp) -{ - m_TimeStamp = timestamp; - return true; +bool RawPacket::setPacketTimeStamp(timespec timestamp) { + m_TimeStamp = timestamp; + return true; } -bool RawPacket::isLinkTypeValid(int linkTypeValue) -{ - if ((linkTypeValue < 0 || linkTypeValue > 264) && linkTypeValue != 276) - return false; - - switch (static_cast(linkTypeValue)) - { - case LINKTYPE_ETHERNET: - case LINKTYPE_LINUX_SLL: - case LINKTYPE_RAW: - case LINKTYPE_DLT_RAW1: - case LINKTYPE_DLT_RAW2: - case LINKTYPE_NULL: - case LINKTYPE_AX25: - case LINKTYPE_IEEE802_5: - case LINKTYPE_ARCNET_BSD: - case LINKTYPE_SLIP: - case LINKTYPE_PPP: - case LINKTYPE_FDDI: - case LINKTYPE_PPP_HDLC: - case LINKTYPE_PPP_ETHER: - case LINKTYPE_ATM_RFC1483: - case LINKTYPE_C_HDLC: - case LINKTYPE_IEEE802_11: - case LINKTYPE_FRELAY: - case LINKTYPE_LOOP: - case LINKTYPE_LTALK: - case LINKTYPE_PFLOG: - case LINKTYPE_IEEE802_11_PRISM: - case LINKTYPE_IP_OVER_FC: - case LINKTYPE_SUNATM: - case LINKTYPE_IEEE802_11_RADIOTAP: - case LINKTYPE_ARCNET_LINUX: - case LINKTYPE_APPLE_IP_OVER_IEEE1394: - case LINKTYPE_MTP2_WITH_PHDR: - case LINKTYPE_MTP2: - case LINKTYPE_MTP3: - case LINKTYPE_SCCP: - case LINKTYPE_DOCSIS: - case LINKTYPE_LINUX_IRDA: - case LINKTYPE_IEEE802_11_AVS: - case LINKTYPE_BACNET_MS_TP: - case LINKTYPE_PPP_PPPD: - case LINKTYPE_GPRS_LLC: - case LINKTYPE_GPF_T: - case LINKTYPE_GPF_F: - case LINKTYPE_LINUX_LAPD: - case LINKTYPE_BLUETOOTH_HCI_H4: - case LINKTYPE_USB_LINUX: - case LINKTYPE_PPI: - case LINKTYPE_IEEE802_15_4: - case LINKTYPE_SITA: - case LINKTYPE_ERF: - case LINKTYPE_BLUETOOTH_HCI_H4_WITH_PHDR: - case LINKTYPE_AX25_KISS: - case LINKTYPE_LAPD: - case LINKTYPE_PPP_WITH_DIR: - case LINKTYPE_C_HDLC_WITH_DIR: - case LINKTYPE_FRELAY_WITH_DIR: - case LINKTYPE_IPMB_LINUX: - case LINKTYPE_IEEE802_15_4_NONASK_PHY: - case LINKTYPE_USB_LINUX_MMAPPED: - case LINKTYPE_FC_2: - case LINKTYPE_FC_2_WITH_FRAME_DELIMS: - case LINKTYPE_IPNET: - case LINKTYPE_CAN_SOCKETCAN: - case LINKTYPE_IPV4: - case LINKTYPE_IPV6: - case LINKTYPE_IEEE802_15_4_NOFCS: - case LINKTYPE_DBUS: - case LINKTYPE_DVB_CI: - case LINKTYPE_MUX27010: - case LINKTYPE_STANAG_5066_D_PDU: - case LINKTYPE_NFLOG: - case LINKTYPE_NETANALYZER: - case LINKTYPE_NETANALYZER_TRANSPARENT: - case LINKTYPE_IPOIB: - case LINKTYPE_MPEG_2_TS: - case LINKTYPE_NG40: - case LINKTYPE_NFC_LLCP: - case LINKTYPE_INFINIBAND: - case LINKTYPE_SCTP: - case LINKTYPE_USBPCAP: - case LINKTYPE_RTAC_SERIAL: - case LINKTYPE_BLUETOOTH_LE_LL: - case LINKTYPE_NETLINK: - case LINKTYPE_BLUETOOTH_LINUX_MONITOR: - case LINKTYPE_BLUETOOTH_BREDR_BB: - case LINKTYPE_BLUETOOTH_LE_LL_WITH_PHDR: - case LINKTYPE_PROFIBUS_DL: - case LINKTYPE_PKTAP: - case LINKTYPE_EPON: - case LINKTYPE_IPMI_HPM_2: - case LINKTYPE_ZWAVE_R1_R2: - case LINKTYPE_ZWAVE_R3: - case LINKTYPE_WATTSTOPPER_DLM: - case LINKTYPE_ISO_14443: - case LINKTYPE_LINUX_SLL2: - return true; - default: - return false; - } +bool RawPacket::isLinkTypeValid(int linkTypeValue) { + if ((linkTypeValue < 0 || linkTypeValue > 264) && linkTypeValue != 276) + return false; + + switch (static_cast(linkTypeValue)) { + case LINKTYPE_ETHERNET: + case LINKTYPE_LINUX_SLL: + case LINKTYPE_RAW: + case LINKTYPE_DLT_RAW1: + case LINKTYPE_DLT_RAW2: + case LINKTYPE_NULL: + case LINKTYPE_AX25: + case LINKTYPE_IEEE802_5: + case LINKTYPE_ARCNET_BSD: + case LINKTYPE_SLIP: + case LINKTYPE_PPP: + case LINKTYPE_FDDI: + case LINKTYPE_PPP_HDLC: + case LINKTYPE_PPP_ETHER: + case LINKTYPE_ATM_RFC1483: + case LINKTYPE_C_HDLC: + case LINKTYPE_IEEE802_11: + case LINKTYPE_FRELAY: + case LINKTYPE_LOOP: + case LINKTYPE_LTALK: + case LINKTYPE_PFLOG: + case LINKTYPE_IEEE802_11_PRISM: + case LINKTYPE_IP_OVER_FC: + case LINKTYPE_SUNATM: + case LINKTYPE_IEEE802_11_RADIOTAP: + case LINKTYPE_ARCNET_LINUX: + case LINKTYPE_APPLE_IP_OVER_IEEE1394: + case LINKTYPE_MTP2_WITH_PHDR: + case LINKTYPE_MTP2: + case LINKTYPE_MTP3: + case LINKTYPE_SCCP: + case LINKTYPE_DOCSIS: + case LINKTYPE_LINUX_IRDA: + case LINKTYPE_IEEE802_11_AVS: + case LINKTYPE_BACNET_MS_TP: + case LINKTYPE_PPP_PPPD: + case LINKTYPE_GPRS_LLC: + case LINKTYPE_GPF_T: + case LINKTYPE_GPF_F: + case LINKTYPE_LINUX_LAPD: + case LINKTYPE_BLUETOOTH_HCI_H4: + case LINKTYPE_USB_LINUX: + case LINKTYPE_PPI: + case LINKTYPE_IEEE802_15_4: + case LINKTYPE_SITA: + case LINKTYPE_ERF: + case LINKTYPE_BLUETOOTH_HCI_H4_WITH_PHDR: + case LINKTYPE_AX25_KISS: + case LINKTYPE_LAPD: + case LINKTYPE_PPP_WITH_DIR: + case LINKTYPE_C_HDLC_WITH_DIR: + case LINKTYPE_FRELAY_WITH_DIR: + case LINKTYPE_IPMB_LINUX: + case LINKTYPE_IEEE802_15_4_NONASK_PHY: + case LINKTYPE_USB_LINUX_MMAPPED: + case LINKTYPE_FC_2: + case LINKTYPE_FC_2_WITH_FRAME_DELIMS: + case LINKTYPE_IPNET: + case LINKTYPE_CAN_SOCKETCAN: + case LINKTYPE_IPV4: + case LINKTYPE_IPV6: + case LINKTYPE_IEEE802_15_4_NOFCS: + case LINKTYPE_DBUS: + case LINKTYPE_DVB_CI: + case LINKTYPE_MUX27010: + case LINKTYPE_STANAG_5066_D_PDU: + case LINKTYPE_NFLOG: + case LINKTYPE_NETANALYZER: + case LINKTYPE_NETANALYZER_TRANSPARENT: + case LINKTYPE_IPOIB: + case LINKTYPE_MPEG_2_TS: + case LINKTYPE_NG40: + case LINKTYPE_NFC_LLCP: + case LINKTYPE_INFINIBAND: + case LINKTYPE_SCTP: + case LINKTYPE_USBPCAP: + case LINKTYPE_RTAC_SERIAL: + case LINKTYPE_BLUETOOTH_LE_LL: + case LINKTYPE_NETLINK: + case LINKTYPE_BLUETOOTH_LINUX_MONITOR: + case LINKTYPE_BLUETOOTH_BREDR_BB: + case LINKTYPE_BLUETOOTH_LE_LL_WITH_PHDR: + case LINKTYPE_PROFIBUS_DL: + case LINKTYPE_PKTAP: + case LINKTYPE_EPON: + case LINKTYPE_IPMI_HPM_2: + case LINKTYPE_ZWAVE_R1_R2: + case LINKTYPE_ZWAVE_R3: + case LINKTYPE_WATTSTOPPER_DLM: + case LINKTYPE_ISO_14443: + case LINKTYPE_LINUX_SLL2: + return true; + default: + return false; + } } } // namespace pcpp diff --git a/Packet++/src/S7CommLayer.cpp b/Packet++/src/S7CommLayer.cpp index a61fa1aa28..27e6e04016 100644 --- a/Packet++/src/S7CommLayer.cpp +++ b/Packet++/src/S7CommLayer.cpp @@ -7,119 +7,129 @@ #include #include -namespace pcpp -{ - - S7CommLayer::S7CommLayer(uint8_t msgType, uint16_t pduRef, uint16_t paramLength, uint16_t dataLength, - uint8_t errorClass, uint8_t errorCode) - { - size_t basicHeaderLen = msgType == 0x03 ? sizeof(s7comm_ack_data_hdr) : sizeof(s7commhdr); - size_t headerLen = basicHeaderLen + paramLength + dataLength; - m_DataLen = headerLen; - m_Data = new uint8_t[headerLen]; - memset(m_Data, 0, headerLen); - - if (msgType == 0x03) - { - auto *ack_d = (s7comm_ack_data_hdr *)m_Data; - ack_d->protocolId = 0x32; - ack_d->msgType = msgType; - ack_d->reserved = 0x0000; - ack_d->pduRef = htobe16(pduRef); - ack_d->paramLength = htobe16(paramLength); - ack_d->dataLength = htobe16(dataLength); - ack_d->errorClass = errorClass; - ack_d->errorCode = errorCode; - } - else - { - auto *s7commHdr = (s7commhdr *)m_Data; - s7commHdr->protocolId = 0x32; - s7commHdr->msgType = msgType; - s7commHdr->reserved = 0x0000; - s7commHdr->pduRef = htobe16(pduRef); - s7commHdr->paramLength = htobe16(paramLength); - s7commHdr->dataLength = htobe16(dataLength); - } - - m_Parameter = nullptr; - m_Protocol = S7COMM; - } - - std::string S7CommLayer::toString() const - { - std::ostringstream str; - str << "S7Comm Layer, "; - - switch (getS7commHeader()->msgType) - { - case 0x01: - str << "Job Request"; - break; - case 0x02: - str << "Ack"; - break; - case 0x03: - str << "Ack-Data"; - break; - case 0x07: - str << "Userdata"; - break; - default: - str << "Unknown message"; - } - - return str.str(); - } - - bool S7CommLayer::isDataValid(const uint8_t *data, size_t dataSize) - { - if (!data || dataSize < sizeof(s7commhdr)) - return false; - - return data[0] == 0x32; - } - - uint8_t S7CommLayer::getProtocolId() const { return getS7commHeader()->protocolId; } - - uint8_t S7CommLayer::getMsgType() const { return getS7commHeader()->msgType; } - - uint16_t S7CommLayer::getParamLength() const { return be16toh(getS7commHeader()->paramLength); } - - uint16_t S7CommLayer::getPduRef() const { return be16toh(getS7commHeader()->pduRef); } - - uint16_t S7CommLayer::getDataLength() const { return be16toh(getS7commHeader()->dataLength); } - - void S7CommLayer::setMsgType(uint8_t msgType) const { getS7commHeader()->msgType = msgType; } - - uint8_t S7CommLayer::getErrorCode() const { return getS7commAckDataHeader()->errorCode; } - - uint8_t S7CommLayer::getErrorClass() const { return getS7commAckDataHeader()->errorClass; } - - void S7CommLayer::setPduRef(uint16_t pduRef) const { getS7commHeader()->pduRef = htobe16(pduRef); } - - void S7CommLayer::setErrorCode(uint8_t errorCode) const { getS7commAckDataHeader()->errorCode = errorCode; } - - void S7CommLayer::setErrorClass(uint8_t errorClass) const { getS7commAckDataHeader()->errorClass = errorClass; } - - const S7CommParameter *S7CommLayer::getParameter() - { - if (!m_Parameter) - { - uint8_t *payload = m_Data + getS7commHeaderLength(); - m_Parameter = new S7CommParameter(payload, getParamLength()); - } - - return m_Parameter; - } - - size_t S7CommLayer::getS7commHeaderLength() const - { - if (getS7commHeader()->msgType == 0x03) - { - return sizeof(s7comm_ack_data_hdr); - } - return sizeof(s7commhdr); - } +namespace pcpp { + +S7CommLayer::S7CommLayer(uint8_t msgType, uint16_t pduRef, uint16_t paramLength, + uint16_t dataLength, uint8_t errorClass, + uint8_t errorCode) { + size_t basicHeaderLen = + msgType == 0x03 ? sizeof(s7comm_ack_data_hdr) : sizeof(s7commhdr); + size_t headerLen = basicHeaderLen + paramLength + dataLength; + m_DataLen = headerLen; + m_Data = new uint8_t[headerLen]; + memset(m_Data, 0, headerLen); + + if (msgType == 0x03) { + auto* ack_d = (s7comm_ack_data_hdr*)m_Data; + ack_d->protocolId = 0x32; + ack_d->msgType = msgType; + ack_d->reserved = 0x0000; + ack_d->pduRef = htobe16(pduRef); + ack_d->paramLength = htobe16(paramLength); + ack_d->dataLength = htobe16(dataLength); + ack_d->errorClass = errorClass; + ack_d->errorCode = errorCode; + } else { + auto* s7commHdr = (s7commhdr*)m_Data; + s7commHdr->protocolId = 0x32; + s7commHdr->msgType = msgType; + s7commHdr->reserved = 0x0000; + s7commHdr->pduRef = htobe16(pduRef); + s7commHdr->paramLength = htobe16(paramLength); + s7commHdr->dataLength = htobe16(dataLength); + } + + m_Parameter = nullptr; + m_Protocol = S7COMM; +} + +std::string S7CommLayer::toString() const { + std::ostringstream str; + str << "S7Comm Layer, "; + + switch (getS7commHeader()->msgType) { + case 0x01: + str << "Job Request"; + break; + case 0x02: + str << "Ack"; + break; + case 0x03: + str << "Ack-Data"; + break; + case 0x07: + str << "Userdata"; + break; + default: + str << "Unknown message"; + } + + return str.str(); +} + +bool S7CommLayer::isDataValid(const uint8_t* data, size_t dataSize) { + if (!data || dataSize < sizeof(s7commhdr)) + return false; + + return data[0] == 0x32; +} + +uint8_t S7CommLayer::getProtocolId() const { + return getS7commHeader()->protocolId; +} + +uint8_t S7CommLayer::getMsgType() const { return getS7commHeader()->msgType; } + +uint16_t S7CommLayer::getParamLength() const { + return be16toh(getS7commHeader()->paramLength); +} + +uint16_t S7CommLayer::getPduRef() const { + return be16toh(getS7commHeader()->pduRef); +} + +uint16_t S7CommLayer::getDataLength() const { + return be16toh(getS7commHeader()->dataLength); +} + +void S7CommLayer::setMsgType(uint8_t msgType) const { + getS7commHeader()->msgType = msgType; +} + +uint8_t S7CommLayer::getErrorCode() const { + return getS7commAckDataHeader()->errorCode; +} + +uint8_t S7CommLayer::getErrorClass() const { + return getS7commAckDataHeader()->errorClass; +} + +void S7CommLayer::setPduRef(uint16_t pduRef) const { + getS7commHeader()->pduRef = htobe16(pduRef); +} + +void S7CommLayer::setErrorCode(uint8_t errorCode) const { + getS7commAckDataHeader()->errorCode = errorCode; +} + +void S7CommLayer::setErrorClass(uint8_t errorClass) const { + getS7commAckDataHeader()->errorClass = errorClass; +} + +const S7CommParameter* S7CommLayer::getParameter() { + if (!m_Parameter) { + uint8_t* payload = m_Data + getS7commHeaderLength(); + m_Parameter = new S7CommParameter(payload, getParamLength()); + } + + return m_Parameter; +} + +size_t S7CommLayer::getS7commHeaderLength() const { + if (getS7commHeader()->msgType == 0x03) { + return sizeof(s7comm_ack_data_hdr); + } + return sizeof(s7commhdr); +} } // namespace pcpp diff --git a/Packet++/src/SSHLayer.cpp b/Packet++/src/SSHLayer.cpp index 5a862bde5b..4b6c44ac3a 100644 --- a/Packet++/src/SSHLayer.cpp +++ b/Packet++/src/SSHLayer.cpp @@ -1,262 +1,259 @@ #define LOG_MODULE PacketLogModuleSSHLayer #include "SSHLayer.h" +#include "EndianPortable.h" #include "GeneralUtils.h" #include "Logger.h" -#include "EndianPortable.h" #include -namespace pcpp -{ +namespace pcpp { #define SSH_LAYER_BASE_STRING "SSH Layer" - // ---------------- // SSHLayer methods // ---------------- -SSHLayer* SSHLayer::createSSHMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) -{ - SSHIdentificationMessage* sshIdnetMsg = SSHIdentificationMessage::tryParse(data, dataLen, prevLayer, packet); - if (sshIdnetMsg != nullptr) - return sshIdnetMsg; +SSHLayer* SSHLayer::createSSHMessage(uint8_t* data, size_t dataLen, + Layer* prevLayer, Packet* packet) { + SSHIdentificationMessage* sshIdnetMsg = + SSHIdentificationMessage::tryParse(data, dataLen, prevLayer, packet); + if (sshIdnetMsg != nullptr) + return sshIdnetMsg; - SSHHandshakeMessage* sshHandshakeMessage = SSHHandshakeMessage::tryParse(data, dataLen, prevLayer, packet); - if (sshHandshakeMessage != nullptr) - return sshHandshakeMessage; + SSHHandshakeMessage* sshHandshakeMessage = + SSHHandshakeMessage::tryParse(data, dataLen, prevLayer, packet); + if (sshHandshakeMessage != nullptr) + return sshHandshakeMessage; - return new SSHEncryptedMessage(data, dataLen, prevLayer, packet); + return new SSHEncryptedMessage(data, dataLen, prevLayer, packet); } -void SSHLayer::parseNextLayer() -{ - size_t headerLen = getHeaderLen(); - if (m_DataLen <= headerLen) - return; - m_NextLayer = SSHLayer::createSSHMessage(m_Data + headerLen, m_DataLen - headerLen, this, m_Packet); +void SSHLayer::parseNextLayer() { + size_t headerLen = getHeaderLen(); + if (m_DataLen <= headerLen) + return; + m_NextLayer = SSHLayer::createSSHMessage( + m_Data + headerLen, m_DataLen - headerLen, this, m_Packet); } - // -------------------------------- // SSHIdentificationMessage methods // -------------------------------- -SSHIdentificationMessage* SSHIdentificationMessage::tryParse(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) -{ - // Payload must be at least as long as the string "SSH-" - if (dataLen < 5) - return nullptr; +SSHIdentificationMessage* SSHIdentificationMessage::tryParse(uint8_t* data, + size_t dataLen, + Layer* prevLayer, + Packet* packet) { + // Payload must be at least as long as the string "SSH-" + if (dataLen < 5) + return nullptr; - // Payload must begin with "SSH-" and end with "\n" - if (data[0] == 0x53 && data[1] == 0x53 && data[2] == 0x48 && data[3] == 0x2d && data[dataLen - 1] == 0x0a) - return new SSHIdentificationMessage(data, dataLen, prevLayer, packet); + // Payload must begin with "SSH-" and end with "\n" + if (data[0] == 0x53 && data[1] == 0x53 && data[2] == 0x48 && + data[3] == 0x2d && data[dataLen - 1] == 0x0a) + return new SSHIdentificationMessage(data, dataLen, prevLayer, packet); - return nullptr; + return nullptr; } -std::string SSHIdentificationMessage::getIdentificationMessage() -{ - // check if message ends with "\r\n" or just with "\n" - size_t identMsgEOL = (m_Data[m_DataLen - 2] == 0x0d ? 2 : 1); - return std::string(reinterpret_cast(m_Data), m_DataLen - identMsgEOL); +std::string SSHIdentificationMessage::getIdentificationMessage() { + // check if message ends with "\r\n" or just with "\n" + size_t identMsgEOL = (m_Data[m_DataLen - 2] == 0x0d ? 2 : 1); + return std::string(reinterpret_cast(m_Data), + m_DataLen - identMsgEOL); } -std::string SSHIdentificationMessage::toString() const -{ - return std::string(SSH_LAYER_BASE_STRING) + ", " + "Identification message"; +std::string SSHIdentificationMessage::toString() const { + return std::string(SSH_LAYER_BASE_STRING) + ", " + "Identification message"; } - // --------------------------- // SSHHandshakeMessage methods // --------------------------- -SSHHandshakeMessage::SSHHandshakeMessageType SSHHandshakeMessage::getMessageType() const -{ - uint8_t messageCode = getMsgBaseHeader()->messageCode; - if (messageCode == 20 || messageCode == 21 || (messageCode >= 30 && messageCode <= 34)) - return static_cast(messageCode); - return SSHHandshakeMessage::SSH_MSG_UNKNOWN; +SSHHandshakeMessage::SSHHandshakeMessageType +SSHHandshakeMessage::getMessageType() const { + uint8_t messageCode = getMsgBaseHeader()->messageCode; + if (messageCode == 20 || messageCode == 21 || + (messageCode >= 30 && messageCode <= 34)) + return static_cast( + messageCode); + return SSHHandshakeMessage::SSH_MSG_UNKNOWN; } -std::string SSHHandshakeMessage::getMessageTypeStr() const -{ - switch (getMessageType()) - { - case SSHHandshakeMessage::SSH_MSG_KEX_INIT: - return "Key Exchange Init"; - case SSHHandshakeMessage::SSH_MSG_NEW_KEYS: - return "New Keys"; - case SSHHandshakeMessage::SSH_MSG_KEX_DH_INIT: - return "Diffie-Hellman Key Exchange Init"; - case SSHHandshakeMessage::SSH_MSG_KEX_DH_REPLY: - return "Diffie-Hellman Key Exchange Reply"; - case SSHHandshakeMessage::SSH_MSG_KEX_DH_GEX_INIT: - return "Diffie-Hellman Group Exchange Init"; - case SSHHandshakeMessage::SSH_MSG_KEX_DH_GEX_REPLY: - return "Diffie-Hellman Group Exchange Reply"; - case SSHHandshakeMessage::SSH_MSG_KEX_DH_GEX_REQUEST: - return "Diffie-Hellman Group Exchange Request"; - default: - return "Unknown"; - - } +std::string SSHHandshakeMessage::getMessageTypeStr() const { + switch (getMessageType()) { + case SSHHandshakeMessage::SSH_MSG_KEX_INIT: + return "Key Exchange Init"; + case SSHHandshakeMessage::SSH_MSG_NEW_KEYS: + return "New Keys"; + case SSHHandshakeMessage::SSH_MSG_KEX_DH_INIT: + return "Diffie-Hellman Key Exchange Init"; + case SSHHandshakeMessage::SSH_MSG_KEX_DH_REPLY: + return "Diffie-Hellman Key Exchange Reply"; + case SSHHandshakeMessage::SSH_MSG_KEX_DH_GEX_INIT: + return "Diffie-Hellman Group Exchange Init"; + case SSHHandshakeMessage::SSH_MSG_KEX_DH_GEX_REPLY: + return "Diffie-Hellman Group Exchange Reply"; + case SSHHandshakeMessage::SSH_MSG_KEX_DH_GEX_REQUEST: + return "Diffie-Hellman Group Exchange Request"; + default: + return "Unknown"; + } } -uint8_t* SSHHandshakeMessage::getSSHHandshakeMessage() const -{ - return m_Data + sizeof(SSHHandshakeMessage::ssh_message_base); +uint8_t* SSHHandshakeMessage::getSSHHandshakeMessage() const { + return m_Data + sizeof(SSHHandshakeMessage::ssh_message_base); } -size_t SSHHandshakeMessage::getSSHHandshakeMessageLength() const -{ - uint32_t length = be32toh(getMsgBaseHeader()->packetLength); - return static_cast(length) - getMsgBaseHeader()->paddingLength - sizeof(uint8_t)*2; +size_t SSHHandshakeMessage::getSSHHandshakeMessageLength() const { + uint32_t length = be32toh(getMsgBaseHeader()->packetLength); + return static_cast(length) - getMsgBaseHeader()->paddingLength - + sizeof(uint8_t) * 2; } -size_t SSHHandshakeMessage::getPaddingLength() const -{ - return getMsgBaseHeader()->paddingLength; +size_t SSHHandshakeMessage::getPaddingLength() const { + return getMsgBaseHeader()->paddingLength; } -size_t SSHHandshakeMessage::getHeaderLen() const -{ - return (size_t)be32toh(getMsgBaseHeader()->packetLength) + sizeof(uint32_t); +size_t SSHHandshakeMessage::getHeaderLen() const { + return (size_t)be32toh(getMsgBaseHeader()->packetLength) + sizeof(uint32_t); } -std::string SSHHandshakeMessage::toString() const -{ - return std::string(SSH_LAYER_BASE_STRING) + ", " + "Handshake Message: " + getMessageTypeStr(); +std::string SSHHandshakeMessage::toString() const { + return std::string(SSH_LAYER_BASE_STRING) + ", " + + "Handshake Message: " + getMessageTypeStr(); } -SSHHandshakeMessage* SSHHandshakeMessage::tryParse(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) -{ - if (dataLen < sizeof(SSHHandshakeMessage::ssh_message_base)) - { - PCPP_LOG_DEBUG("Data length is smaller than the minimum size of an SSH handshake message. It's probably not an SSH handshake message"); - return nullptr; - } - - SSHHandshakeMessage::ssh_message_base* msgBase = (SSHHandshakeMessage::ssh_message_base*)data; - - uint32_t msgLength = be32toh(msgBase->packetLength); - if (msgLength + sizeof(uint32_t) > dataLen) - { - PCPP_LOG_DEBUG("Message size is larger than layer size. It's probably not an SSH handshake message"); - return nullptr; - } - - if (msgBase->paddingLength > msgLength) - { - PCPP_LOG_DEBUG("Message padding is larger than message size. It's probably not an SSH handshake message"); - return nullptr; - } - - if (msgBase->messageCode != 20 && - msgBase->messageCode != 21 && - (msgBase->messageCode < 30 || msgBase->messageCode > 49)) - { - PCPP_LOG_DEBUG("Unknown message type " << (int)msgBase->messageCode << ". It's probably not an SSH handshake message"); - return nullptr; - } - - switch (msgBase->messageCode) - { - case SSHHandshakeMessage::SSH_MSG_KEX_INIT: - return new SSHKeyExchangeInitMessage(data, dataLen, prevLayer, packet); - default: - return new SSHHandshakeMessage(data, dataLen, prevLayer, packet); - } +SSHHandshakeMessage* SSHHandshakeMessage::tryParse(uint8_t* data, + size_t dataLen, + Layer* prevLayer, + Packet* packet) { + if (dataLen < sizeof(SSHHandshakeMessage::ssh_message_base)) { + PCPP_LOG_DEBUG( + "Data length is smaller than the minimum size of an SSH handshake " + "message. It's probably not an SSH handshake message"); + return nullptr; + } + + SSHHandshakeMessage::ssh_message_base* msgBase = + (SSHHandshakeMessage::ssh_message_base*)data; + + uint32_t msgLength = be32toh(msgBase->packetLength); + if (msgLength + sizeof(uint32_t) > dataLen) { + PCPP_LOG_DEBUG("Message size is larger than layer size. It's probably not " + "an SSH handshake message"); + return nullptr; + } + + if (msgBase->paddingLength > msgLength) { + PCPP_LOG_DEBUG("Message padding is larger than message size. It's probably " + "not an SSH handshake message"); + return nullptr; + } + + if (msgBase->messageCode != 20 && msgBase->messageCode != 21 && + (msgBase->messageCode < 30 || msgBase->messageCode > 49)) { + PCPP_LOG_DEBUG("Unknown message type " + << (int)msgBase->messageCode + << ". It's probably not an SSH handshake message"); + return nullptr; + } + + switch (msgBase->messageCode) { + case SSHHandshakeMessage::SSH_MSG_KEX_INIT: + return new SSHKeyExchangeInitMessage(data, dataLen, prevLayer, packet); + default: + return new SSHHandshakeMessage(data, dataLen, prevLayer, packet); + } } - // --------------------------------- // SSHKeyExchangeInitMessage methods // --------------------------------- -SSHKeyExchangeInitMessage::SSHKeyExchangeInitMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : SSHHandshakeMessage(data, dataLen, prevLayer, packet), m_OffsetsInitialized(false) -{ - memset(m_FieldOffsets, 0, 11*sizeof(size_t)); +SSHKeyExchangeInitMessage::SSHKeyExchangeInitMessage(uint8_t* data, + size_t dataLen, + Layer* prevLayer, + Packet* packet) + : SSHHandshakeMessage(data, dataLen, prevLayer, packet), + m_OffsetsInitialized(false) { + memset(m_FieldOffsets, 0, 11 * sizeof(size_t)); } -void SSHKeyExchangeInitMessage::parseMessageAndInitOffsets() -{ - m_OffsetsInitialized = true; - if (m_DataLen <= sizeof(ssh_message_base) + 16) - return; +void SSHKeyExchangeInitMessage::parseMessageAndInitOffsets() { + m_OffsetsInitialized = true; + if (m_DataLen <= sizeof(ssh_message_base) + 16) + return; - size_t offset = sizeof(ssh_message_base) + 16; - for (int i = 0; i < 10; i++) - { - if (offset + sizeof(uint32_t) >= m_DataLen) - return; + size_t offset = sizeof(ssh_message_base) + 16; + for (int i = 0; i < 10; i++) { + if (offset + sizeof(uint32_t) >= m_DataLen) + return; - size_t fieldLength = static_cast(be32toh(*(uint32_t*)(m_Data + offset))); - if (offset + sizeof(uint32_t) + fieldLength > m_DataLen) - return; + size_t fieldLength = + static_cast(be32toh(*(uint32_t*)(m_Data + offset))); + if (offset + sizeof(uint32_t) + fieldLength > m_DataLen) + return; - PCPP_LOG_DEBUG("Field offset [" << i << "] = " << offset << ", length = " << fieldLength); - m_FieldOffsets[i] = offset; - offset += sizeof(uint32_t) + fieldLength; - } + PCPP_LOG_DEBUG("Field offset [" << i << "] = " << offset + << ", length = " << fieldLength); + m_FieldOffsets[i] = offset; + offset += sizeof(uint32_t) + fieldLength; + } - if (offset >= m_DataLen) - return; - - m_FieldOffsets[10] = offset; + if (offset >= m_DataLen) + return; + m_FieldOffsets[10] = offset; } -std::string SSHKeyExchangeInitMessage::getFieldValue(int fieldOffsetIndex) -{ - if (!m_OffsetsInitialized) - parseMessageAndInitOffsets(); +std::string SSHKeyExchangeInitMessage::getFieldValue(int fieldOffsetIndex) { + if (!m_OffsetsInitialized) + parseMessageAndInitOffsets(); - if (m_FieldOffsets[fieldOffsetIndex] == 0) - return ""; + if (m_FieldOffsets[fieldOffsetIndex] == 0) + return ""; - size_t fieldOffset = m_FieldOffsets[fieldOffsetIndex]; - uint32_t fieldLength = be32toh(*(uint32_t*)(m_Data + fieldOffset)); - return std::string(reinterpret_cast(m_Data + fieldOffset + sizeof(uint32_t)), (size_t)fieldLength); + size_t fieldOffset = m_FieldOffsets[fieldOffsetIndex]; + uint32_t fieldLength = be32toh(*(uint32_t*)(m_Data + fieldOffset)); + return std::string( + reinterpret_cast(m_Data + fieldOffset + sizeof(uint32_t)), + (size_t)fieldLength); } -uint8_t* SSHKeyExchangeInitMessage::getCookie() -{ - if (m_DataLen < sizeof(ssh_message_base) + 16) - return nullptr; +uint8_t* SSHKeyExchangeInitMessage::getCookie() { + if (m_DataLen < sizeof(ssh_message_base) + 16) + return nullptr; - return m_Data + sizeof(ssh_message_base); + return m_Data + sizeof(ssh_message_base); } -std::string SSHKeyExchangeInitMessage::getCookieAsHexStream() -{ - uint8_t* cookie = getCookie(); - if (cookie == nullptr) - return ""; +std::string SSHKeyExchangeInitMessage::getCookieAsHexStream() { + uint8_t* cookie = getCookie(); + if (cookie == nullptr) + return ""; - return byteArrayToHexString(cookie, 16); + return byteArrayToHexString(cookie, 16); } -bool SSHKeyExchangeInitMessage::isFirstKexPacketFollows() -{ - if (!m_OffsetsInitialized) - parseMessageAndInitOffsets(); +bool SSHKeyExchangeInitMessage::isFirstKexPacketFollows() { + if (!m_OffsetsInitialized) + parseMessageAndInitOffsets(); - if (m_FieldOffsets[10] == 0) - return false; + if (m_FieldOffsets[10] == 0) + return false; - return m_Data[m_FieldOffsets[10]] != 0; + return m_Data[m_FieldOffsets[10]] != 0; } - // --------------------------- // SSHEncryptedMessage methods // --------------------------- -std::string SSHEncryptedMessage::toString() const -{ - return std::string(SSH_LAYER_BASE_STRING) + ", " + "Encrypted Message"; +std::string SSHEncryptedMessage::toString() const { + return std::string(SSH_LAYER_BASE_STRING) + ", " + "Encrypted Message"; } } // namespace pcpp diff --git a/Packet++/src/SSLCommon.cpp b/Packet++/src/SSLCommon.cpp index 08070c1f0d..81e5af9c0d 100644 --- a/Packet++/src/SSLCommon.cpp +++ b/Packet++/src/SSLCommon.cpp @@ -2,91 +2,85 @@ #include "SSLCommon.h" -namespace pcpp -{ +namespace pcpp { // ------------------------- // SSLVersion methods // ------------------------- -SSLVersion::SSLVersionEnum SSLVersion::asEnum(bool countTlsDraftsAs1_3) -{ - if (m_SSLVersionValue >= 0x0300 && m_SSLVersionValue <= 0x0304) - return static_cast(m_SSLVersionValue); +SSLVersion::SSLVersionEnum SSLVersion::asEnum(bool countTlsDraftsAs1_3) { + if (m_SSLVersionValue >= 0x0300 && m_SSLVersionValue <= 0x0304) + return static_cast(m_SSLVersionValue); - if ((m_SSLVersionValue >= 0x7f0e && m_SSLVersionValue <= 0x7f1c) || m_SSLVersionValue == 0xfb17 || m_SSLVersionValue == 0xfb1a) - { - if (countTlsDraftsAs1_3) - return SSLVersion::TLS1_3; - else - return static_cast(m_SSLVersionValue); - } + if ((m_SSLVersionValue >= 0x7f0e && m_SSLVersionValue <= 0x7f1c) || + m_SSLVersionValue == 0xfb17 || m_SSLVersionValue == 0xfb1a) { + if (countTlsDraftsAs1_3) + return SSLVersion::TLS1_3; + else + return static_cast(m_SSLVersionValue); + } - if (m_SSLVersionValue == 0x200) - return SSLVersion::SSL2; + if (m_SSLVersionValue == 0x200) + return SSLVersion::SSL2; - return SSLVersion::Unknown; + return SSLVersion::Unknown; } +std::string SSLVersion::toString(bool countTlsDraftsAs1_3) { + SSLVersionEnum enumValue = asEnum(countTlsDraftsAs1_3); -std::string SSLVersion::toString(bool countTlsDraftsAs1_3) -{ - SSLVersionEnum enumValue = asEnum(countTlsDraftsAs1_3); - - switch (enumValue) - { - case SSLVersion::TLS1_3: - return "TLS 1.3"; - case SSLVersion::TLS1_2: - return "TLS 1.2"; - case SSLVersion::TLS1_1: - return "TLS 1.1"; - case SSLVersion::TLS1_0: - return "TLS 1.0"; - case SSLVersion::SSL3: - return "SSL 3.0"; - case SSLVersion::TLS1_3_D28: - return "TLS 1.3 (draft 28)"; - case SSLVersion::TLS1_3_D27: - return "TLS 1.3 (draft 27)"; - case SSLVersion::TLS1_3_D26: - return "TLS 1.3 (draft 26)"; - case SSLVersion::TLS1_3_D25: - return "TLS 1.3 (draft 25)"; - case SSLVersion::TLS1_3_D24: - return "TLS 1.3 (draft 24)"; - case SSLVersion::TLS1_3_D23: - return "TLS 1.3 (draft 23)"; - case SSLVersion::TLS1_3_D22: - return "TLS 1.3 (draft 22)"; - case SSLVersion::TLS1_3_D21: - return "TLS 1.3 (draft 21)"; - case SSLVersion::TLS1_3_D20: - return "TLS 1.3 (draft 20)"; - case SSLVersion::TLS1_3_D19: - return "TLS 1.3 (draft 19)"; - case SSLVersion::TLS1_3_D18: - return "TLS 1.3 (draft 18)"; - case SSLVersion::TLS1_3_D17: - return "TLS 1.3 (draft 17)"; - case SSLVersion::TLS1_3_D16: - return "TLS 1.3 (draft 16)"; - case SSLVersion::TLS1_3_D15: - return "TLS 1.3 (draft 15)"; - case SSLVersion::TLS1_3_D14: - return "TLS 1.3 (draft 14)"; - case SSLVersion::TLS1_3_FBD23: - return "TLS 1.3 (Facebook draft 23)"; - case SSLVersion::TLS1_3_FBD26: - return "TLS 1.3 (Facebook draft 26)"; - case SSLVersion::Unknown: - return "Unknown"; - case SSLVersion::SSL2: - return "SSL 2.0"; - default: - return "Unknown"; - } + switch (enumValue) { + case SSLVersion::TLS1_3: + return "TLS 1.3"; + case SSLVersion::TLS1_2: + return "TLS 1.2"; + case SSLVersion::TLS1_1: + return "TLS 1.1"; + case SSLVersion::TLS1_0: + return "TLS 1.0"; + case SSLVersion::SSL3: + return "SSL 3.0"; + case SSLVersion::TLS1_3_D28: + return "TLS 1.3 (draft 28)"; + case SSLVersion::TLS1_3_D27: + return "TLS 1.3 (draft 27)"; + case SSLVersion::TLS1_3_D26: + return "TLS 1.3 (draft 26)"; + case SSLVersion::TLS1_3_D25: + return "TLS 1.3 (draft 25)"; + case SSLVersion::TLS1_3_D24: + return "TLS 1.3 (draft 24)"; + case SSLVersion::TLS1_3_D23: + return "TLS 1.3 (draft 23)"; + case SSLVersion::TLS1_3_D22: + return "TLS 1.3 (draft 22)"; + case SSLVersion::TLS1_3_D21: + return "TLS 1.3 (draft 21)"; + case SSLVersion::TLS1_3_D20: + return "TLS 1.3 (draft 20)"; + case SSLVersion::TLS1_3_D19: + return "TLS 1.3 (draft 19)"; + case SSLVersion::TLS1_3_D18: + return "TLS 1.3 (draft 18)"; + case SSLVersion::TLS1_3_D17: + return "TLS 1.3 (draft 17)"; + case SSLVersion::TLS1_3_D16: + return "TLS 1.3 (draft 16)"; + case SSLVersion::TLS1_3_D15: + return "TLS 1.3 (draft 15)"; + case SSLVersion::TLS1_3_D14: + return "TLS 1.3 (draft 14)"; + case SSLVersion::TLS1_3_FBD23: + return "TLS 1.3 (Facebook draft 23)"; + case SSLVersion::TLS1_3_FBD26: + return "TLS 1.3 (Facebook draft 26)"; + case SSLVersion::Unknown: + return "Unknown"; + case SSLVersion::SSL2: + return "SSL 2.0"; + default: + return "Unknown"; + } } - } // namespace pcpp diff --git a/Packet++/src/SSLHandshake.cpp b/Packet++/src/SSLHandshake.cpp index 39601eecea..6857f5cedd 100644 --- a/Packet++/src/SSLHandshake.cpp +++ b/Packet++/src/SSLHandshake.cpp @@ -1,2142 +1,2717 @@ #define LOG_MODULE PacketLogModuleSSLLayer +#include "SSLHandshake.h" #include "EndianPortable.h" +#include "Logger.h" #include "md5.h" -#include -#include #include #include +#include +#include #include -#include "Logger.h" -#include "SSLHandshake.h" - -namespace pcpp -{ +namespace pcpp { // -------------- // SSLCipherSuite // -------------- -static const SSLCipherSuite Cipher1 = SSLCipherSuite(0x0000, SSL_KEYX_NULL, SSL_AUTH_NULL, SSL_SYM_NULL, SSL_HASH_NULL, "TLS_NULL_WITH_NULL_NULL"); -static const SSLCipherSuite Cipher2 = SSLCipherSuite(0x0001, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_NULL, SSL_HASH_MD5, "TLS_RSA_WITH_NULL_MD5"); -static const SSLCipherSuite Cipher3 = SSLCipherSuite(0x0002, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_NULL, SSL_HASH_SHA, "TLS_RSA_WITH_NULL_SHA"); -static const SSLCipherSuite Cipher4 = SSLCipherSuite(0x0003, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_RC4_40, SSL_HASH_MD5, "TLS_RSA_EXPORT_WITH_RC4_40_MD5"); -static const SSLCipherSuite Cipher5 = SSLCipherSuite(0x0004, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_RC4_128, SSL_HASH_MD5, "TLS_RSA_WITH_RC4_128_MD5"); -static const SSLCipherSuite Cipher6 = SSLCipherSuite(0x0005, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_RC4_128, SSL_HASH_SHA, "TLS_RSA_WITH_RC4_128_SHA"); -static const SSLCipherSuite Cipher7 = SSLCipherSuite(0x0006, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_RC2_CBC_40, SSL_HASH_MD5, "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5"); -static const SSLCipherSuite Cipher8 = SSLCipherSuite(0x0007, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_IDEA_CBC, SSL_HASH_SHA, "TLS_RSA_WITH_IDEA_CBC_SHA"); -static const SSLCipherSuite Cipher9 = SSLCipherSuite(0x0008, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_DES40_CBC, SSL_HASH_SHA, "TLS_RSA_EXPORT_WITH_DES40_CBC_SHA"); -static const SSLCipherSuite Cipher10 = SSLCipherSuite(0x0009, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_DES_CBC, SSL_HASH_SHA, "TLS_RSA_WITH_DES_CBC_SHA"); -static const SSLCipherSuite Cipher11 = SSLCipherSuite(0x000A, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_3DES_EDE_CBC, SSL_HASH_SHA, "TLS_RSA_WITH_3DES_EDE_CBC_SHA"); -static const SSLCipherSuite Cipher12 = SSLCipherSuite(0x000B, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_DES40_CBC, SSL_HASH_SHA, "TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA"); -static const SSLCipherSuite Cipher13 = SSLCipherSuite(0x000C, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_DES_CBC, SSL_HASH_SHA, "TLS_DH_DSS_WITH_DES_CBC_SHA"); -static const SSLCipherSuite Cipher14 = SSLCipherSuite(0x000D, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_3DES_EDE_CBC, SSL_HASH_SHA, "TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA"); -static const SSLCipherSuite Cipher15 = SSLCipherSuite(0x000E, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_DES40_CBC, SSL_HASH_SHA, "TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA"); -static const SSLCipherSuite Cipher16 = SSLCipherSuite(0x000F, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_DES_CBC, SSL_HASH_SHA, "TLS_DH_RSA_WITH_DES_CBC_SHA"); -static const SSLCipherSuite Cipher17 = SSLCipherSuite(0x0010, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_3DES_EDE_CBC, SSL_HASH_SHA, "TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA"); -static const SSLCipherSuite Cipher18 = SSLCipherSuite(0x0011, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_DES40_CBC, SSL_HASH_SHA, "TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA"); -static const SSLCipherSuite Cipher19 = SSLCipherSuite(0x0012, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_DES_CBC, SSL_HASH_SHA, "TLS_DHE_DSS_WITH_DES_CBC_SHA"); -static const SSLCipherSuite Cipher20 = SSLCipherSuite(0x0013, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_3DES_EDE_CBC, SSL_HASH_SHA, "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA"); -static const SSLCipherSuite Cipher21 = SSLCipherSuite(0x0014, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_DES40_CBC, SSL_HASH_SHA, "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA"); -static const SSLCipherSuite Cipher22 = SSLCipherSuite(0x0015, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_DES_CBC, SSL_HASH_SHA, "TLS_DHE_RSA_WITH_DES_CBC_SHA"); -static const SSLCipherSuite Cipher23 = SSLCipherSuite(0x0016, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_3DES_EDE_CBC, SSL_HASH_SHA, "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA"); -static const SSLCipherSuite Cipher24 = SSLCipherSuite(0x0017, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_RC4_40, SSL_HASH_MD5, "TLS_DH_anon_EXPORT_WITH_RC4_40_MD5"); -static const SSLCipherSuite Cipher25 = SSLCipherSuite(0x0018, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_RC4_128, SSL_HASH_MD5, "TLS_DH_anon_WITH_RC4_128_MD5"); -static const SSLCipherSuite Cipher26 = SSLCipherSuite(0x0019, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_DES40_CBC, SSL_HASH_SHA, "TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA"); -static const SSLCipherSuite Cipher27 = SSLCipherSuite(0x001A, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_DES_CBC, SSL_HASH_SHA, "TLS_DH_anon_WITH_DES_CBC_SHA"); -static const SSLCipherSuite Cipher28 = SSLCipherSuite(0x001B, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_3DES_EDE_CBC, SSL_HASH_SHA, "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA"); -static const SSLCipherSuite Cipher29 = SSLCipherSuite(0x001E, SSL_KEYX_KRB5, SSL_AUTH_KRB5, SSL_SYM_DES_CBC, SSL_HASH_SHA, "TLS_KRB5_WITH_DES_CBC_SHA"); -static const SSLCipherSuite Cipher30 = SSLCipherSuite(0x001F, SSL_KEYX_KRB5, SSL_AUTH_KRB5, SSL_SYM_3DES_EDE_CBC, SSL_HASH_SHA, "TLS_KRB5_WITH_3DES_EDE_CBC_SHA"); -static const SSLCipherSuite Cipher31 = SSLCipherSuite(0x0020, SSL_KEYX_KRB5, SSL_AUTH_KRB5, SSL_SYM_RC4_128, SSL_HASH_SHA, "TLS_KRB5_WITH_RC4_128_SHA"); -static const SSLCipherSuite Cipher32 = SSLCipherSuite(0x0021, SSL_KEYX_KRB5, SSL_AUTH_KRB5, SSL_SYM_IDEA_CBC, SSL_HASH_SHA, "TLS_KRB5_WITH_IDEA_CBC_SHA"); -static const SSLCipherSuite Cipher33 = SSLCipherSuite(0x0022, SSL_KEYX_KRB5, SSL_AUTH_KRB5, SSL_SYM_DES_CBC, SSL_HASH_MD5, "TLS_KRB5_WITH_DES_CBC_MD5"); -static const SSLCipherSuite Cipher34 = SSLCipherSuite(0x0023, SSL_KEYX_KRB5, SSL_AUTH_KRB5, SSL_SYM_3DES_EDE_CBC, SSL_HASH_MD5, "TLS_KRB5_WITH_3DES_EDE_CBC_MD5"); -static const SSLCipherSuite Cipher35 = SSLCipherSuite(0x0024, SSL_KEYX_KRB5, SSL_AUTH_KRB5, SSL_SYM_RC4_128, SSL_HASH_MD5, "TLS_KRB5_WITH_RC4_128_MD5"); -static const SSLCipherSuite Cipher36 = SSLCipherSuite(0x0025, SSL_KEYX_KRB5, SSL_AUTH_KRB5, SSL_SYM_IDEA_CBC, SSL_HASH_MD5, "TLS_KRB5_WITH_IDEA_CBC_MD5"); -static const SSLCipherSuite Cipher37 = SSLCipherSuite(0x0026, SSL_KEYX_KRB5, SSL_AUTH_KRB5, SSL_SYM_DES_CBC_40, SSL_HASH_SHA, "TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA"); -static const SSLCipherSuite Cipher38 = SSLCipherSuite(0x0027, SSL_KEYX_KRB5, SSL_AUTH_KRB5, SSL_SYM_RC2_CBC_40, SSL_HASH_SHA, "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA"); -static const SSLCipherSuite Cipher39 = SSLCipherSuite(0x0028, SSL_KEYX_KRB5, SSL_AUTH_KRB5, SSL_SYM_RC4_40, SSL_HASH_SHA, "TLS_KRB5_EXPORT_WITH_RC4_40_SHA"); -static const SSLCipherSuite Cipher40 = SSLCipherSuite(0x0029, SSL_KEYX_KRB5, SSL_AUTH_KRB5, SSL_SYM_DES_CBC_40, SSL_HASH_MD5, "TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5"); -static const SSLCipherSuite Cipher41 = SSLCipherSuite(0x002A, SSL_KEYX_KRB5, SSL_AUTH_KRB5, SSL_SYM_RC2_CBC_40, SSL_HASH_MD5, "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5"); -static const SSLCipherSuite Cipher42 = SSLCipherSuite(0x002B, SSL_KEYX_KRB5, SSL_AUTH_KRB5, SSL_SYM_RC4_40, SSL_HASH_MD5, "TLS_KRB5_EXPORT_WITH_RC4_40_MD5"); -static const SSLCipherSuite Cipher43 = SSLCipherSuite(0x002C, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_NULL, SSL_HASH_SHA, "TLS_PSK_WITH_NULL_SHA"); -static const SSLCipherSuite Cipher44 = SSLCipherSuite(0x002D, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_NULL, SSL_HASH_SHA, "TLS_DHE_PSK_WITH_NULL_SHA"); -static const SSLCipherSuite Cipher45 = SSLCipherSuite(0x002E, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_NULL, SSL_HASH_SHA, "TLS_RSA_PSK_WITH_NULL_SHA"); -static const SSLCipherSuite Cipher46 = SSLCipherSuite(0x002F, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_AES_128_CBC, SSL_HASH_SHA, "TLS_RSA_WITH_AES_128_CBC_SHA"); -static const SSLCipherSuite Cipher47 = SSLCipherSuite(0x0030, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_AES_128_CBC, SSL_HASH_SHA, "TLS_DH_DSS_WITH_AES_128_CBC_SHA"); -static const SSLCipherSuite Cipher48 = SSLCipherSuite(0x0031, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_AES_128_CBC, SSL_HASH_SHA, "TLS_DH_RSA_WITH_AES_128_CBC_SHA"); -static const SSLCipherSuite Cipher49 = SSLCipherSuite(0x0032, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_AES_128_CBC, SSL_HASH_SHA, "TLS_DHE_DSS_WITH_AES_128_CBC_SHA"); -static const SSLCipherSuite Cipher50 = SSLCipherSuite(0x0033, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_AES_128_CBC, SSL_HASH_SHA, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA"); -static const SSLCipherSuite Cipher51 = SSLCipherSuite(0x0034, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_AES_128_CBC, SSL_HASH_SHA, "TLS_DH_anon_WITH_AES_128_CBC_SHA"); -static const SSLCipherSuite Cipher52 = SSLCipherSuite(0x0035, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_AES_256_CBC, SSL_HASH_SHA, "TLS_RSA_WITH_AES_256_CBC_SHA"); -static const SSLCipherSuite Cipher53 = SSLCipherSuite(0x0036, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_AES_256_CBC, SSL_HASH_SHA, "TLS_DH_DSS_WITH_AES_256_CBC_SHA"); -static const SSLCipherSuite Cipher54 = SSLCipherSuite(0x0037, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_AES_256_CBC, SSL_HASH_SHA, "TLS_DH_RSA_WITH_AES_256_CBC_SHA"); -static const SSLCipherSuite Cipher55 = SSLCipherSuite(0x0038, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_AES_256_CBC, SSL_HASH_SHA, "TLS_DHE_DSS_WITH_AES_256_CBC_SHA"); -static const SSLCipherSuite Cipher56 = SSLCipherSuite(0x0039, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_AES_256_CBC, SSL_HASH_SHA, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA"); -static const SSLCipherSuite Cipher57 = SSLCipherSuite(0x003A, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_AES_256_CBC, SSL_HASH_SHA, "TLS_DH_anon_WITH_AES_256_CBC_SHA"); -static const SSLCipherSuite Cipher58 = SSLCipherSuite(0x003B, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_NULL, SSL_HASH_SHA256, "TLS_RSA_WITH_NULL_SHA256"); -static const SSLCipherSuite Cipher59 = SSLCipherSuite(0x003C, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_AES_128_CBC, SSL_HASH_SHA256, "TLS_RSA_WITH_AES_128_CBC_SHA256"); -static const SSLCipherSuite Cipher60 = SSLCipherSuite(0x003D, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_AES_256_CBC, SSL_HASH_SHA256, "TLS_RSA_WITH_AES_256_CBC_SHA256"); -static const SSLCipherSuite Cipher61 = SSLCipherSuite(0x003E, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_AES_128_CBC, SSL_HASH_SHA256, "TLS_DH_DSS_WITH_AES_128_CBC_SHA256"); -static const SSLCipherSuite Cipher62 = SSLCipherSuite(0x003F, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_AES_128_CBC, SSL_HASH_SHA256, "TLS_DH_RSA_WITH_AES_128_CBC_SHA256"); -static const SSLCipherSuite Cipher63 = SSLCipherSuite(0x0040, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_AES_128_CBC, SSL_HASH_SHA256, "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256"); -static const SSLCipherSuite Cipher64 = SSLCipherSuite(0x0041, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_128_CBC, SSL_HASH_SHA, "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA"); -static const SSLCipherSuite Cipher65 = SSLCipherSuite(0x0042, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_CAMELLIA_128_CBC, SSL_HASH_SHA, "TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA"); -static const SSLCipherSuite Cipher66 = SSLCipherSuite(0x0043, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_128_CBC, SSL_HASH_SHA, "TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA"); -static const SSLCipherSuite Cipher67 = SSLCipherSuite(0x0044, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_CAMELLIA_128_CBC, SSL_HASH_SHA, "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA"); -static const SSLCipherSuite Cipher68 = SSLCipherSuite(0x0045, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_128_CBC, SSL_HASH_SHA, "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA"); -static const SSLCipherSuite Cipher69 = SSLCipherSuite(0x0046, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_CAMELLIA_128_CBC, SSL_HASH_SHA, "TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA"); -static const SSLCipherSuite Cipher70 = SSLCipherSuite(0x0067, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_AES_128_CBC, SSL_HASH_SHA256, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256"); -static const SSLCipherSuite Cipher71 = SSLCipherSuite(0x0068, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_AES_256_CBC, SSL_HASH_SHA256, "TLS_DH_DSS_WITH_AES_256_CBC_SHA256"); -static const SSLCipherSuite Cipher72 = SSLCipherSuite(0x0069, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_AES_256_CBC, SSL_HASH_SHA256, "TLS_DH_RSA_WITH_AES_256_CBC_SHA256"); -static const SSLCipherSuite Cipher73 = SSLCipherSuite(0x006A, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_AES_256_CBC, SSL_HASH_SHA256, "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256"); -static const SSLCipherSuite Cipher74 = SSLCipherSuite(0x006B, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_AES_256_CBC, SSL_HASH_SHA256, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256"); -static const SSLCipherSuite Cipher75 = SSLCipherSuite(0x006C, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_AES_128_CBC, SSL_HASH_SHA256, "TLS_DH_anon_WITH_AES_128_CBC_SHA256"); -static const SSLCipherSuite Cipher76 = SSLCipherSuite(0x006D, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_AES_256_CBC, SSL_HASH_SHA256, "TLS_DH_anon_WITH_AES_256_CBC_SHA256"); -static const SSLCipherSuite Cipher77 = SSLCipherSuite(0x0084, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_256_CBC, SSL_HASH_SHA, "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA"); -static const SSLCipherSuite Cipher78 = SSLCipherSuite(0x0085, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_CAMELLIA_256_CBC, SSL_HASH_SHA, "TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA"); -static const SSLCipherSuite Cipher79 = SSLCipherSuite(0x0086, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_256_CBC, SSL_HASH_SHA, "TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA"); -static const SSLCipherSuite Cipher80 = SSLCipherSuite(0x0087, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_CAMELLIA_256_CBC, SSL_HASH_SHA, "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA"); -static const SSLCipherSuite Cipher81 = SSLCipherSuite(0x0088, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_256_CBC, SSL_HASH_SHA, "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA"); -static const SSLCipherSuite Cipher82 = SSLCipherSuite(0x0089, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_CAMELLIA_256_CBC, SSL_HASH_SHA, "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA"); -static const SSLCipherSuite Cipher83 = SSLCipherSuite(0x008A, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_RC4_128, SSL_HASH_SHA, "TLS_PSK_WITH_RC4_128_SHA"); -static const SSLCipherSuite Cipher84 = SSLCipherSuite(0x008B, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_3DES_EDE_CBC, SSL_HASH_SHA, "TLS_PSK_WITH_3DES_EDE_CBC_SHA"); -static const SSLCipherSuite Cipher85 = SSLCipherSuite(0x008C, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_AES_128_CBC, SSL_HASH_SHA, "TLS_PSK_WITH_AES_128_CBC_SHA"); -static const SSLCipherSuite Cipher86 = SSLCipherSuite(0x008D, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_AES_256_CBC, SSL_HASH_SHA, "TLS_PSK_WITH_AES_256_CBC_SHA"); -static const SSLCipherSuite Cipher87 = SSLCipherSuite(0x008E, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_RC4_128, SSL_HASH_SHA, "TLS_DHE_PSK_WITH_RC4_128_SHA"); -static const SSLCipherSuite Cipher88 = SSLCipherSuite(0x008F, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_3DES_EDE_CBC, SSL_HASH_SHA, "TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA"); -static const SSLCipherSuite Cipher89 = SSLCipherSuite(0x0090, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_AES_128_CBC, SSL_HASH_SHA, "TLS_DHE_PSK_WITH_AES_128_CBC_SHA"); -static const SSLCipherSuite Cipher90 = SSLCipherSuite(0x0091, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_AES_256_CBC, SSL_HASH_SHA, "TLS_DHE_PSK_WITH_AES_256_CBC_SHA"); -static const SSLCipherSuite Cipher91 = SSLCipherSuite(0x0092, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_RC4_128, SSL_HASH_SHA, "TLS_RSA_PSK_WITH_RC4_128_SHA"); -static const SSLCipherSuite Cipher92 = SSLCipherSuite(0x0093, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_3DES_EDE_CBC, SSL_HASH_SHA, "TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA"); -static const SSLCipherSuite Cipher93 = SSLCipherSuite(0x0094, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_AES_128_CBC, SSL_HASH_SHA, "TLS_RSA_PSK_WITH_AES_128_CBC_SHA"); -static const SSLCipherSuite Cipher94 = SSLCipherSuite(0x0095, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_AES_256_CBC, SSL_HASH_SHA, "TLS_RSA_PSK_WITH_AES_256_CBC_SHA"); -static const SSLCipherSuite Cipher95 = SSLCipherSuite(0x0096, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_SEED_CBC, SSL_HASH_SHA, "TLS_RSA_WITH_SEED_CBC_SHA"); -static const SSLCipherSuite Cipher96 = SSLCipherSuite(0x0097, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_SEED_CBC, SSL_HASH_SHA, "TLS_DH_DSS_WITH_SEED_CBC_SHA"); -static const SSLCipherSuite Cipher97 = SSLCipherSuite(0x0098, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_SEED_CBC, SSL_HASH_SHA, "TLS_DH_RSA_WITH_SEED_CBC_SHA"); -static const SSLCipherSuite Cipher98 = SSLCipherSuite(0x0099, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_SEED_CBC, SSL_HASH_SHA, "TLS_DHE_DSS_WITH_SEED_CBC_SHA"); -static const SSLCipherSuite Cipher99 = SSLCipherSuite(0x009A, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_SEED_CBC, SSL_HASH_SHA, "TLS_DHE_RSA_WITH_SEED_CBC_SHA"); -static const SSLCipherSuite Cipher100 = SSLCipherSuite(0x009B, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_SEED_CBC, SSL_HASH_SHA, "TLS_DH_anon_WITH_SEED_CBC_SHA"); -static const SSLCipherSuite Cipher101 = SSLCipherSuite(0x009C, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_AES_128_GCM, SSL_HASH_SHA256, "TLS_RSA_WITH_AES_128_GCM_SHA256"); -static const SSLCipherSuite Cipher102 = SSLCipherSuite(0x009D, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_AES_256_GCM, SSL_HASH_SHA384, "TLS_RSA_WITH_AES_256_GCM_SHA384"); -static const SSLCipherSuite Cipher103 = SSLCipherSuite(0x009E, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_AES_128_GCM, SSL_HASH_SHA256, "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256"); -static const SSLCipherSuite Cipher104 = SSLCipherSuite(0x009F, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_AES_256_GCM, SSL_HASH_SHA384, "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384"); -static const SSLCipherSuite Cipher105 = SSLCipherSuite(0x00A0, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_AES_128_GCM, SSL_HASH_SHA256, "TLS_DH_RSA_WITH_AES_128_GCM_SHA256"); -static const SSLCipherSuite Cipher106 = SSLCipherSuite(0x00A1, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_AES_256_GCM, SSL_HASH_SHA384, "TLS_DH_RSA_WITH_AES_256_GCM_SHA384"); -static const SSLCipherSuite Cipher107 = SSLCipherSuite(0x00A2, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_AES_128_GCM, SSL_HASH_SHA256, "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256"); -static const SSLCipherSuite Cipher108 = SSLCipherSuite(0x00A3, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_AES_256_GCM, SSL_HASH_SHA384, "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384"); -static const SSLCipherSuite Cipher109 = SSLCipherSuite(0x00A4, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_AES_128_GCM, SSL_HASH_SHA256, "TLS_DH_DSS_WITH_AES_128_GCM_SHA256"); -static const SSLCipherSuite Cipher110 = SSLCipherSuite(0x00A5, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_AES_256_GCM, SSL_HASH_SHA384, "TLS_DH_DSS_WITH_AES_256_GCM_SHA384"); -static const SSLCipherSuite Cipher111 = SSLCipherSuite(0x00A6, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_AES_128_GCM, SSL_HASH_SHA256, "TLS_DH_anon_WITH_AES_128_GCM_SHA256"); -static const SSLCipherSuite Cipher112 = SSLCipherSuite(0x00A7, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_AES_256_GCM, SSL_HASH_SHA384, "TLS_DH_anon_WITH_AES_256_GCM_SHA384"); -static const SSLCipherSuite Cipher113 = SSLCipherSuite(0x00A8, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_AES_128_GCM, SSL_HASH_SHA256, "TLS_PSK_WITH_AES_128_GCM_SHA256"); -static const SSLCipherSuite Cipher114 = SSLCipherSuite(0x00A9, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_AES_256_GCM, SSL_HASH_SHA384, "TLS_PSK_WITH_AES_256_GCM_SHA384"); -static const SSLCipherSuite Cipher115 = SSLCipherSuite(0x00AA, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_AES_128_GCM, SSL_HASH_SHA256, "TLS_DHE_PSK_WITH_AES_128_GCM_SHA256"); -static const SSLCipherSuite Cipher116 = SSLCipherSuite(0x00AB, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_AES_256_GCM, SSL_HASH_SHA384, "TLS_DHE_PSK_WITH_AES_256_GCM_SHA384"); -static const SSLCipherSuite Cipher117 = SSLCipherSuite(0x00AC, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_AES_128_GCM, SSL_HASH_SHA256, "TLS_RSA_PSK_WITH_AES_128_GCM_SHA256"); -static const SSLCipherSuite Cipher118 = SSLCipherSuite(0x00AD, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_AES_256_GCM, SSL_HASH_SHA384, "TLS_RSA_PSK_WITH_AES_256_GCM_SHA384"); -static const SSLCipherSuite Cipher119 = SSLCipherSuite(0x00AE, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_AES_128_CBC, SSL_HASH_SHA256, "TLS_PSK_WITH_AES_128_CBC_SHA256"); -static const SSLCipherSuite Cipher120 = SSLCipherSuite(0x00AF, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_AES_256_CBC, SSL_HASH_SHA384, "TLS_PSK_WITH_AES_256_CBC_SHA384"); -static const SSLCipherSuite Cipher121 = SSLCipherSuite(0x00B0, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_NULL, SSL_HASH_SHA256, "TLS_PSK_WITH_NULL_SHA256"); -static const SSLCipherSuite Cipher122 = SSLCipherSuite(0x00B1, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_NULL, SSL_HASH_SHA384, "TLS_PSK_WITH_NULL_SHA384"); -static const SSLCipherSuite Cipher123 = SSLCipherSuite(0x00B2, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_AES_128_CBC, SSL_HASH_SHA256, "TLS_DHE_PSK_WITH_AES_128_CBC_SHA256"); -static const SSLCipherSuite Cipher124 = SSLCipherSuite(0x00B3, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_AES_256_CBC, SSL_HASH_SHA384, "TLS_DHE_PSK_WITH_AES_256_CBC_SHA384"); -static const SSLCipherSuite Cipher125 = SSLCipherSuite(0x00B4, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_NULL, SSL_HASH_SHA256, "TLS_DHE_PSK_WITH_NULL_SHA256"); -static const SSLCipherSuite Cipher126 = SSLCipherSuite(0x00B5, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_NULL, SSL_HASH_SHA384, "TLS_DHE_PSK_WITH_NULL_SHA384"); -static const SSLCipherSuite Cipher127 = SSLCipherSuite(0x00B6, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_AES_128_CBC, SSL_HASH_SHA256, "TLS_RSA_PSK_WITH_AES_128_CBC_SHA256"); -static const SSLCipherSuite Cipher128 = SSLCipherSuite(0x00B7, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_AES_256_CBC, SSL_HASH_SHA384, "TLS_RSA_PSK_WITH_AES_256_CBC_SHA384"); -static const SSLCipherSuite Cipher129 = SSLCipherSuite(0x00B8, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_NULL, SSL_HASH_SHA256, "TLS_RSA_PSK_WITH_NULL_SHA256"); -static const SSLCipherSuite Cipher130 = SSLCipherSuite(0x00B9, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_NULL, SSL_HASH_SHA384, "TLS_RSA_PSK_WITH_NULL_SHA384"); -static const SSLCipherSuite Cipher131 = SSLCipherSuite(0x00BA, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_128_CBC, SSL_HASH_SHA256, "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256"); -static const SSLCipherSuite Cipher132 = SSLCipherSuite(0x00BB, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_CAMELLIA_128_CBC, SSL_HASH_SHA256, "TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256"); -static const SSLCipherSuite Cipher133 = SSLCipherSuite(0x00BC, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_128_CBC, SSL_HASH_SHA256, "TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256"); -static const SSLCipherSuite Cipher134 = SSLCipherSuite(0x00BD, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_CAMELLIA_128_CBC, SSL_HASH_SHA256, "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256"); -static const SSLCipherSuite Cipher135 = SSLCipherSuite(0x00BE, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_128_CBC, SSL_HASH_SHA256, "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256"); -static const SSLCipherSuite Cipher136 = SSLCipherSuite(0x00BF, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_CAMELLIA_128_CBC, SSL_HASH_SHA256, "TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256"); -static const SSLCipherSuite Cipher137 = SSLCipherSuite(0x00C0, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_256_CBC, SSL_HASH_SHA256, "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256"); -static const SSLCipherSuite Cipher138 = SSLCipherSuite(0x00C1, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_CAMELLIA_256_CBC, SSL_HASH_SHA256, "TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256"); -static const SSLCipherSuite Cipher139 = SSLCipherSuite(0x00C2, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_256_CBC, SSL_HASH_SHA256, "TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256"); -static const SSLCipherSuite Cipher140 = SSLCipherSuite(0x00C3, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_CAMELLIA_256_CBC, SSL_HASH_SHA256, "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256"); -static const SSLCipherSuite Cipher141 = SSLCipherSuite(0x00C4, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_256_CBC, SSL_HASH_SHA256, "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256"); -static const SSLCipherSuite Cipher142 = SSLCipherSuite(0x00C5, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_CAMELLIA_256_CBC, SSL_HASH_SHA256, "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256"); -static const SSLCipherSuite Cipher143 = SSLCipherSuite(0xC001, SSL_KEYX_ECDH, SSL_AUTH_ECDSA, SSL_SYM_NULL, SSL_HASH_SHA, "TLS_ECDH_ECDSA_WITH_NULL_SHA"); -static const SSLCipherSuite Cipher144 = SSLCipherSuite(0xC002, SSL_KEYX_ECDH, SSL_AUTH_ECDSA, SSL_SYM_RC4_128, SSL_HASH_SHA, "TLS_ECDH_ECDSA_WITH_RC4_128_SHA"); -static const SSLCipherSuite Cipher145 = SSLCipherSuite(0xC003, SSL_KEYX_ECDH, SSL_AUTH_ECDSA, SSL_SYM_3DES_EDE_CBC, SSL_HASH_SHA, "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA"); -static const SSLCipherSuite Cipher146 = SSLCipherSuite(0xC004, SSL_KEYX_ECDH, SSL_AUTH_ECDSA, SSL_SYM_AES_128_CBC, SSL_HASH_SHA, "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA"); -static const SSLCipherSuite Cipher147 = SSLCipherSuite(0xC005, SSL_KEYX_ECDH, SSL_AUTH_ECDSA, SSL_SYM_AES_256_CBC, SSL_HASH_SHA, "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA"); -static const SSLCipherSuite Cipher148 = SSLCipherSuite(0xC006, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_NULL, SSL_HASH_SHA, "TLS_ECDHE_ECDSA_WITH_NULL_SHA"); -static const SSLCipherSuite Cipher149 = SSLCipherSuite(0xC007, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_RC4_128, SSL_HASH_SHA, "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA"); -static const SSLCipherSuite Cipher150 = SSLCipherSuite(0xC008, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_3DES_EDE_CBC, SSL_HASH_SHA, "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA"); -static const SSLCipherSuite Cipher151 = SSLCipherSuite(0xC009, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_AES_128_CBC, SSL_HASH_SHA, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA"); -static const SSLCipherSuite Cipher152 = SSLCipherSuite(0xC00A, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_AES_256_CBC, SSL_HASH_SHA, "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA"); -static const SSLCipherSuite Cipher153 = SSLCipherSuite(0xC00B, SSL_KEYX_ECDH, SSL_AUTH_RSA, SSL_SYM_NULL, SSL_HASH_SHA, "TLS_ECDH_RSA_WITH_NULL_SHA"); -static const SSLCipherSuite Cipher154 = SSLCipherSuite(0xC00C, SSL_KEYX_ECDH, SSL_AUTH_RSA, SSL_SYM_RC4_128, SSL_HASH_SHA, "TLS_ECDH_RSA_WITH_RC4_128_SHA"); -static const SSLCipherSuite Cipher155 = SSLCipherSuite(0xC00D, SSL_KEYX_ECDH, SSL_AUTH_RSA, SSL_SYM_3DES_EDE_CBC, SSL_HASH_SHA, "TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA"); -static const SSLCipherSuite Cipher156 = SSLCipherSuite(0xC00E, SSL_KEYX_ECDH, SSL_AUTH_RSA, SSL_SYM_AES_128_CBC, SSL_HASH_SHA, "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA"); -static const SSLCipherSuite Cipher157 = SSLCipherSuite(0xC00F, SSL_KEYX_ECDH, SSL_AUTH_RSA, SSL_SYM_AES_256_CBC, SSL_HASH_SHA, "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA"); -static const SSLCipherSuite Cipher158 = SSLCipherSuite(0xC010, SSL_KEYX_ECDHE, SSL_AUTH_RSA, SSL_SYM_NULL, SSL_HASH_SHA, "TLS_ECDHE_RSA_WITH_NULL_SHA"); -static const SSLCipherSuite Cipher159 = SSLCipherSuite(0xC011, SSL_KEYX_ECDHE, SSL_AUTH_RSA, SSL_SYM_RC4_128, SSL_HASH_SHA, "TLS_ECDHE_RSA_WITH_RC4_128_SHA"); -static const SSLCipherSuite Cipher160 = SSLCipherSuite(0xC012, SSL_KEYX_ECDHE, SSL_AUTH_RSA, SSL_SYM_3DES_EDE_CBC, SSL_HASH_SHA, "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA"); -static const SSLCipherSuite Cipher161 = SSLCipherSuite(0xC013, SSL_KEYX_ECDHE, SSL_AUTH_RSA, SSL_SYM_AES_128_CBC, SSL_HASH_SHA, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"); -static const SSLCipherSuite Cipher162 = SSLCipherSuite(0xC014, SSL_KEYX_ECDHE, SSL_AUTH_RSA, SSL_SYM_AES_256_CBC, SSL_HASH_SHA, "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA"); -static const SSLCipherSuite Cipher163 = SSLCipherSuite(0xC015, SSL_KEYX_ECDH, SSL_AUTH_anon, SSL_SYM_NULL, SSL_HASH_SHA, "TLS_ECDH_anon_WITH_NULL_SHA"); -static const SSLCipherSuite Cipher164 = SSLCipherSuite(0xC016, SSL_KEYX_ECDH, SSL_AUTH_anon, SSL_SYM_RC4_128, SSL_HASH_SHA, "TLS_ECDH_anon_WITH_RC4_128_SHA"); -static const SSLCipherSuite Cipher165 = SSLCipherSuite(0xC017, SSL_KEYX_ECDH, SSL_AUTH_anon, SSL_SYM_3DES_EDE_CBC, SSL_HASH_SHA, "TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA"); -static const SSLCipherSuite Cipher166 = SSLCipherSuite(0xC018, SSL_KEYX_ECDH, SSL_AUTH_anon, SSL_SYM_AES_128_CBC, SSL_HASH_SHA, "TLS_ECDH_anon_WITH_AES_128_CBC_SHA"); -static const SSLCipherSuite Cipher167 = SSLCipherSuite(0xC019, SSL_KEYX_ECDH, SSL_AUTH_anon, SSL_SYM_AES_256_CBC, SSL_HASH_SHA, "TLS_ECDH_anon_WITH_AES_256_CBC_SHA"); -static const SSLCipherSuite Cipher168 = SSLCipherSuite(0xC01A, SSL_KEYX_SRP, SSL_AUTH_SHA, SSL_SYM_3DES_EDE_CBC, SSL_HASH_SHA, "TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA"); -static const SSLCipherSuite Cipher169 = SSLCipherSuite(0xC01B, SSL_KEYX_SRP, SSL_AUTH_RSA, SSL_SYM_3DES_EDE_CBC, SSL_HASH_SHA, "TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA"); -static const SSLCipherSuite Cipher170 = SSLCipherSuite(0xC01C, SSL_KEYX_SRP, SSL_AUTH_DSS, SSL_SYM_3DES_EDE_CBC, SSL_HASH_SHA, "TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA"); -static const SSLCipherSuite Cipher171 = SSLCipherSuite(0xC01D, SSL_KEYX_SRP, SSL_AUTH_SHA, SSL_SYM_AES_128_CBC, SSL_HASH_SHA, "TLS_SRP_SHA_WITH_AES_128_CBC_SHA"); -static const SSLCipherSuite Cipher172 = SSLCipherSuite(0xC01E, SSL_KEYX_SRP, SSL_AUTH_RSA, SSL_SYM_AES_128_CBC, SSL_HASH_SHA, "TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA"); -static const SSLCipherSuite Cipher173 = SSLCipherSuite(0xC01F, SSL_KEYX_SRP, SSL_AUTH_DSS, SSL_SYM_AES_128_CBC, SSL_HASH_SHA, "TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA"); -static const SSLCipherSuite Cipher174 = SSLCipherSuite(0xC020, SSL_KEYX_SRP, SSL_AUTH_SHA, SSL_SYM_AES_256_CBC, SSL_HASH_SHA, "TLS_SRP_SHA_WITH_AES_256_CBC_SHA"); -static const SSLCipherSuite Cipher175 = SSLCipherSuite(0xC021, SSL_KEYX_SRP, SSL_AUTH_RSA, SSL_SYM_AES_256_CBC, SSL_HASH_SHA, "TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA"); -static const SSLCipherSuite Cipher176 = SSLCipherSuite(0xC022, SSL_KEYX_SRP, SSL_AUTH_DSS, SSL_SYM_AES_256_CBC, SSL_HASH_SHA, "TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA"); -static const SSLCipherSuite Cipher177 = SSLCipherSuite(0xC023, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_AES_128_CBC, SSL_HASH_SHA256, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256"); -static const SSLCipherSuite Cipher178 = SSLCipherSuite(0xC024, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_AES_256_CBC, SSL_HASH_SHA384, "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384"); -static const SSLCipherSuite Cipher179 = SSLCipherSuite(0xC025, SSL_KEYX_ECDH, SSL_AUTH_ECDSA, SSL_SYM_AES_128_CBC, SSL_HASH_SHA256, "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256"); -static const SSLCipherSuite Cipher180 = SSLCipherSuite(0xC026, SSL_KEYX_ECDH, SSL_AUTH_ECDSA, SSL_SYM_AES_256_CBC, SSL_HASH_SHA384, "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384"); -static const SSLCipherSuite Cipher181 = SSLCipherSuite(0xC027, SSL_KEYX_ECDHE, SSL_AUTH_RSA, SSL_SYM_AES_128_CBC, SSL_HASH_SHA256, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"); -static const SSLCipherSuite Cipher182 = SSLCipherSuite(0xC028, SSL_KEYX_ECDHE, SSL_AUTH_RSA, SSL_SYM_AES_256_CBC, SSL_HASH_SHA384, "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384"); -static const SSLCipherSuite Cipher183 = SSLCipherSuite(0xC029, SSL_KEYX_ECDH, SSL_AUTH_RSA, SSL_SYM_AES_128_CBC, SSL_HASH_SHA256, "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256"); -static const SSLCipherSuite Cipher184 = SSLCipherSuite(0xC02A, SSL_KEYX_ECDH, SSL_AUTH_RSA, SSL_SYM_AES_256_CBC, SSL_HASH_SHA384, "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384"); -static const SSLCipherSuite Cipher185 = SSLCipherSuite(0xC02B, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_AES_128_GCM, SSL_HASH_SHA256, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"); -static const SSLCipherSuite Cipher186 = SSLCipherSuite(0xC02C, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_AES_256_GCM, SSL_HASH_SHA384, "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"); -static const SSLCipherSuite Cipher187 = SSLCipherSuite(0xC02D, SSL_KEYX_ECDH, SSL_AUTH_ECDSA, SSL_SYM_AES_128_GCM, SSL_HASH_SHA256, "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256"); -static const SSLCipherSuite Cipher188 = SSLCipherSuite(0xC02E, SSL_KEYX_ECDH, SSL_AUTH_ECDSA, SSL_SYM_AES_256_GCM, SSL_HASH_SHA384, "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384"); -static const SSLCipherSuite Cipher189 = SSLCipherSuite(0xC02F, SSL_KEYX_ECDHE, SSL_AUTH_RSA, SSL_SYM_AES_128_GCM, SSL_HASH_SHA256, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"); -static const SSLCipherSuite Cipher190 = SSLCipherSuite(0xC030, SSL_KEYX_ECDHE, SSL_AUTH_RSA, SSL_SYM_AES_256_GCM, SSL_HASH_SHA384, "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"); -static const SSLCipherSuite Cipher191 = SSLCipherSuite(0xC031, SSL_KEYX_ECDH, SSL_AUTH_RSA, SSL_SYM_AES_128_GCM, SSL_HASH_SHA256, "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256"); -static const SSLCipherSuite Cipher192 = SSLCipherSuite(0xC032, SSL_KEYX_ECDH, SSL_AUTH_RSA, SSL_SYM_AES_256_GCM, SSL_HASH_SHA384, "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384"); -static const SSLCipherSuite Cipher193 = SSLCipherSuite(0xC033, SSL_KEYX_ECDHE, SSL_AUTH_PSK, SSL_SYM_RC4_128, SSL_HASH_SHA, "TLS_ECDHE_PSK_WITH_RC4_128_SHA"); -static const SSLCipherSuite Cipher194 = SSLCipherSuite(0xC034, SSL_KEYX_ECDHE, SSL_AUTH_PSK, SSL_SYM_3DES_EDE_CBC, SSL_HASH_SHA, "TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA"); -static const SSLCipherSuite Cipher195 = SSLCipherSuite(0xC035, SSL_KEYX_ECDHE, SSL_AUTH_PSK, SSL_SYM_AES_128_CBC, SSL_HASH_SHA, "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA"); -static const SSLCipherSuite Cipher196 = SSLCipherSuite(0xC036, SSL_KEYX_ECDHE, SSL_AUTH_PSK, SSL_SYM_AES_256_CBC, SSL_HASH_SHA, "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA"); -static const SSLCipherSuite Cipher197 = SSLCipherSuite(0xC037, SSL_KEYX_ECDHE, SSL_AUTH_PSK, SSL_SYM_AES_128_CBC, SSL_HASH_SHA256, "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256"); -static const SSLCipherSuite Cipher198 = SSLCipherSuite(0xC038, SSL_KEYX_ECDHE, SSL_AUTH_PSK, SSL_SYM_AES_256_CBC, SSL_HASH_SHA384, "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384"); -static const SSLCipherSuite Cipher199 = SSLCipherSuite(0xC039, SSL_KEYX_ECDHE, SSL_AUTH_PSK, SSL_SYM_NULL, SSL_HASH_SHA, "TLS_ECDHE_PSK_WITH_NULL_SHA"); -static const SSLCipherSuite Cipher200 = SSLCipherSuite(0xC03A, SSL_KEYX_ECDHE, SSL_AUTH_PSK, SSL_SYM_NULL, SSL_HASH_SHA256, "TLS_ECDHE_PSK_WITH_NULL_SHA256"); -static const SSLCipherSuite Cipher201 = SSLCipherSuite(0xC03B, SSL_KEYX_ECDHE, SSL_AUTH_PSK, SSL_SYM_NULL, SSL_HASH_SHA384, "TLS_ECDHE_PSK_WITH_NULL_SHA384"); -static const SSLCipherSuite Cipher202 = SSLCipherSuite(0xC03C, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_ARIA_128_CBC, SSL_HASH_SHA256, "TLS_RSA_WITH_ARIA_128_CBC_SHA256"); -static const SSLCipherSuite Cipher203 = SSLCipherSuite(0xC03D, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_ARIA_256_CBC, SSL_HASH_SHA384, "TLS_RSA_WITH_ARIA_256_CBC_SHA384"); -static const SSLCipherSuite Cipher204 = SSLCipherSuite(0xC03E, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_ARIA_128_CBC, SSL_HASH_SHA256, "TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256"); -static const SSLCipherSuite Cipher205 = SSLCipherSuite(0xC03F, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_ARIA_256_CBC, SSL_HASH_SHA384, "TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384"); -static const SSLCipherSuite Cipher206 = SSLCipherSuite(0xC040, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_ARIA_128_CBC, SSL_HASH_SHA256, "TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256"); -static const SSLCipherSuite Cipher207 = SSLCipherSuite(0xC041, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_ARIA_256_CBC, SSL_HASH_SHA384, "TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384"); -static const SSLCipherSuite Cipher208 = SSLCipherSuite(0xC042, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_ARIA_128_CBC, SSL_HASH_SHA256, "TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256"); -static const SSLCipherSuite Cipher209 = SSLCipherSuite(0xC043, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_ARIA_256_CBC, SSL_HASH_SHA384, "TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384"); -static const SSLCipherSuite Cipher210 = SSLCipherSuite(0xC044, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_ARIA_128_CBC, SSL_HASH_SHA256, "TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256"); -static const SSLCipherSuite Cipher211 = SSLCipherSuite(0xC045, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_ARIA_256_CBC, SSL_HASH_SHA384, "TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384"); -static const SSLCipherSuite Cipher212 = SSLCipherSuite(0xC046, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_ARIA_128_CBC, SSL_HASH_SHA256, "TLS_DH_anon_WITH_ARIA_128_CBC_SHA256"); -static const SSLCipherSuite Cipher213 = SSLCipherSuite(0xC047, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_ARIA_256_CBC, SSL_HASH_SHA384, "TLS_DH_anon_WITH_ARIA_256_CBC_SHA384"); -static const SSLCipherSuite Cipher214 = SSLCipherSuite(0xC048, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_ARIA_128_CBC, SSL_HASH_SHA256, "TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256"); -static const SSLCipherSuite Cipher215 = SSLCipherSuite(0xC049, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_ARIA_256_CBC, SSL_HASH_SHA384, "TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384"); -static const SSLCipherSuite Cipher216 = SSLCipherSuite(0xC04A, SSL_KEYX_ECDH, SSL_AUTH_ECDSA, SSL_SYM_ARIA_128_CBC, SSL_HASH_SHA256, "TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256"); -static const SSLCipherSuite Cipher217 = SSLCipherSuite(0xC04B, SSL_KEYX_ECDH, SSL_AUTH_ECDSA, SSL_SYM_ARIA_256_CBC, SSL_HASH_SHA384, "TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384"); -static const SSLCipherSuite Cipher218 = SSLCipherSuite(0xC04C, SSL_KEYX_ECDHE, SSL_AUTH_RSA, SSL_SYM_ARIA_128_CBC, SSL_HASH_SHA256, "TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256"); -static const SSLCipherSuite Cipher219 = SSLCipherSuite(0xC04D, SSL_KEYX_ECDHE, SSL_AUTH_RSA, SSL_SYM_ARIA_256_CBC, SSL_HASH_SHA384, "TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384"); -static const SSLCipherSuite Cipher220 = SSLCipherSuite(0xC04E, SSL_KEYX_ECDH, SSL_AUTH_RSA, SSL_SYM_ARIA_128_CBC, SSL_HASH_SHA256, "TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256"); -static const SSLCipherSuite Cipher221 = SSLCipherSuite(0xC04F, SSL_KEYX_ECDH, SSL_AUTH_RSA, SSL_SYM_ARIA_256_CBC, SSL_HASH_SHA384, "TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384"); -static const SSLCipherSuite Cipher222 = SSLCipherSuite(0xC050, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_ARIA_128_GCM, SSL_HASH_SHA256, "TLS_RSA_WITH_ARIA_128_GCM_SHA256"); -static const SSLCipherSuite Cipher223 = SSLCipherSuite(0xC051, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_ARIA_256_GCM, SSL_HASH_SHA384, "TLS_RSA_WITH_ARIA_256_GCM_SHA384"); -static const SSLCipherSuite Cipher224 = SSLCipherSuite(0xC052, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_ARIA_128_GCM, SSL_HASH_SHA256, "TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256"); -static const SSLCipherSuite Cipher225 = SSLCipherSuite(0xC053, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_ARIA_256_GCM, SSL_HASH_SHA384, "TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384"); -static const SSLCipherSuite Cipher226 = SSLCipherSuite(0xC054, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_ARIA_128_GCM, SSL_HASH_SHA256, "TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256"); -static const SSLCipherSuite Cipher227 = SSLCipherSuite(0xC055, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_ARIA_256_GCM, SSL_HASH_SHA384, "TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384"); -static const SSLCipherSuite Cipher228 = SSLCipherSuite(0xC056, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_ARIA_128_GCM, SSL_HASH_SHA256, "TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256"); -static const SSLCipherSuite Cipher229 = SSLCipherSuite(0xC057, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_ARIA_256_GCM, SSL_HASH_SHA384, "TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384"); -static const SSLCipherSuite Cipher230 = SSLCipherSuite(0xC058, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_ARIA_128_GCM, SSL_HASH_SHA256, "TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256"); -static const SSLCipherSuite Cipher231 = SSLCipherSuite(0xC059, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_ARIA_256_GCM, SSL_HASH_SHA384, "TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384"); -static const SSLCipherSuite Cipher232 = SSLCipherSuite(0xC05A, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_ARIA_128_GCM, SSL_HASH_SHA256, "TLS_DH_anon_WITH_ARIA_128_GCM_SHA256"); -static const SSLCipherSuite Cipher233 = SSLCipherSuite(0xC05B, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_ARIA_256_GCM, SSL_HASH_SHA384, "TLS_DH_anon_WITH_ARIA_256_GCM_SHA384"); -static const SSLCipherSuite Cipher234 = SSLCipherSuite(0xC05C, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_ARIA_128_GCM, SSL_HASH_SHA256, "TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256"); -static const SSLCipherSuite Cipher235 = SSLCipherSuite(0xC05D, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_ARIA_256_GCM, SSL_HASH_SHA384, "TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384"); -static const SSLCipherSuite Cipher236 = SSLCipherSuite(0xC05E, SSL_KEYX_ECDH, SSL_AUTH_ECDSA, SSL_SYM_ARIA_128_GCM, SSL_HASH_SHA256, "TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256"); -static const SSLCipherSuite Cipher237 = SSLCipherSuite(0xC05F, SSL_KEYX_ECDH, SSL_AUTH_ECDSA, SSL_SYM_ARIA_256_GCM, SSL_HASH_SHA384, "TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384"); -static const SSLCipherSuite Cipher238 = SSLCipherSuite(0xC060, SSL_KEYX_ECDHE, SSL_AUTH_RSA, SSL_SYM_ARIA_128_GCM, SSL_HASH_SHA256, "TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256"); -static const SSLCipherSuite Cipher239 = SSLCipherSuite(0xC061, SSL_KEYX_ECDHE, SSL_AUTH_RSA, SSL_SYM_ARIA_256_GCM, SSL_HASH_SHA384, "TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384"); -static const SSLCipherSuite Cipher240 = SSLCipherSuite(0xC062, SSL_KEYX_ECDH, SSL_AUTH_RSA, SSL_SYM_ARIA_128_GCM, SSL_HASH_SHA256, "TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256"); -static const SSLCipherSuite Cipher241 = SSLCipherSuite(0xC063, SSL_KEYX_ECDH, SSL_AUTH_RSA, SSL_SYM_ARIA_256_GCM, SSL_HASH_SHA384, "TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384"); -static const SSLCipherSuite Cipher242 = SSLCipherSuite(0xC064, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_ARIA_128_CBC, SSL_HASH_SHA256, "TLS_PSK_WITH_ARIA_128_CBC_SHA256"); -static const SSLCipherSuite Cipher243 = SSLCipherSuite(0xC065, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_ARIA_256_CBC, SSL_HASH_SHA384, "TLS_PSK_WITH_ARIA_256_CBC_SHA384"); -static const SSLCipherSuite Cipher244 = SSLCipherSuite(0xC066, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_ARIA_128_CBC, SSL_HASH_SHA256, "TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256"); -static const SSLCipherSuite Cipher245 = SSLCipherSuite(0xC067, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_ARIA_256_CBC, SSL_HASH_SHA384, "TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384"); -static const SSLCipherSuite Cipher246 = SSLCipherSuite(0xC068, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_ARIA_128_CBC, SSL_HASH_SHA256, "TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256"); -static const SSLCipherSuite Cipher247 = SSLCipherSuite(0xC069, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_ARIA_256_CBC, SSL_HASH_SHA384, "TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384"); -static const SSLCipherSuite Cipher248 = SSLCipherSuite(0xC06A, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_ARIA_128_GCM, SSL_HASH_SHA256, "TLS_PSK_WITH_ARIA_128_GCM_SHA256"); -static const SSLCipherSuite Cipher249 = SSLCipherSuite(0xC06B, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_ARIA_256_GCM, SSL_HASH_SHA384, "TLS_PSK_WITH_ARIA_256_GCM_SHA384"); -static const SSLCipherSuite Cipher250 = SSLCipherSuite(0xC06C, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_ARIA_128_GCM, SSL_HASH_SHA256, "TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256"); -static const SSLCipherSuite Cipher251 = SSLCipherSuite(0xC06D, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_ARIA_256_GCM, SSL_HASH_SHA384, "TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384"); -static const SSLCipherSuite Cipher252 = SSLCipherSuite(0xC06E, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_ARIA_128_GCM, SSL_HASH_SHA256, "TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256"); -static const SSLCipherSuite Cipher253 = SSLCipherSuite(0xC06F, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_ARIA_256_GCM, SSL_HASH_SHA384, "TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384"); -static const SSLCipherSuite Cipher254 = SSLCipherSuite(0xC070, SSL_KEYX_ECDHE, SSL_AUTH_PSK, SSL_SYM_ARIA_128_CBC, SSL_HASH_SHA256, "TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256"); -static const SSLCipherSuite Cipher255 = SSLCipherSuite(0xC071, SSL_KEYX_ECDHE, SSL_AUTH_PSK, SSL_SYM_ARIA_256_CBC, SSL_HASH_SHA384, "TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384"); -static const SSLCipherSuite Cipher256 = SSLCipherSuite(0xC072, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_CAMELLIA_128_CBC, SSL_HASH_SHA256, "TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256"); -static const SSLCipherSuite Cipher257 = SSLCipherSuite(0xC073, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_CAMELLIA_256_CBC, SSL_HASH_SHA384, "TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384"); -static const SSLCipherSuite Cipher258 = SSLCipherSuite(0xC074, SSL_KEYX_ECDH, SSL_AUTH_ECDSA, SSL_SYM_CAMELLIA_128_CBC, SSL_HASH_SHA256, "TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256"); -static const SSLCipherSuite Cipher259 = SSLCipherSuite(0xC075, SSL_KEYX_ECDH, SSL_AUTH_ECDSA, SSL_SYM_CAMELLIA_256_CBC, SSL_HASH_SHA384, "TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384"); -static const SSLCipherSuite Cipher260 = SSLCipherSuite(0xC076, SSL_KEYX_ECDHE, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_128_CBC, SSL_HASH_SHA256, "TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256"); -static const SSLCipherSuite Cipher261 = SSLCipherSuite(0xC077, SSL_KEYX_ECDHE, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_256_CBC, SSL_HASH_SHA384, "TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384"); -static const SSLCipherSuite Cipher262 = SSLCipherSuite(0xC078, SSL_KEYX_ECDH, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_128_CBC, SSL_HASH_SHA256, "TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256"); -static const SSLCipherSuite Cipher263 = SSLCipherSuite(0xC079, SSL_KEYX_ECDH, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_256_CBC, SSL_HASH_SHA384, "TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384"); -static const SSLCipherSuite Cipher264 = SSLCipherSuite(0xC07A, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_128_GCM, SSL_HASH_SHA256, "TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256"); -static const SSLCipherSuite Cipher265 = SSLCipherSuite(0xC07B, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_256_GCM, SSL_HASH_SHA384, "TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384"); -static const SSLCipherSuite Cipher266 = SSLCipherSuite(0xC07C, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_128_GCM, SSL_HASH_SHA256, "TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256"); -static const SSLCipherSuite Cipher267 = SSLCipherSuite(0xC07D, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_256_GCM, SSL_HASH_SHA384, "TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384"); -static const SSLCipherSuite Cipher268 = SSLCipherSuite(0xC07E, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_128_GCM, SSL_HASH_SHA256, "TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256"); -static const SSLCipherSuite Cipher269 = SSLCipherSuite(0xC07F, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_256_GCM, SSL_HASH_SHA384, "TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384"); -static const SSLCipherSuite Cipher270 = SSLCipherSuite(0xC080, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_CAMELLIA_128_GCM, SSL_HASH_SHA256, "TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256"); -static const SSLCipherSuite Cipher271 = SSLCipherSuite(0xC081, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_CAMELLIA_256_GCM, SSL_HASH_SHA384, "TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384"); -static const SSLCipherSuite Cipher272 = SSLCipherSuite(0xC082, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_CAMELLIA_128_GCM, SSL_HASH_SHA256, "TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256"); -static const SSLCipherSuite Cipher273 = SSLCipherSuite(0xC083, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_CAMELLIA_256_GCM, SSL_HASH_SHA384, "TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384"); -static const SSLCipherSuite Cipher274 = SSLCipherSuite(0xC084, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_CAMELLIA_128_GCM, SSL_HASH_SHA256, "TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256"); -static const SSLCipherSuite Cipher275 = SSLCipherSuite(0xC085, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_CAMELLIA_256_GCM, SSL_HASH_SHA384, "TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384"); -static const SSLCipherSuite Cipher276 = SSLCipherSuite(0xC086, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_CAMELLIA_128_GCM, SSL_HASH_SHA256, "TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256"); -static const SSLCipherSuite Cipher277 = SSLCipherSuite(0xC087, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_CAMELLIA_256_GCM, SSL_HASH_SHA384, "TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384"); -static const SSLCipherSuite Cipher278 = SSLCipherSuite(0xC088, SSL_KEYX_ECDH, SSL_AUTH_ECDSA, SSL_SYM_CAMELLIA_128_GCM, SSL_HASH_SHA256, "TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256"); -static const SSLCipherSuite Cipher279 = SSLCipherSuite(0xC089, SSL_KEYX_ECDH, SSL_AUTH_ECDSA, SSL_SYM_CAMELLIA_256_GCM, SSL_HASH_SHA384, "TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384"); -static const SSLCipherSuite Cipher280 = SSLCipherSuite(0xC08A, SSL_KEYX_ECDHE, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_128_GCM, SSL_HASH_SHA256, "TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256"); -static const SSLCipherSuite Cipher281 = SSLCipherSuite(0xC08B, SSL_KEYX_ECDHE, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_256_GCM, SSL_HASH_SHA384, "TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384"); -static const SSLCipherSuite Cipher282 = SSLCipherSuite(0xC08C, SSL_KEYX_ECDH, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_128_GCM, SSL_HASH_SHA256, "TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256"); -static const SSLCipherSuite Cipher283 = SSLCipherSuite(0xC08D, SSL_KEYX_ECDH, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_256_GCM, SSL_HASH_SHA384, "TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384"); -static const SSLCipherSuite Cipher284 = SSLCipherSuite(0xC08E, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_CAMELLIA_128_GCM, SSL_HASH_SHA256, "TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256"); -static const SSLCipherSuite Cipher285 = SSLCipherSuite(0xC08F, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_CAMELLIA_256_GCM, SSL_HASH_SHA384, "TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384"); -static const SSLCipherSuite Cipher286 = SSLCipherSuite(0xC090, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_CAMELLIA_128_GCM, SSL_HASH_SHA256, "TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256"); -static const SSLCipherSuite Cipher287 = SSLCipherSuite(0xC091, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_CAMELLIA_256_GCM, SSL_HASH_SHA384, "TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384"); -static const SSLCipherSuite Cipher288 = SSLCipherSuite(0xC092, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_CAMELLIA_128_GCM, SSL_HASH_SHA256, "TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256"); -static const SSLCipherSuite Cipher289 = SSLCipherSuite(0xC093, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_CAMELLIA_256_GCM, SSL_HASH_SHA384, "TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384"); -static const SSLCipherSuite Cipher290 = SSLCipherSuite(0xC094, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_CAMELLIA_128_CBC, SSL_HASH_SHA256, "TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256"); -static const SSLCipherSuite Cipher291 = SSLCipherSuite(0xC095, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_CAMELLIA_256_CBC, SSL_HASH_SHA384, "TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384"); -static const SSLCipherSuite Cipher292 = SSLCipherSuite(0xC096, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_CAMELLIA_128_CBC, SSL_HASH_SHA256, "TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256"); -static const SSLCipherSuite Cipher293 = SSLCipherSuite(0xC097, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_CAMELLIA_256_CBC, SSL_HASH_SHA384, "TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384"); -static const SSLCipherSuite Cipher294 = SSLCipherSuite(0xC098, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_CAMELLIA_128_CBC, SSL_HASH_SHA256, "TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256"); -static const SSLCipherSuite Cipher295 = SSLCipherSuite(0xC099, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_CAMELLIA_256_CBC, SSL_HASH_SHA384, "TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384"); -static const SSLCipherSuite Cipher296 = SSLCipherSuite(0xC09A, SSL_KEYX_ECDHE, SSL_AUTH_PSK, SSL_SYM_CAMELLIA_128_CBC, SSL_HASH_SHA256, "TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256"); -static const SSLCipherSuite Cipher297 = SSLCipherSuite(0xC09B, SSL_KEYX_ECDHE, SSL_AUTH_PSK, SSL_SYM_CAMELLIA_256_CBC, SSL_HASH_SHA384, "TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384"); -static const SSLCipherSuite Cipher298 = SSLCipherSuite(0xC09C, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_AES_128, SSL_HASH_CCM, "TLS_RSA_WITH_AES_128_CCM"); -static const SSLCipherSuite Cipher299 = SSLCipherSuite(0xC09D, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_AES_256, SSL_HASH_CCM, "TLS_RSA_WITH_AES_256_CCM"); -static const SSLCipherSuite Cipher300 = SSLCipherSuite(0xC09E, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_AES_128, SSL_HASH_CCM, "TLS_DHE_RSA_WITH_AES_128_CCM"); -static const SSLCipherSuite Cipher301 = SSLCipherSuite(0xC09F, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_AES_256, SSL_HASH_CCM, "TLS_DHE_RSA_WITH_AES_256_CCM"); -static const SSLCipherSuite Cipher302 = SSLCipherSuite(0xC0A0, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_AES_128, SSL_HASH_CCM_8, "TLS_RSA_WITH_AES_128_CCM_8"); -static const SSLCipherSuite Cipher303 = SSLCipherSuite(0xC0A1, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_AES_256, SSL_HASH_CCM_8, "TLS_RSA_WITH_AES_256_CCM_8"); -static const SSLCipherSuite Cipher304 = SSLCipherSuite(0xC0A2, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_AES_128, SSL_HASH_CCM_8, "TLS_DHE_RSA_WITH_AES_128_CCM_8"); -static const SSLCipherSuite Cipher305 = SSLCipherSuite(0xC0A3, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_AES_256, SSL_HASH_CCM_8, "TLS_DHE_RSA_WITH_AES_256_CCM_8"); -static const SSLCipherSuite Cipher306 = SSLCipherSuite(0xC0A4, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_AES_128, SSL_HASH_CCM, "TLS_PSK_WITH_AES_128_CCM"); -static const SSLCipherSuite Cipher307 = SSLCipherSuite(0xC0A5, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_AES_256, SSL_HASH_CCM, "TLS_PSK_WITH_AES_256_CCM"); -static const SSLCipherSuite Cipher308 = SSLCipherSuite(0xC0A6, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_AES_128, SSL_HASH_CCM, "TLS_DHE_PSK_WITH_AES_128_CCM"); -static const SSLCipherSuite Cipher309 = SSLCipherSuite(0xC0A7, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_AES_256, SSL_HASH_CCM, "TLS_DHE_PSK_WITH_AES_256_CCM"); -static const SSLCipherSuite Cipher310 = SSLCipherSuite(0xC0A8, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_AES_128, SSL_HASH_CCM_8, "TLS_PSK_WITH_AES_128_CCM_8"); -static const SSLCipherSuite Cipher311 = SSLCipherSuite(0xC0A9, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_AES_256, SSL_HASH_CCM_8, "TLS_PSK_WITH_AES_256_CCM_8"); -static const SSLCipherSuite Cipher312 = SSLCipherSuite(0xC0AA, SSL_KEYX_PSK, SSL_AUTH_DHE, SSL_SYM_AES_128, SSL_HASH_CCM_8, "TLS_PSK_DHE_WITH_AES_128_CCM_8"); -static const SSLCipherSuite Cipher313 = SSLCipherSuite(0xC0AB, SSL_KEYX_PSK, SSL_AUTH_DHE, SSL_SYM_AES_256, SSL_HASH_CCM_8, "TLS_PSK_DHE_WITH_AES_256_CCM_8"); -static const SSLCipherSuite Cipher314 = SSLCipherSuite(0xC0AC, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_AES_128, SSL_HASH_CCM, "TLS_ECDHE_ECDSA_WITH_AES_128_CCM"); -static const SSLCipherSuite Cipher315 = SSLCipherSuite(0xC0AD, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_AES_256, SSL_HASH_CCM, "TLS_ECDHE_ECDSA_WITH_AES_256_CCM"); -static const SSLCipherSuite Cipher316 = SSLCipherSuite(0xC0AE, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_AES_128, SSL_HASH_CCM_8, "TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8"); -static const SSLCipherSuite Cipher317 = SSLCipherSuite(0xC0AF, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_AES_256, SSL_HASH_CCM_8, "TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8"); -static const SSLCipherSuite Cipher318 = SSLCipherSuite(0xCCA8, SSL_KEYX_ECDHE, SSL_AUTH_RSA, SSL_SYM_CHACHA20_POLY1305, SSL_HASH_SHA256, "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256"); -static const SSLCipherSuite Cipher319 = SSLCipherSuite(0xCCA9, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_CHACHA20_POLY1305, SSL_HASH_SHA256, "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256"); -static const SSLCipherSuite Cipher320 = SSLCipherSuite(0xCCAA, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_CHACHA20_POLY1305, SSL_HASH_SHA256, "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256"); -static const SSLCipherSuite Cipher321 = SSLCipherSuite(0xCCAB, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_CHACHA20_POLY1305, SSL_HASH_SHA256, "TLS_PSK_WITH_CHACHA20_POLY1305_SHA256"); -static const SSLCipherSuite Cipher322 = SSLCipherSuite(0xCCAC, SSL_KEYX_ECDHE, SSL_AUTH_PSK, SSL_SYM_CHACHA20_POLY1305, SSL_HASH_SHA256, "TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256"); -static const SSLCipherSuite Cipher323 = SSLCipherSuite(0xCCAD, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_CHACHA20_POLY1305, SSL_HASH_SHA256, "TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256"); -static const SSLCipherSuite Cipher324 = SSLCipherSuite(0xCCAE, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_CHACHA20_POLY1305, SSL_HASH_SHA256, "TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256"); -static const SSLCipherSuite Cipher325 = SSLCipherSuite(0x1301, SSL_KEYX_NULL, SSL_AUTH_NULL, SSL_SYM_AES_128_GCM, SSL_HASH_SHA256, "TLS_AES_128_GCM_SHA256"); -static const SSLCipherSuite Cipher326 = SSLCipherSuite(0x1302, SSL_KEYX_NULL, SSL_AUTH_NULL, SSL_SYM_AES_256_GCM, SSL_HASH_SHA384, "TLS_AES_256_GCM_SHA384"); -static const SSLCipherSuite Cipher327 = SSLCipherSuite(0x1303, SSL_KEYX_NULL, SSL_AUTH_NULL, SSL_SYM_CHACHA20_POLY1305, SSL_HASH_SHA256, "TLS_CHACHA20_POLY1305_SHA256"); -static const SSLCipherSuite Cipher328 = SSLCipherSuite(0x1304, SSL_KEYX_NULL, SSL_AUTH_NULL, SSL_SYM_AES_128_CCM, SSL_HASH_SHA256, "TLS_AES_128_CCM_SHA256"); -static const SSLCipherSuite Cipher329 = SSLCipherSuite(0x1305, SSL_KEYX_NULL, SSL_AUTH_NULL, SSL_SYM_AES_128_CCM_8, SSL_HASH_SHA256, "TLS_AES_128_CCM_8_SHA256"); - - -static std::map createCipherSuiteIdToObjectMap() -{ - std::map result; - - result[0x0000] = (SSLCipherSuite*)&Cipher1; - result[0x0001] = (SSLCipherSuite*)&Cipher2; - result[0x0002] = (SSLCipherSuite*)&Cipher3; - result[0x0003] = (SSLCipherSuite*)&Cipher4; - result[0x0004] = (SSLCipherSuite*)&Cipher5; - result[0x0005] = (SSLCipherSuite*)&Cipher6; - result[0x0006] = (SSLCipherSuite*)&Cipher7; - result[0x0007] = (SSLCipherSuite*)&Cipher8; - result[0x0008] = (SSLCipherSuite*)&Cipher9; - result[0x0009] = (SSLCipherSuite*)&Cipher10; - result[0x000A] = (SSLCipherSuite*)&Cipher11; - result[0x000B] = (SSLCipherSuite*)&Cipher12; - result[0x000C] = (SSLCipherSuite*)&Cipher13; - result[0x000D] = (SSLCipherSuite*)&Cipher14; - result[0x000E] = (SSLCipherSuite*)&Cipher15; - result[0x000F] = (SSLCipherSuite*)&Cipher16; - result[0x0010] = (SSLCipherSuite*)&Cipher17; - result[0x0011] = (SSLCipherSuite*)&Cipher18; - result[0x0012] = (SSLCipherSuite*)&Cipher19; - result[0x0013] = (SSLCipherSuite*)&Cipher20; - result[0x0014] = (SSLCipherSuite*)&Cipher21; - result[0x0015] = (SSLCipherSuite*)&Cipher22; - result[0x0016] = (SSLCipherSuite*)&Cipher23; - result[0x0017] = (SSLCipherSuite*)&Cipher24; - result[0x0018] = (SSLCipherSuite*)&Cipher25; - result[0x0019] = (SSLCipherSuite*)&Cipher26; - result[0x001A] = (SSLCipherSuite*)&Cipher27; - result[0x001B] = (SSLCipherSuite*)&Cipher28; - result[0x001E] = (SSLCipherSuite*)&Cipher29; - result[0x001F] = (SSLCipherSuite*)&Cipher30; - result[0x0020] = (SSLCipherSuite*)&Cipher31; - result[0x0021] = (SSLCipherSuite*)&Cipher32; - result[0x0022] = (SSLCipherSuite*)&Cipher33; - result[0x0023] = (SSLCipherSuite*)&Cipher34; - result[0x0024] = (SSLCipherSuite*)&Cipher35; - result[0x0025] = (SSLCipherSuite*)&Cipher36; - result[0x0026] = (SSLCipherSuite*)&Cipher37; - result[0x0027] = (SSLCipherSuite*)&Cipher38; - result[0x0028] = (SSLCipherSuite*)&Cipher39; - result[0x0029] = (SSLCipherSuite*)&Cipher40; - result[0x002A] = (SSLCipherSuite*)&Cipher41; - result[0x002B] = (SSLCipherSuite*)&Cipher42; - result[0x002C] = (SSLCipherSuite*)&Cipher43; - result[0x002D] = (SSLCipherSuite*)&Cipher44; - result[0x002E] = (SSLCipherSuite*)&Cipher45; - result[0x002F] = (SSLCipherSuite*)&Cipher46; - result[0x0030] = (SSLCipherSuite*)&Cipher47; - result[0x0031] = (SSLCipherSuite*)&Cipher48; - result[0x0032] = (SSLCipherSuite*)&Cipher49; - result[0x0033] = (SSLCipherSuite*)&Cipher50; - result[0x0034] = (SSLCipherSuite*)&Cipher51; - result[0x0035] = (SSLCipherSuite*)&Cipher52; - result[0x0036] = (SSLCipherSuite*)&Cipher53; - result[0x0037] = (SSLCipherSuite*)&Cipher54; - result[0x0038] = (SSLCipherSuite*)&Cipher55; - result[0x0039] = (SSLCipherSuite*)&Cipher56; - result[0x003A] = (SSLCipherSuite*)&Cipher57; - result[0x003B] = (SSLCipherSuite*)&Cipher58; - result[0x003C] = (SSLCipherSuite*)&Cipher59; - result[0x003D] = (SSLCipherSuite*)&Cipher60; - result[0x003E] = (SSLCipherSuite*)&Cipher61; - result[0x003F] = (SSLCipherSuite*)&Cipher62; - result[0x0040] = (SSLCipherSuite*)&Cipher63; - result[0x0041] = (SSLCipherSuite*)&Cipher64; - result[0x0042] = (SSLCipherSuite*)&Cipher65; - result[0x0043] = (SSLCipherSuite*)&Cipher66; - result[0x0044] = (SSLCipherSuite*)&Cipher67; - result[0x0045] = (SSLCipherSuite*)&Cipher68; - result[0x0046] = (SSLCipherSuite*)&Cipher69; - result[0x0067] = (SSLCipherSuite*)&Cipher70; - result[0x0068] = (SSLCipherSuite*)&Cipher71; - result[0x0069] = (SSLCipherSuite*)&Cipher72; - result[0x006A] = (SSLCipherSuite*)&Cipher73; - result[0x006B] = (SSLCipherSuite*)&Cipher74; - result[0x006C] = (SSLCipherSuite*)&Cipher75; - result[0x006D] = (SSLCipherSuite*)&Cipher76; - result[0x0084] = (SSLCipherSuite*)&Cipher77; - result[0x0085] = (SSLCipherSuite*)&Cipher78; - result[0x0086] = (SSLCipherSuite*)&Cipher79; - result[0x0087] = (SSLCipherSuite*)&Cipher80; - result[0x0088] = (SSLCipherSuite*)&Cipher81; - result[0x0089] = (SSLCipherSuite*)&Cipher82; - result[0x008A] = (SSLCipherSuite*)&Cipher83; - result[0x008B] = (SSLCipherSuite*)&Cipher84; - result[0x008C] = (SSLCipherSuite*)&Cipher85; - result[0x008D] = (SSLCipherSuite*)&Cipher86; - result[0x008E] = (SSLCipherSuite*)&Cipher87; - result[0x008F] = (SSLCipherSuite*)&Cipher88; - result[0x0090] = (SSLCipherSuite*)&Cipher89; - result[0x0091] = (SSLCipherSuite*)&Cipher90; - result[0x0092] = (SSLCipherSuite*)&Cipher91; - result[0x0093] = (SSLCipherSuite*)&Cipher92; - result[0x0094] = (SSLCipherSuite*)&Cipher93; - result[0x0095] = (SSLCipherSuite*)&Cipher94; - result[0x0096] = (SSLCipherSuite*)&Cipher95; - result[0x0097] = (SSLCipherSuite*)&Cipher96; - result[0x0098] = (SSLCipherSuite*)&Cipher97; - result[0x0099] = (SSLCipherSuite*)&Cipher98; - result[0x009A] = (SSLCipherSuite*)&Cipher99; - result[0x009B] = (SSLCipherSuite*)&Cipher100; - result[0x009C] = (SSLCipherSuite*)&Cipher101; - result[0x009D] = (SSLCipherSuite*)&Cipher102; - result[0x009E] = (SSLCipherSuite*)&Cipher103; - result[0x009F] = (SSLCipherSuite*)&Cipher104; - result[0x00A0] = (SSLCipherSuite*)&Cipher105; - result[0x00A1] = (SSLCipherSuite*)&Cipher106; - result[0x00A2] = (SSLCipherSuite*)&Cipher107; - result[0x00A3] = (SSLCipherSuite*)&Cipher108; - result[0x00A4] = (SSLCipherSuite*)&Cipher109; - result[0x00A5] = (SSLCipherSuite*)&Cipher110; - result[0x00A6] = (SSLCipherSuite*)&Cipher111; - result[0x00A7] = (SSLCipherSuite*)&Cipher112; - result[0x00A8] = (SSLCipherSuite*)&Cipher113; - result[0x00A9] = (SSLCipherSuite*)&Cipher114; - result[0x00AA] = (SSLCipherSuite*)&Cipher115; - result[0x00AB] = (SSLCipherSuite*)&Cipher116; - result[0x00AC] = (SSLCipherSuite*)&Cipher117; - result[0x00AD] = (SSLCipherSuite*)&Cipher118; - result[0x00AE] = (SSLCipherSuite*)&Cipher119; - result[0x00AF] = (SSLCipherSuite*)&Cipher120; - result[0x00B0] = (SSLCipherSuite*)&Cipher121; - result[0x00B1] = (SSLCipherSuite*)&Cipher122; - result[0x00B2] = (SSLCipherSuite*)&Cipher123; - result[0x00B3] = (SSLCipherSuite*)&Cipher124; - result[0x00B4] = (SSLCipherSuite*)&Cipher125; - result[0x00B5] = (SSLCipherSuite*)&Cipher126; - result[0x00B6] = (SSLCipherSuite*)&Cipher127; - result[0x00B7] = (SSLCipherSuite*)&Cipher128; - result[0x00B8] = (SSLCipherSuite*)&Cipher129; - result[0x00B9] = (SSLCipherSuite*)&Cipher130; - result[0x00BA] = (SSLCipherSuite*)&Cipher131; - result[0x00BB] = (SSLCipherSuite*)&Cipher132; - result[0x00BC] = (SSLCipherSuite*)&Cipher133; - result[0x00BD] = (SSLCipherSuite*)&Cipher134; - result[0x00BE] = (SSLCipherSuite*)&Cipher135; - result[0x00BF] = (SSLCipherSuite*)&Cipher136; - result[0x00C0] = (SSLCipherSuite*)&Cipher137; - result[0x00C1] = (SSLCipherSuite*)&Cipher138; - result[0x00C2] = (SSLCipherSuite*)&Cipher139; - result[0x00C3] = (SSLCipherSuite*)&Cipher140; - result[0x00C4] = (SSLCipherSuite*)&Cipher141; - result[0x00C5] = (SSLCipherSuite*)&Cipher142; - result[0xC001] = (SSLCipherSuite*)&Cipher143; - result[0xC002] = (SSLCipherSuite*)&Cipher144; - result[0xC003] = (SSLCipherSuite*)&Cipher145; - result[0xC004] = (SSLCipherSuite*)&Cipher146; - result[0xC005] = (SSLCipherSuite*)&Cipher147; - result[0xC006] = (SSLCipherSuite*)&Cipher148; - result[0xC007] = (SSLCipherSuite*)&Cipher149; - result[0xC008] = (SSLCipherSuite*)&Cipher150; - result[0xC009] = (SSLCipherSuite*)&Cipher151; - result[0xC00A] = (SSLCipherSuite*)&Cipher152; - result[0xC00B] = (SSLCipherSuite*)&Cipher153; - result[0xC00C] = (SSLCipherSuite*)&Cipher154; - result[0xC00D] = (SSLCipherSuite*)&Cipher155; - result[0xC00E] = (SSLCipherSuite*)&Cipher156; - result[0xC00F] = (SSLCipherSuite*)&Cipher157; - result[0xC010] = (SSLCipherSuite*)&Cipher158; - result[0xC011] = (SSLCipherSuite*)&Cipher159; - result[0xC012] = (SSLCipherSuite*)&Cipher160; - result[0xC013] = (SSLCipherSuite*)&Cipher161; - result[0xC014] = (SSLCipherSuite*)&Cipher162; - result[0xC015] = (SSLCipherSuite*)&Cipher163; - result[0xC016] = (SSLCipherSuite*)&Cipher164; - result[0xC017] = (SSLCipherSuite*)&Cipher165; - result[0xC018] = (SSLCipherSuite*)&Cipher166; - result[0xC019] = (SSLCipherSuite*)&Cipher167; - result[0xC01A] = (SSLCipherSuite*)&Cipher168; - result[0xC01B] = (SSLCipherSuite*)&Cipher169; - result[0xC01C] = (SSLCipherSuite*)&Cipher170; - result[0xC01D] = (SSLCipherSuite*)&Cipher171; - result[0xC01E] = (SSLCipherSuite*)&Cipher172; - result[0xC01F] = (SSLCipherSuite*)&Cipher173; - result[0xC020] = (SSLCipherSuite*)&Cipher174; - result[0xC021] = (SSLCipherSuite*)&Cipher175; - result[0xC022] = (SSLCipherSuite*)&Cipher176; - result[0xC023] = (SSLCipherSuite*)&Cipher177; - result[0xC024] = (SSLCipherSuite*)&Cipher178; - result[0xC025] = (SSLCipherSuite*)&Cipher179; - result[0xC026] = (SSLCipherSuite*)&Cipher180; - result[0xC027] = (SSLCipherSuite*)&Cipher181; - result[0xC028] = (SSLCipherSuite*)&Cipher182; - result[0xC029] = (SSLCipherSuite*)&Cipher183; - result[0xC02A] = (SSLCipherSuite*)&Cipher184; - result[0xC02B] = (SSLCipherSuite*)&Cipher185; - result[0xC02C] = (SSLCipherSuite*)&Cipher186; - result[0xC02D] = (SSLCipherSuite*)&Cipher187; - result[0xC02E] = (SSLCipherSuite*)&Cipher188; - result[0xC02F] = (SSLCipherSuite*)&Cipher189; - result[0xC030] = (SSLCipherSuite*)&Cipher190; - result[0xC031] = (SSLCipherSuite*)&Cipher191; - result[0xC032] = (SSLCipherSuite*)&Cipher192; - result[0xC033] = (SSLCipherSuite*)&Cipher193; - result[0xC034] = (SSLCipherSuite*)&Cipher194; - result[0xC035] = (SSLCipherSuite*)&Cipher195; - result[0xC036] = (SSLCipherSuite*)&Cipher196; - result[0xC037] = (SSLCipherSuite*)&Cipher197; - result[0xC038] = (SSLCipherSuite*)&Cipher198; - result[0xC039] = (SSLCipherSuite*)&Cipher199; - result[0xC03A] = (SSLCipherSuite*)&Cipher200; - result[0xC03B] = (SSLCipherSuite*)&Cipher201; - result[0xC03C] = (SSLCipherSuite*)&Cipher202; - result[0xC03D] = (SSLCipherSuite*)&Cipher203; - result[0xC03E] = (SSLCipherSuite*)&Cipher204; - result[0xC03F] = (SSLCipherSuite*)&Cipher205; - result[0xC040] = (SSLCipherSuite*)&Cipher206; - result[0xC041] = (SSLCipherSuite*)&Cipher207; - result[0xC042] = (SSLCipherSuite*)&Cipher208; - result[0xC043] = (SSLCipherSuite*)&Cipher209; - result[0xC044] = (SSLCipherSuite*)&Cipher210; - result[0xC045] = (SSLCipherSuite*)&Cipher211; - result[0xC046] = (SSLCipherSuite*)&Cipher212; - result[0xC047] = (SSLCipherSuite*)&Cipher213; - result[0xC048] = (SSLCipherSuite*)&Cipher214; - result[0xC049] = (SSLCipherSuite*)&Cipher215; - result[0xC04A] = (SSLCipherSuite*)&Cipher216; - result[0xC04B] = (SSLCipherSuite*)&Cipher217; - result[0xC04C] = (SSLCipherSuite*)&Cipher218; - result[0xC04D] = (SSLCipherSuite*)&Cipher219; - result[0xC04E] = (SSLCipherSuite*)&Cipher220; - result[0xC04F] = (SSLCipherSuite*)&Cipher221; - result[0xC050] = (SSLCipherSuite*)&Cipher222; - result[0xC051] = (SSLCipherSuite*)&Cipher223; - result[0xC052] = (SSLCipherSuite*)&Cipher224; - result[0xC053] = (SSLCipherSuite*)&Cipher225; - result[0xC054] = (SSLCipherSuite*)&Cipher226; - result[0xC055] = (SSLCipherSuite*)&Cipher227; - result[0xC056] = (SSLCipherSuite*)&Cipher228; - result[0xC057] = (SSLCipherSuite*)&Cipher229; - result[0xC058] = (SSLCipherSuite*)&Cipher230; - result[0xC059] = (SSLCipherSuite*)&Cipher231; - result[0xC05A] = (SSLCipherSuite*)&Cipher232; - result[0xC05B] = (SSLCipherSuite*)&Cipher233; - result[0xC05C] = (SSLCipherSuite*)&Cipher234; - result[0xC05D] = (SSLCipherSuite*)&Cipher235; - result[0xC05E] = (SSLCipherSuite*)&Cipher236; - result[0xC05F] = (SSLCipherSuite*)&Cipher237; - result[0xC060] = (SSLCipherSuite*)&Cipher238; - result[0xC061] = (SSLCipherSuite*)&Cipher239; - result[0xC062] = (SSLCipherSuite*)&Cipher240; - result[0xC063] = (SSLCipherSuite*)&Cipher241; - result[0xC064] = (SSLCipherSuite*)&Cipher242; - result[0xC065] = (SSLCipherSuite*)&Cipher243; - result[0xC066] = (SSLCipherSuite*)&Cipher244; - result[0xC067] = (SSLCipherSuite*)&Cipher245; - result[0xC068] = (SSLCipherSuite*)&Cipher246; - result[0xC069] = (SSLCipherSuite*)&Cipher247; - result[0xC06A] = (SSLCipherSuite*)&Cipher248; - result[0xC06B] = (SSLCipherSuite*)&Cipher249; - result[0xC06C] = (SSLCipherSuite*)&Cipher250; - result[0xC06D] = (SSLCipherSuite*)&Cipher251; - result[0xC06E] = (SSLCipherSuite*)&Cipher252; - result[0xC06F] = (SSLCipherSuite*)&Cipher253; - result[0xC070] = (SSLCipherSuite*)&Cipher254; - result[0xC071] = (SSLCipherSuite*)&Cipher255; - result[0xC072] = (SSLCipherSuite*)&Cipher256; - result[0xC073] = (SSLCipherSuite*)&Cipher257; - result[0xC074] = (SSLCipherSuite*)&Cipher258; - result[0xC075] = (SSLCipherSuite*)&Cipher259; - result[0xC076] = (SSLCipherSuite*)&Cipher260; - result[0xC077] = (SSLCipherSuite*)&Cipher261; - result[0xC078] = (SSLCipherSuite*)&Cipher262; - result[0xC079] = (SSLCipherSuite*)&Cipher263; - result[0xC07A] = (SSLCipherSuite*)&Cipher264; - result[0xC07B] = (SSLCipherSuite*)&Cipher265; - result[0xC07C] = (SSLCipherSuite*)&Cipher266; - result[0xC07D] = (SSLCipherSuite*)&Cipher267; - result[0xC07E] = (SSLCipherSuite*)&Cipher268; - result[0xC07F] = (SSLCipherSuite*)&Cipher269; - result[0xC080] = (SSLCipherSuite*)&Cipher270; - result[0xC081] = (SSLCipherSuite*)&Cipher271; - result[0xC082] = (SSLCipherSuite*)&Cipher272; - result[0xC083] = (SSLCipherSuite*)&Cipher273; - result[0xC084] = (SSLCipherSuite*)&Cipher274; - result[0xC085] = (SSLCipherSuite*)&Cipher275; - result[0xC086] = (SSLCipherSuite*)&Cipher276; - result[0xC087] = (SSLCipherSuite*)&Cipher277; - result[0xC088] = (SSLCipherSuite*)&Cipher278; - result[0xC089] = (SSLCipherSuite*)&Cipher279; - result[0xC08A] = (SSLCipherSuite*)&Cipher280; - result[0xC08B] = (SSLCipherSuite*)&Cipher281; - result[0xC08C] = (SSLCipherSuite*)&Cipher282; - result[0xC08D] = (SSLCipherSuite*)&Cipher283; - result[0xC08E] = (SSLCipherSuite*)&Cipher284; - result[0xC08F] = (SSLCipherSuite*)&Cipher285; - result[0xC090] = (SSLCipherSuite*)&Cipher286; - result[0xC091] = (SSLCipherSuite*)&Cipher287; - result[0xC092] = (SSLCipherSuite*)&Cipher288; - result[0xC093] = (SSLCipherSuite*)&Cipher289; - result[0xC094] = (SSLCipherSuite*)&Cipher290; - result[0xC095] = (SSLCipherSuite*)&Cipher291; - result[0xC096] = (SSLCipherSuite*)&Cipher292; - result[0xC097] = (SSLCipherSuite*)&Cipher293; - result[0xC098] = (SSLCipherSuite*)&Cipher294; - result[0xC099] = (SSLCipherSuite*)&Cipher295; - result[0xC09A] = (SSLCipherSuite*)&Cipher296; - result[0xC09B] = (SSLCipherSuite*)&Cipher297; - result[0xC09C] = (SSLCipherSuite*)&Cipher298; - result[0xC09D] = (SSLCipherSuite*)&Cipher299; - result[0xC09E] = (SSLCipherSuite*)&Cipher300; - result[0xC09F] = (SSLCipherSuite*)&Cipher301; - result[0xC0A0] = (SSLCipherSuite*)&Cipher302; - result[0xC0A1] = (SSLCipherSuite*)&Cipher303; - result[0xC0A2] = (SSLCipherSuite*)&Cipher304; - result[0xC0A3] = (SSLCipherSuite*)&Cipher305; - result[0xC0A4] = (SSLCipherSuite*)&Cipher306; - result[0xC0A5] = (SSLCipherSuite*)&Cipher307; - result[0xC0A6] = (SSLCipherSuite*)&Cipher308; - result[0xC0A7] = (SSLCipherSuite*)&Cipher309; - result[0xC0A8] = (SSLCipherSuite*)&Cipher310; - result[0xC0A9] = (SSLCipherSuite*)&Cipher311; - result[0xC0AA] = (SSLCipherSuite*)&Cipher312; - result[0xC0AB] = (SSLCipherSuite*)&Cipher313; - result[0xC0AC] = (SSLCipherSuite*)&Cipher314; - result[0xC0AD] = (SSLCipherSuite*)&Cipher315; - result[0xC0AE] = (SSLCipherSuite*)&Cipher316; - result[0xC0AF] = (SSLCipherSuite*)&Cipher317; - result[0xCCA8] = (SSLCipherSuite*)&Cipher318; - result[0xCCA9] = (SSLCipherSuite*)&Cipher319; - result[0xCCAA] = (SSLCipherSuite*)&Cipher320; - result[0xCCAB] = (SSLCipherSuite*)&Cipher321; - result[0xCCAC] = (SSLCipherSuite*)&Cipher322; - result[0xCCAD] = (SSLCipherSuite*)&Cipher323; - result[0xCCAE] = (SSLCipherSuite*)&Cipher324; - result[0x1301] = (SSLCipherSuite*)&Cipher325; - result[0x1302] = (SSLCipherSuite*)&Cipher326; - result[0x1303] = (SSLCipherSuite*)&Cipher327; - result[0x1304] = (SSLCipherSuite*)&Cipher328; - result[0x1305] = (SSLCipherSuite*)&Cipher329; - return result; -} - -#define A 54059 /* a prime */ -#define B 76963 /* another prime */ -#define C 86969 /* yet another prime */ +static const SSLCipherSuite Cipher1 = + SSLCipherSuite(0x0000, SSL_KEYX_NULL, SSL_AUTH_NULL, SSL_SYM_NULL, + SSL_HASH_NULL, "TLS_NULL_WITH_NULL_NULL"); +static const SSLCipherSuite Cipher2 = + SSLCipherSuite(0x0001, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_NULL, + SSL_HASH_MD5, "TLS_RSA_WITH_NULL_MD5"); +static const SSLCipherSuite Cipher3 = + SSLCipherSuite(0x0002, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_NULL, + SSL_HASH_SHA, "TLS_RSA_WITH_NULL_SHA"); +static const SSLCipherSuite Cipher4 = + SSLCipherSuite(0x0003, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_RC4_40, + SSL_HASH_MD5, "TLS_RSA_EXPORT_WITH_RC4_40_MD5"); +static const SSLCipherSuite Cipher5 = + SSLCipherSuite(0x0004, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_RC4_128, + SSL_HASH_MD5, "TLS_RSA_WITH_RC4_128_MD5"); +static const SSLCipherSuite Cipher6 = + SSLCipherSuite(0x0005, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_RC4_128, + SSL_HASH_SHA, "TLS_RSA_WITH_RC4_128_SHA"); +static const SSLCipherSuite Cipher7 = + SSLCipherSuite(0x0006, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_RC2_CBC_40, + SSL_HASH_MD5, "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5"); +static const SSLCipherSuite Cipher8 = + SSLCipherSuite(0x0007, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_IDEA_CBC, + SSL_HASH_SHA, "TLS_RSA_WITH_IDEA_CBC_SHA"); +static const SSLCipherSuite Cipher9 = + SSLCipherSuite(0x0008, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_DES40_CBC, + SSL_HASH_SHA, "TLS_RSA_EXPORT_WITH_DES40_CBC_SHA"); +static const SSLCipherSuite Cipher10 = + SSLCipherSuite(0x0009, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_DES_CBC, + SSL_HASH_SHA, "TLS_RSA_WITH_DES_CBC_SHA"); +static const SSLCipherSuite Cipher11 = + SSLCipherSuite(0x000A, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_3DES_EDE_CBC, + SSL_HASH_SHA, "TLS_RSA_WITH_3DES_EDE_CBC_SHA"); +static const SSLCipherSuite Cipher12 = + SSLCipherSuite(0x000B, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_DES40_CBC, + SSL_HASH_SHA, "TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA"); +static const SSLCipherSuite Cipher13 = + SSLCipherSuite(0x000C, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_DES_CBC, + SSL_HASH_SHA, "TLS_DH_DSS_WITH_DES_CBC_SHA"); +static const SSLCipherSuite Cipher14 = + SSLCipherSuite(0x000D, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_3DES_EDE_CBC, + SSL_HASH_SHA, "TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA"); +static const SSLCipherSuite Cipher15 = + SSLCipherSuite(0x000E, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_DES40_CBC, + SSL_HASH_SHA, "TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA"); +static const SSLCipherSuite Cipher16 = + SSLCipherSuite(0x000F, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_DES_CBC, + SSL_HASH_SHA, "TLS_DH_RSA_WITH_DES_CBC_SHA"); +static const SSLCipherSuite Cipher17 = + SSLCipherSuite(0x0010, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_3DES_EDE_CBC, + SSL_HASH_SHA, "TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA"); +static const SSLCipherSuite Cipher18 = + SSLCipherSuite(0x0011, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_DES40_CBC, + SSL_HASH_SHA, "TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA"); +static const SSLCipherSuite Cipher19 = + SSLCipherSuite(0x0012, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_DES_CBC, + SSL_HASH_SHA, "TLS_DHE_DSS_WITH_DES_CBC_SHA"); +static const SSLCipherSuite Cipher20 = + SSLCipherSuite(0x0013, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_3DES_EDE_CBC, + SSL_HASH_SHA, "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA"); +static const SSLCipherSuite Cipher21 = + SSLCipherSuite(0x0014, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_DES40_CBC, + SSL_HASH_SHA, "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA"); +static const SSLCipherSuite Cipher22 = + SSLCipherSuite(0x0015, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_DES_CBC, + SSL_HASH_SHA, "TLS_DHE_RSA_WITH_DES_CBC_SHA"); +static const SSLCipherSuite Cipher23 = + SSLCipherSuite(0x0016, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_3DES_EDE_CBC, + SSL_HASH_SHA, "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA"); +static const SSLCipherSuite Cipher24 = + SSLCipherSuite(0x0017, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_RC4_40, + SSL_HASH_MD5, "TLS_DH_anon_EXPORT_WITH_RC4_40_MD5"); +static const SSLCipherSuite Cipher25 = + SSLCipherSuite(0x0018, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_RC4_128, + SSL_HASH_MD5, "TLS_DH_anon_WITH_RC4_128_MD5"); +static const SSLCipherSuite Cipher26 = + SSLCipherSuite(0x0019, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_DES40_CBC, + SSL_HASH_SHA, "TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA"); +static const SSLCipherSuite Cipher27 = + SSLCipherSuite(0x001A, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_DES_CBC, + SSL_HASH_SHA, "TLS_DH_anon_WITH_DES_CBC_SHA"); +static const SSLCipherSuite Cipher28 = + SSLCipherSuite(0x001B, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_3DES_EDE_CBC, + SSL_HASH_SHA, "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA"); +static const SSLCipherSuite Cipher29 = + SSLCipherSuite(0x001E, SSL_KEYX_KRB5, SSL_AUTH_KRB5, SSL_SYM_DES_CBC, + SSL_HASH_SHA, "TLS_KRB5_WITH_DES_CBC_SHA"); +static const SSLCipherSuite Cipher30 = + SSLCipherSuite(0x001F, SSL_KEYX_KRB5, SSL_AUTH_KRB5, SSL_SYM_3DES_EDE_CBC, + SSL_HASH_SHA, "TLS_KRB5_WITH_3DES_EDE_CBC_SHA"); +static const SSLCipherSuite Cipher31 = + SSLCipherSuite(0x0020, SSL_KEYX_KRB5, SSL_AUTH_KRB5, SSL_SYM_RC4_128, + SSL_HASH_SHA, "TLS_KRB5_WITH_RC4_128_SHA"); +static const SSLCipherSuite Cipher32 = + SSLCipherSuite(0x0021, SSL_KEYX_KRB5, SSL_AUTH_KRB5, SSL_SYM_IDEA_CBC, + SSL_HASH_SHA, "TLS_KRB5_WITH_IDEA_CBC_SHA"); +static const SSLCipherSuite Cipher33 = + SSLCipherSuite(0x0022, SSL_KEYX_KRB5, SSL_AUTH_KRB5, SSL_SYM_DES_CBC, + SSL_HASH_MD5, "TLS_KRB5_WITH_DES_CBC_MD5"); +static const SSLCipherSuite Cipher34 = + SSLCipherSuite(0x0023, SSL_KEYX_KRB5, SSL_AUTH_KRB5, SSL_SYM_3DES_EDE_CBC, + SSL_HASH_MD5, "TLS_KRB5_WITH_3DES_EDE_CBC_MD5"); +static const SSLCipherSuite Cipher35 = + SSLCipherSuite(0x0024, SSL_KEYX_KRB5, SSL_AUTH_KRB5, SSL_SYM_RC4_128, + SSL_HASH_MD5, "TLS_KRB5_WITH_RC4_128_MD5"); +static const SSLCipherSuite Cipher36 = + SSLCipherSuite(0x0025, SSL_KEYX_KRB5, SSL_AUTH_KRB5, SSL_SYM_IDEA_CBC, + SSL_HASH_MD5, "TLS_KRB5_WITH_IDEA_CBC_MD5"); +static const SSLCipherSuite Cipher37 = + SSLCipherSuite(0x0026, SSL_KEYX_KRB5, SSL_AUTH_KRB5, SSL_SYM_DES_CBC_40, + SSL_HASH_SHA, "TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA"); +static const SSLCipherSuite Cipher38 = + SSLCipherSuite(0x0027, SSL_KEYX_KRB5, SSL_AUTH_KRB5, SSL_SYM_RC2_CBC_40, + SSL_HASH_SHA, "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA"); +static const SSLCipherSuite Cipher39 = + SSLCipherSuite(0x0028, SSL_KEYX_KRB5, SSL_AUTH_KRB5, SSL_SYM_RC4_40, + SSL_HASH_SHA, "TLS_KRB5_EXPORT_WITH_RC4_40_SHA"); +static const SSLCipherSuite Cipher40 = + SSLCipherSuite(0x0029, SSL_KEYX_KRB5, SSL_AUTH_KRB5, SSL_SYM_DES_CBC_40, + SSL_HASH_MD5, "TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5"); +static const SSLCipherSuite Cipher41 = + SSLCipherSuite(0x002A, SSL_KEYX_KRB5, SSL_AUTH_KRB5, SSL_SYM_RC2_CBC_40, + SSL_HASH_MD5, "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5"); +static const SSLCipherSuite Cipher42 = + SSLCipherSuite(0x002B, SSL_KEYX_KRB5, SSL_AUTH_KRB5, SSL_SYM_RC4_40, + SSL_HASH_MD5, "TLS_KRB5_EXPORT_WITH_RC4_40_MD5"); +static const SSLCipherSuite Cipher43 = + SSLCipherSuite(0x002C, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_NULL, + SSL_HASH_SHA, "TLS_PSK_WITH_NULL_SHA"); +static const SSLCipherSuite Cipher44 = + SSLCipherSuite(0x002D, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_NULL, + SSL_HASH_SHA, "TLS_DHE_PSK_WITH_NULL_SHA"); +static const SSLCipherSuite Cipher45 = + SSLCipherSuite(0x002E, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_NULL, + SSL_HASH_SHA, "TLS_RSA_PSK_WITH_NULL_SHA"); +static const SSLCipherSuite Cipher46 = + SSLCipherSuite(0x002F, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_AES_128_CBC, + SSL_HASH_SHA, "TLS_RSA_WITH_AES_128_CBC_SHA"); +static const SSLCipherSuite Cipher47 = + SSLCipherSuite(0x0030, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_AES_128_CBC, + SSL_HASH_SHA, "TLS_DH_DSS_WITH_AES_128_CBC_SHA"); +static const SSLCipherSuite Cipher48 = + SSLCipherSuite(0x0031, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_AES_128_CBC, + SSL_HASH_SHA, "TLS_DH_RSA_WITH_AES_128_CBC_SHA"); +static const SSLCipherSuite Cipher49 = + SSLCipherSuite(0x0032, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_AES_128_CBC, + SSL_HASH_SHA, "TLS_DHE_DSS_WITH_AES_128_CBC_SHA"); +static const SSLCipherSuite Cipher50 = + SSLCipherSuite(0x0033, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_AES_128_CBC, + SSL_HASH_SHA, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA"); +static const SSLCipherSuite Cipher51 = + SSLCipherSuite(0x0034, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_AES_128_CBC, + SSL_HASH_SHA, "TLS_DH_anon_WITH_AES_128_CBC_SHA"); +static const SSLCipherSuite Cipher52 = + SSLCipherSuite(0x0035, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_AES_256_CBC, + SSL_HASH_SHA, "TLS_RSA_WITH_AES_256_CBC_SHA"); +static const SSLCipherSuite Cipher53 = + SSLCipherSuite(0x0036, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_AES_256_CBC, + SSL_HASH_SHA, "TLS_DH_DSS_WITH_AES_256_CBC_SHA"); +static const SSLCipherSuite Cipher54 = + SSLCipherSuite(0x0037, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_AES_256_CBC, + SSL_HASH_SHA, "TLS_DH_RSA_WITH_AES_256_CBC_SHA"); +static const SSLCipherSuite Cipher55 = + SSLCipherSuite(0x0038, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_AES_256_CBC, + SSL_HASH_SHA, "TLS_DHE_DSS_WITH_AES_256_CBC_SHA"); +static const SSLCipherSuite Cipher56 = + SSLCipherSuite(0x0039, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_AES_256_CBC, + SSL_HASH_SHA, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA"); +static const SSLCipherSuite Cipher57 = + SSLCipherSuite(0x003A, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_AES_256_CBC, + SSL_HASH_SHA, "TLS_DH_anon_WITH_AES_256_CBC_SHA"); +static const SSLCipherSuite Cipher58 = + SSLCipherSuite(0x003B, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_NULL, + SSL_HASH_SHA256, "TLS_RSA_WITH_NULL_SHA256"); +static const SSLCipherSuite Cipher59 = + SSLCipherSuite(0x003C, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_AES_128_CBC, + SSL_HASH_SHA256, "TLS_RSA_WITH_AES_128_CBC_SHA256"); +static const SSLCipherSuite Cipher60 = + SSLCipherSuite(0x003D, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_AES_256_CBC, + SSL_HASH_SHA256, "TLS_RSA_WITH_AES_256_CBC_SHA256"); +static const SSLCipherSuite Cipher61 = + SSLCipherSuite(0x003E, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_AES_128_CBC, + SSL_HASH_SHA256, "TLS_DH_DSS_WITH_AES_128_CBC_SHA256"); +static const SSLCipherSuite Cipher62 = + SSLCipherSuite(0x003F, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_AES_128_CBC, + SSL_HASH_SHA256, "TLS_DH_RSA_WITH_AES_128_CBC_SHA256"); +static const SSLCipherSuite Cipher63 = + SSLCipherSuite(0x0040, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_AES_128_CBC, + SSL_HASH_SHA256, "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256"); +static const SSLCipherSuite Cipher64 = + SSLCipherSuite(0x0041, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_128_CBC, + SSL_HASH_SHA, "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA"); +static const SSLCipherSuite Cipher65 = + SSLCipherSuite(0x0042, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_CAMELLIA_128_CBC, + SSL_HASH_SHA, "TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA"); +static const SSLCipherSuite Cipher66 = + SSLCipherSuite(0x0043, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_128_CBC, + SSL_HASH_SHA, "TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA"); +static const SSLCipherSuite Cipher67 = + SSLCipherSuite(0x0044, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_CAMELLIA_128_CBC, + SSL_HASH_SHA, "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA"); +static const SSLCipherSuite Cipher68 = + SSLCipherSuite(0x0045, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_128_CBC, + SSL_HASH_SHA, "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA"); +static const SSLCipherSuite Cipher69 = + SSLCipherSuite(0x0046, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_CAMELLIA_128_CBC, + SSL_HASH_SHA, "TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA"); +static const SSLCipherSuite Cipher70 = + SSLCipherSuite(0x0067, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_AES_128_CBC, + SSL_HASH_SHA256, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256"); +static const SSLCipherSuite Cipher71 = + SSLCipherSuite(0x0068, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_AES_256_CBC, + SSL_HASH_SHA256, "TLS_DH_DSS_WITH_AES_256_CBC_SHA256"); +static const SSLCipherSuite Cipher72 = + SSLCipherSuite(0x0069, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_AES_256_CBC, + SSL_HASH_SHA256, "TLS_DH_RSA_WITH_AES_256_CBC_SHA256"); +static const SSLCipherSuite Cipher73 = + SSLCipherSuite(0x006A, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_AES_256_CBC, + SSL_HASH_SHA256, "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256"); +static const SSLCipherSuite Cipher74 = + SSLCipherSuite(0x006B, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_AES_256_CBC, + SSL_HASH_SHA256, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256"); +static const SSLCipherSuite Cipher75 = + SSLCipherSuite(0x006C, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_AES_128_CBC, + SSL_HASH_SHA256, "TLS_DH_anon_WITH_AES_128_CBC_SHA256"); +static const SSLCipherSuite Cipher76 = + SSLCipherSuite(0x006D, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_AES_256_CBC, + SSL_HASH_SHA256, "TLS_DH_anon_WITH_AES_256_CBC_SHA256"); +static const SSLCipherSuite Cipher77 = + SSLCipherSuite(0x0084, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_256_CBC, + SSL_HASH_SHA, "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA"); +static const SSLCipherSuite Cipher78 = + SSLCipherSuite(0x0085, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_CAMELLIA_256_CBC, + SSL_HASH_SHA, "TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA"); +static const SSLCipherSuite Cipher79 = + SSLCipherSuite(0x0086, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_256_CBC, + SSL_HASH_SHA, "TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA"); +static const SSLCipherSuite Cipher80 = + SSLCipherSuite(0x0087, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_CAMELLIA_256_CBC, + SSL_HASH_SHA, "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA"); +static const SSLCipherSuite Cipher81 = + SSLCipherSuite(0x0088, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_256_CBC, + SSL_HASH_SHA, "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA"); +static const SSLCipherSuite Cipher82 = + SSLCipherSuite(0x0089, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_CAMELLIA_256_CBC, + SSL_HASH_SHA, "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA"); +static const SSLCipherSuite Cipher83 = + SSLCipherSuite(0x008A, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_RC4_128, + SSL_HASH_SHA, "TLS_PSK_WITH_RC4_128_SHA"); +static const SSLCipherSuite Cipher84 = + SSLCipherSuite(0x008B, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_3DES_EDE_CBC, + SSL_HASH_SHA, "TLS_PSK_WITH_3DES_EDE_CBC_SHA"); +static const SSLCipherSuite Cipher85 = + SSLCipherSuite(0x008C, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_AES_128_CBC, + SSL_HASH_SHA, "TLS_PSK_WITH_AES_128_CBC_SHA"); +static const SSLCipherSuite Cipher86 = + SSLCipherSuite(0x008D, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_AES_256_CBC, + SSL_HASH_SHA, "TLS_PSK_WITH_AES_256_CBC_SHA"); +static const SSLCipherSuite Cipher87 = + SSLCipherSuite(0x008E, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_RC4_128, + SSL_HASH_SHA, "TLS_DHE_PSK_WITH_RC4_128_SHA"); +static const SSLCipherSuite Cipher88 = + SSLCipherSuite(0x008F, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_3DES_EDE_CBC, + SSL_HASH_SHA, "TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA"); +static const SSLCipherSuite Cipher89 = + SSLCipherSuite(0x0090, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_AES_128_CBC, + SSL_HASH_SHA, "TLS_DHE_PSK_WITH_AES_128_CBC_SHA"); +static const SSLCipherSuite Cipher90 = + SSLCipherSuite(0x0091, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_AES_256_CBC, + SSL_HASH_SHA, "TLS_DHE_PSK_WITH_AES_256_CBC_SHA"); +static const SSLCipherSuite Cipher91 = + SSLCipherSuite(0x0092, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_RC4_128, + SSL_HASH_SHA, "TLS_RSA_PSK_WITH_RC4_128_SHA"); +static const SSLCipherSuite Cipher92 = + SSLCipherSuite(0x0093, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_3DES_EDE_CBC, + SSL_HASH_SHA, "TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA"); +static const SSLCipherSuite Cipher93 = + SSLCipherSuite(0x0094, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_AES_128_CBC, + SSL_HASH_SHA, "TLS_RSA_PSK_WITH_AES_128_CBC_SHA"); +static const SSLCipherSuite Cipher94 = + SSLCipherSuite(0x0095, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_AES_256_CBC, + SSL_HASH_SHA, "TLS_RSA_PSK_WITH_AES_256_CBC_SHA"); +static const SSLCipherSuite Cipher95 = + SSLCipherSuite(0x0096, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_SEED_CBC, + SSL_HASH_SHA, "TLS_RSA_WITH_SEED_CBC_SHA"); +static const SSLCipherSuite Cipher96 = + SSLCipherSuite(0x0097, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_SEED_CBC, + SSL_HASH_SHA, "TLS_DH_DSS_WITH_SEED_CBC_SHA"); +static const SSLCipherSuite Cipher97 = + SSLCipherSuite(0x0098, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_SEED_CBC, + SSL_HASH_SHA, "TLS_DH_RSA_WITH_SEED_CBC_SHA"); +static const SSLCipherSuite Cipher98 = + SSLCipherSuite(0x0099, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_SEED_CBC, + SSL_HASH_SHA, "TLS_DHE_DSS_WITH_SEED_CBC_SHA"); +static const SSLCipherSuite Cipher99 = + SSLCipherSuite(0x009A, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_SEED_CBC, + SSL_HASH_SHA, "TLS_DHE_RSA_WITH_SEED_CBC_SHA"); +static const SSLCipherSuite Cipher100 = + SSLCipherSuite(0x009B, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_SEED_CBC, + SSL_HASH_SHA, "TLS_DH_anon_WITH_SEED_CBC_SHA"); +static const SSLCipherSuite Cipher101 = + SSLCipherSuite(0x009C, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_AES_128_GCM, + SSL_HASH_SHA256, "TLS_RSA_WITH_AES_128_GCM_SHA256"); +static const SSLCipherSuite Cipher102 = + SSLCipherSuite(0x009D, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_AES_256_GCM, + SSL_HASH_SHA384, "TLS_RSA_WITH_AES_256_GCM_SHA384"); +static const SSLCipherSuite Cipher103 = + SSLCipherSuite(0x009E, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_AES_128_GCM, + SSL_HASH_SHA256, "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256"); +static const SSLCipherSuite Cipher104 = + SSLCipherSuite(0x009F, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_AES_256_GCM, + SSL_HASH_SHA384, "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384"); +static const SSLCipherSuite Cipher105 = + SSLCipherSuite(0x00A0, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_AES_128_GCM, + SSL_HASH_SHA256, "TLS_DH_RSA_WITH_AES_128_GCM_SHA256"); +static const SSLCipherSuite Cipher106 = + SSLCipherSuite(0x00A1, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_AES_256_GCM, + SSL_HASH_SHA384, "TLS_DH_RSA_WITH_AES_256_GCM_SHA384"); +static const SSLCipherSuite Cipher107 = + SSLCipherSuite(0x00A2, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_AES_128_GCM, + SSL_HASH_SHA256, "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256"); +static const SSLCipherSuite Cipher108 = + SSLCipherSuite(0x00A3, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_AES_256_GCM, + SSL_HASH_SHA384, "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384"); +static const SSLCipherSuite Cipher109 = + SSLCipherSuite(0x00A4, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_AES_128_GCM, + SSL_HASH_SHA256, "TLS_DH_DSS_WITH_AES_128_GCM_SHA256"); +static const SSLCipherSuite Cipher110 = + SSLCipherSuite(0x00A5, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_AES_256_GCM, + SSL_HASH_SHA384, "TLS_DH_DSS_WITH_AES_256_GCM_SHA384"); +static const SSLCipherSuite Cipher111 = + SSLCipherSuite(0x00A6, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_AES_128_GCM, + SSL_HASH_SHA256, "TLS_DH_anon_WITH_AES_128_GCM_SHA256"); +static const SSLCipherSuite Cipher112 = + SSLCipherSuite(0x00A7, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_AES_256_GCM, + SSL_HASH_SHA384, "TLS_DH_anon_WITH_AES_256_GCM_SHA384"); +static const SSLCipherSuite Cipher113 = + SSLCipherSuite(0x00A8, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_AES_128_GCM, + SSL_HASH_SHA256, "TLS_PSK_WITH_AES_128_GCM_SHA256"); +static const SSLCipherSuite Cipher114 = + SSLCipherSuite(0x00A9, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_AES_256_GCM, + SSL_HASH_SHA384, "TLS_PSK_WITH_AES_256_GCM_SHA384"); +static const SSLCipherSuite Cipher115 = + SSLCipherSuite(0x00AA, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_AES_128_GCM, + SSL_HASH_SHA256, "TLS_DHE_PSK_WITH_AES_128_GCM_SHA256"); +static const SSLCipherSuite Cipher116 = + SSLCipherSuite(0x00AB, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_AES_256_GCM, + SSL_HASH_SHA384, "TLS_DHE_PSK_WITH_AES_256_GCM_SHA384"); +static const SSLCipherSuite Cipher117 = + SSLCipherSuite(0x00AC, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_AES_128_GCM, + SSL_HASH_SHA256, "TLS_RSA_PSK_WITH_AES_128_GCM_SHA256"); +static const SSLCipherSuite Cipher118 = + SSLCipherSuite(0x00AD, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_AES_256_GCM, + SSL_HASH_SHA384, "TLS_RSA_PSK_WITH_AES_256_GCM_SHA384"); +static const SSLCipherSuite Cipher119 = + SSLCipherSuite(0x00AE, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_AES_128_CBC, + SSL_HASH_SHA256, "TLS_PSK_WITH_AES_128_CBC_SHA256"); +static const SSLCipherSuite Cipher120 = + SSLCipherSuite(0x00AF, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_AES_256_CBC, + SSL_HASH_SHA384, "TLS_PSK_WITH_AES_256_CBC_SHA384"); +static const SSLCipherSuite Cipher121 = + SSLCipherSuite(0x00B0, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_NULL, + SSL_HASH_SHA256, "TLS_PSK_WITH_NULL_SHA256"); +static const SSLCipherSuite Cipher122 = + SSLCipherSuite(0x00B1, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_NULL, + SSL_HASH_SHA384, "TLS_PSK_WITH_NULL_SHA384"); +static const SSLCipherSuite Cipher123 = + SSLCipherSuite(0x00B2, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_AES_128_CBC, + SSL_HASH_SHA256, "TLS_DHE_PSK_WITH_AES_128_CBC_SHA256"); +static const SSLCipherSuite Cipher124 = + SSLCipherSuite(0x00B3, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_AES_256_CBC, + SSL_HASH_SHA384, "TLS_DHE_PSK_WITH_AES_256_CBC_SHA384"); +static const SSLCipherSuite Cipher125 = + SSLCipherSuite(0x00B4, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_NULL, + SSL_HASH_SHA256, "TLS_DHE_PSK_WITH_NULL_SHA256"); +static const SSLCipherSuite Cipher126 = + SSLCipherSuite(0x00B5, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_NULL, + SSL_HASH_SHA384, "TLS_DHE_PSK_WITH_NULL_SHA384"); +static const SSLCipherSuite Cipher127 = + SSLCipherSuite(0x00B6, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_AES_128_CBC, + SSL_HASH_SHA256, "TLS_RSA_PSK_WITH_AES_128_CBC_SHA256"); +static const SSLCipherSuite Cipher128 = + SSLCipherSuite(0x00B7, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_AES_256_CBC, + SSL_HASH_SHA384, "TLS_RSA_PSK_WITH_AES_256_CBC_SHA384"); +static const SSLCipherSuite Cipher129 = + SSLCipherSuite(0x00B8, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_NULL, + SSL_HASH_SHA256, "TLS_RSA_PSK_WITH_NULL_SHA256"); +static const SSLCipherSuite Cipher130 = + SSLCipherSuite(0x00B9, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_NULL, + SSL_HASH_SHA384, "TLS_RSA_PSK_WITH_NULL_SHA384"); +static const SSLCipherSuite Cipher131 = + SSLCipherSuite(0x00BA, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_128_CBC, + SSL_HASH_SHA256, "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256"); +static const SSLCipherSuite Cipher132 = + SSLCipherSuite(0x00BB, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_CAMELLIA_128_CBC, + SSL_HASH_SHA256, "TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256"); +static const SSLCipherSuite Cipher133 = + SSLCipherSuite(0x00BC, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_128_CBC, + SSL_HASH_SHA256, "TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256"); +static const SSLCipherSuite Cipher134 = + SSLCipherSuite(0x00BD, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_CAMELLIA_128_CBC, + SSL_HASH_SHA256, "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256"); +static const SSLCipherSuite Cipher135 = + SSLCipherSuite(0x00BE, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_128_CBC, + SSL_HASH_SHA256, "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256"); +static const SSLCipherSuite Cipher136 = + SSLCipherSuite(0x00BF, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_CAMELLIA_128_CBC, + SSL_HASH_SHA256, "TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256"); +static const SSLCipherSuite Cipher137 = + SSLCipherSuite(0x00C0, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_256_CBC, + SSL_HASH_SHA256, "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256"); +static const SSLCipherSuite Cipher138 = + SSLCipherSuite(0x00C1, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_CAMELLIA_256_CBC, + SSL_HASH_SHA256, "TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256"); +static const SSLCipherSuite Cipher139 = + SSLCipherSuite(0x00C2, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_256_CBC, + SSL_HASH_SHA256, "TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256"); +static const SSLCipherSuite Cipher140 = + SSLCipherSuite(0x00C3, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_CAMELLIA_256_CBC, + SSL_HASH_SHA256, "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256"); +static const SSLCipherSuite Cipher141 = + SSLCipherSuite(0x00C4, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_256_CBC, + SSL_HASH_SHA256, "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256"); +static const SSLCipherSuite Cipher142 = + SSLCipherSuite(0x00C5, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_CAMELLIA_256_CBC, + SSL_HASH_SHA256, "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256"); +static const SSLCipherSuite Cipher143 = + SSLCipherSuite(0xC001, SSL_KEYX_ECDH, SSL_AUTH_ECDSA, SSL_SYM_NULL, + SSL_HASH_SHA, "TLS_ECDH_ECDSA_WITH_NULL_SHA"); +static const SSLCipherSuite Cipher144 = + SSLCipherSuite(0xC002, SSL_KEYX_ECDH, SSL_AUTH_ECDSA, SSL_SYM_RC4_128, + SSL_HASH_SHA, "TLS_ECDH_ECDSA_WITH_RC4_128_SHA"); +static const SSLCipherSuite Cipher145 = + SSLCipherSuite(0xC003, SSL_KEYX_ECDH, SSL_AUTH_ECDSA, SSL_SYM_3DES_EDE_CBC, + SSL_HASH_SHA, "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA"); +static const SSLCipherSuite Cipher146 = + SSLCipherSuite(0xC004, SSL_KEYX_ECDH, SSL_AUTH_ECDSA, SSL_SYM_AES_128_CBC, + SSL_HASH_SHA, "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA"); +static const SSLCipherSuite Cipher147 = + SSLCipherSuite(0xC005, SSL_KEYX_ECDH, SSL_AUTH_ECDSA, SSL_SYM_AES_256_CBC, + SSL_HASH_SHA, "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA"); +static const SSLCipherSuite Cipher148 = + SSLCipherSuite(0xC006, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_NULL, + SSL_HASH_SHA, "TLS_ECDHE_ECDSA_WITH_NULL_SHA"); +static const SSLCipherSuite Cipher149 = + SSLCipherSuite(0xC007, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_RC4_128, + SSL_HASH_SHA, "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA"); +static const SSLCipherSuite Cipher150 = + SSLCipherSuite(0xC008, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_3DES_EDE_CBC, + SSL_HASH_SHA, "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA"); +static const SSLCipherSuite Cipher151 = + SSLCipherSuite(0xC009, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_AES_128_CBC, + SSL_HASH_SHA, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA"); +static const SSLCipherSuite Cipher152 = + SSLCipherSuite(0xC00A, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_AES_256_CBC, + SSL_HASH_SHA, "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA"); +static const SSLCipherSuite Cipher153 = + SSLCipherSuite(0xC00B, SSL_KEYX_ECDH, SSL_AUTH_RSA, SSL_SYM_NULL, + SSL_HASH_SHA, "TLS_ECDH_RSA_WITH_NULL_SHA"); +static const SSLCipherSuite Cipher154 = + SSLCipherSuite(0xC00C, SSL_KEYX_ECDH, SSL_AUTH_RSA, SSL_SYM_RC4_128, + SSL_HASH_SHA, "TLS_ECDH_RSA_WITH_RC4_128_SHA"); +static const SSLCipherSuite Cipher155 = + SSLCipherSuite(0xC00D, SSL_KEYX_ECDH, SSL_AUTH_RSA, SSL_SYM_3DES_EDE_CBC, + SSL_HASH_SHA, "TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA"); +static const SSLCipherSuite Cipher156 = + SSLCipherSuite(0xC00E, SSL_KEYX_ECDH, SSL_AUTH_RSA, SSL_SYM_AES_128_CBC, + SSL_HASH_SHA, "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA"); +static const SSLCipherSuite Cipher157 = + SSLCipherSuite(0xC00F, SSL_KEYX_ECDH, SSL_AUTH_RSA, SSL_SYM_AES_256_CBC, + SSL_HASH_SHA, "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA"); +static const SSLCipherSuite Cipher158 = + SSLCipherSuite(0xC010, SSL_KEYX_ECDHE, SSL_AUTH_RSA, SSL_SYM_NULL, + SSL_HASH_SHA, "TLS_ECDHE_RSA_WITH_NULL_SHA"); +static const SSLCipherSuite Cipher159 = + SSLCipherSuite(0xC011, SSL_KEYX_ECDHE, SSL_AUTH_RSA, SSL_SYM_RC4_128, + SSL_HASH_SHA, "TLS_ECDHE_RSA_WITH_RC4_128_SHA"); +static const SSLCipherSuite Cipher160 = + SSLCipherSuite(0xC012, SSL_KEYX_ECDHE, SSL_AUTH_RSA, SSL_SYM_3DES_EDE_CBC, + SSL_HASH_SHA, "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA"); +static const SSLCipherSuite Cipher161 = + SSLCipherSuite(0xC013, SSL_KEYX_ECDHE, SSL_AUTH_RSA, SSL_SYM_AES_128_CBC, + SSL_HASH_SHA, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"); +static const SSLCipherSuite Cipher162 = + SSLCipherSuite(0xC014, SSL_KEYX_ECDHE, SSL_AUTH_RSA, SSL_SYM_AES_256_CBC, + SSL_HASH_SHA, "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA"); +static const SSLCipherSuite Cipher163 = + SSLCipherSuite(0xC015, SSL_KEYX_ECDH, SSL_AUTH_anon, SSL_SYM_NULL, + SSL_HASH_SHA, "TLS_ECDH_anon_WITH_NULL_SHA"); +static const SSLCipherSuite Cipher164 = + SSLCipherSuite(0xC016, SSL_KEYX_ECDH, SSL_AUTH_anon, SSL_SYM_RC4_128, + SSL_HASH_SHA, "TLS_ECDH_anon_WITH_RC4_128_SHA"); +static const SSLCipherSuite Cipher165 = + SSLCipherSuite(0xC017, SSL_KEYX_ECDH, SSL_AUTH_anon, SSL_SYM_3DES_EDE_CBC, + SSL_HASH_SHA, "TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA"); +static const SSLCipherSuite Cipher166 = + SSLCipherSuite(0xC018, SSL_KEYX_ECDH, SSL_AUTH_anon, SSL_SYM_AES_128_CBC, + SSL_HASH_SHA, "TLS_ECDH_anon_WITH_AES_128_CBC_SHA"); +static const SSLCipherSuite Cipher167 = + SSLCipherSuite(0xC019, SSL_KEYX_ECDH, SSL_AUTH_anon, SSL_SYM_AES_256_CBC, + SSL_HASH_SHA, "TLS_ECDH_anon_WITH_AES_256_CBC_SHA"); +static const SSLCipherSuite Cipher168 = + SSLCipherSuite(0xC01A, SSL_KEYX_SRP, SSL_AUTH_SHA, SSL_SYM_3DES_EDE_CBC, + SSL_HASH_SHA, "TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA"); +static const SSLCipherSuite Cipher169 = + SSLCipherSuite(0xC01B, SSL_KEYX_SRP, SSL_AUTH_RSA, SSL_SYM_3DES_EDE_CBC, + SSL_HASH_SHA, "TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA"); +static const SSLCipherSuite Cipher170 = + SSLCipherSuite(0xC01C, SSL_KEYX_SRP, SSL_AUTH_DSS, SSL_SYM_3DES_EDE_CBC, + SSL_HASH_SHA, "TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA"); +static const SSLCipherSuite Cipher171 = + SSLCipherSuite(0xC01D, SSL_KEYX_SRP, SSL_AUTH_SHA, SSL_SYM_AES_128_CBC, + SSL_HASH_SHA, "TLS_SRP_SHA_WITH_AES_128_CBC_SHA"); +static const SSLCipherSuite Cipher172 = + SSLCipherSuite(0xC01E, SSL_KEYX_SRP, SSL_AUTH_RSA, SSL_SYM_AES_128_CBC, + SSL_HASH_SHA, "TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA"); +static const SSLCipherSuite Cipher173 = + SSLCipherSuite(0xC01F, SSL_KEYX_SRP, SSL_AUTH_DSS, SSL_SYM_AES_128_CBC, + SSL_HASH_SHA, "TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA"); +static const SSLCipherSuite Cipher174 = + SSLCipherSuite(0xC020, SSL_KEYX_SRP, SSL_AUTH_SHA, SSL_SYM_AES_256_CBC, + SSL_HASH_SHA, "TLS_SRP_SHA_WITH_AES_256_CBC_SHA"); +static const SSLCipherSuite Cipher175 = + SSLCipherSuite(0xC021, SSL_KEYX_SRP, SSL_AUTH_RSA, SSL_SYM_AES_256_CBC, + SSL_HASH_SHA, "TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA"); +static const SSLCipherSuite Cipher176 = + SSLCipherSuite(0xC022, SSL_KEYX_SRP, SSL_AUTH_DSS, SSL_SYM_AES_256_CBC, + SSL_HASH_SHA, "TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA"); +static const SSLCipherSuite Cipher177 = + SSLCipherSuite(0xC023, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_AES_128_CBC, + SSL_HASH_SHA256, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256"); +static const SSLCipherSuite Cipher178 = + SSLCipherSuite(0xC024, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_AES_256_CBC, + SSL_HASH_SHA384, "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384"); +static const SSLCipherSuite Cipher179 = + SSLCipherSuite(0xC025, SSL_KEYX_ECDH, SSL_AUTH_ECDSA, SSL_SYM_AES_128_CBC, + SSL_HASH_SHA256, "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256"); +static const SSLCipherSuite Cipher180 = + SSLCipherSuite(0xC026, SSL_KEYX_ECDH, SSL_AUTH_ECDSA, SSL_SYM_AES_256_CBC, + SSL_HASH_SHA384, "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384"); +static const SSLCipherSuite Cipher181 = + SSLCipherSuite(0xC027, SSL_KEYX_ECDHE, SSL_AUTH_RSA, SSL_SYM_AES_128_CBC, + SSL_HASH_SHA256, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"); +static const SSLCipherSuite Cipher182 = + SSLCipherSuite(0xC028, SSL_KEYX_ECDHE, SSL_AUTH_RSA, SSL_SYM_AES_256_CBC, + SSL_HASH_SHA384, "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384"); +static const SSLCipherSuite Cipher183 = + SSLCipherSuite(0xC029, SSL_KEYX_ECDH, SSL_AUTH_RSA, SSL_SYM_AES_128_CBC, + SSL_HASH_SHA256, "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256"); +static const SSLCipherSuite Cipher184 = + SSLCipherSuite(0xC02A, SSL_KEYX_ECDH, SSL_AUTH_RSA, SSL_SYM_AES_256_CBC, + SSL_HASH_SHA384, "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384"); +static const SSLCipherSuite Cipher185 = + SSLCipherSuite(0xC02B, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_AES_128_GCM, + SSL_HASH_SHA256, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"); +static const SSLCipherSuite Cipher186 = + SSLCipherSuite(0xC02C, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_AES_256_GCM, + SSL_HASH_SHA384, "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"); +static const SSLCipherSuite Cipher187 = + SSLCipherSuite(0xC02D, SSL_KEYX_ECDH, SSL_AUTH_ECDSA, SSL_SYM_AES_128_GCM, + SSL_HASH_SHA256, "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256"); +static const SSLCipherSuite Cipher188 = + SSLCipherSuite(0xC02E, SSL_KEYX_ECDH, SSL_AUTH_ECDSA, SSL_SYM_AES_256_GCM, + SSL_HASH_SHA384, "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384"); +static const SSLCipherSuite Cipher189 = + SSLCipherSuite(0xC02F, SSL_KEYX_ECDHE, SSL_AUTH_RSA, SSL_SYM_AES_128_GCM, + SSL_HASH_SHA256, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"); +static const SSLCipherSuite Cipher190 = + SSLCipherSuite(0xC030, SSL_KEYX_ECDHE, SSL_AUTH_RSA, SSL_SYM_AES_256_GCM, + SSL_HASH_SHA384, "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"); +static const SSLCipherSuite Cipher191 = + SSLCipherSuite(0xC031, SSL_KEYX_ECDH, SSL_AUTH_RSA, SSL_SYM_AES_128_GCM, + SSL_HASH_SHA256, "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256"); +static const SSLCipherSuite Cipher192 = + SSLCipherSuite(0xC032, SSL_KEYX_ECDH, SSL_AUTH_RSA, SSL_SYM_AES_256_GCM, + SSL_HASH_SHA384, "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384"); +static const SSLCipherSuite Cipher193 = + SSLCipherSuite(0xC033, SSL_KEYX_ECDHE, SSL_AUTH_PSK, SSL_SYM_RC4_128, + SSL_HASH_SHA, "TLS_ECDHE_PSK_WITH_RC4_128_SHA"); +static const SSLCipherSuite Cipher194 = + SSLCipherSuite(0xC034, SSL_KEYX_ECDHE, SSL_AUTH_PSK, SSL_SYM_3DES_EDE_CBC, + SSL_HASH_SHA, "TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA"); +static const SSLCipherSuite Cipher195 = + SSLCipherSuite(0xC035, SSL_KEYX_ECDHE, SSL_AUTH_PSK, SSL_SYM_AES_128_CBC, + SSL_HASH_SHA, "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA"); +static const SSLCipherSuite Cipher196 = + SSLCipherSuite(0xC036, SSL_KEYX_ECDHE, SSL_AUTH_PSK, SSL_SYM_AES_256_CBC, + SSL_HASH_SHA, "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA"); +static const SSLCipherSuite Cipher197 = + SSLCipherSuite(0xC037, SSL_KEYX_ECDHE, SSL_AUTH_PSK, SSL_SYM_AES_128_CBC, + SSL_HASH_SHA256, "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256"); +static const SSLCipherSuite Cipher198 = + SSLCipherSuite(0xC038, SSL_KEYX_ECDHE, SSL_AUTH_PSK, SSL_SYM_AES_256_CBC, + SSL_HASH_SHA384, "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384"); +static const SSLCipherSuite Cipher199 = + SSLCipherSuite(0xC039, SSL_KEYX_ECDHE, SSL_AUTH_PSK, SSL_SYM_NULL, + SSL_HASH_SHA, "TLS_ECDHE_PSK_WITH_NULL_SHA"); +static const SSLCipherSuite Cipher200 = + SSLCipherSuite(0xC03A, SSL_KEYX_ECDHE, SSL_AUTH_PSK, SSL_SYM_NULL, + SSL_HASH_SHA256, "TLS_ECDHE_PSK_WITH_NULL_SHA256"); +static const SSLCipherSuite Cipher201 = + SSLCipherSuite(0xC03B, SSL_KEYX_ECDHE, SSL_AUTH_PSK, SSL_SYM_NULL, + SSL_HASH_SHA384, "TLS_ECDHE_PSK_WITH_NULL_SHA384"); +static const SSLCipherSuite Cipher202 = + SSLCipherSuite(0xC03C, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_ARIA_128_CBC, + SSL_HASH_SHA256, "TLS_RSA_WITH_ARIA_128_CBC_SHA256"); +static const SSLCipherSuite Cipher203 = + SSLCipherSuite(0xC03D, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_ARIA_256_CBC, + SSL_HASH_SHA384, "TLS_RSA_WITH_ARIA_256_CBC_SHA384"); +static const SSLCipherSuite Cipher204 = + SSLCipherSuite(0xC03E, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_ARIA_128_CBC, + SSL_HASH_SHA256, "TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256"); +static const SSLCipherSuite Cipher205 = + SSLCipherSuite(0xC03F, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_ARIA_256_CBC, + SSL_HASH_SHA384, "TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384"); +static const SSLCipherSuite Cipher206 = + SSLCipherSuite(0xC040, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_ARIA_128_CBC, + SSL_HASH_SHA256, "TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256"); +static const SSLCipherSuite Cipher207 = + SSLCipherSuite(0xC041, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_ARIA_256_CBC, + SSL_HASH_SHA384, "TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384"); +static const SSLCipherSuite Cipher208 = + SSLCipherSuite(0xC042, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_ARIA_128_CBC, + SSL_HASH_SHA256, "TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256"); +static const SSLCipherSuite Cipher209 = + SSLCipherSuite(0xC043, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_ARIA_256_CBC, + SSL_HASH_SHA384, "TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384"); +static const SSLCipherSuite Cipher210 = + SSLCipherSuite(0xC044, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_ARIA_128_CBC, + SSL_HASH_SHA256, "TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256"); +static const SSLCipherSuite Cipher211 = + SSLCipherSuite(0xC045, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_ARIA_256_CBC, + SSL_HASH_SHA384, "TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384"); +static const SSLCipherSuite Cipher212 = + SSLCipherSuite(0xC046, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_ARIA_128_CBC, + SSL_HASH_SHA256, "TLS_DH_anon_WITH_ARIA_128_CBC_SHA256"); +static const SSLCipherSuite Cipher213 = + SSLCipherSuite(0xC047, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_ARIA_256_CBC, + SSL_HASH_SHA384, "TLS_DH_anon_WITH_ARIA_256_CBC_SHA384"); +static const SSLCipherSuite Cipher214 = + SSLCipherSuite(0xC048, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_ARIA_128_CBC, + SSL_HASH_SHA256, "TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256"); +static const SSLCipherSuite Cipher215 = + SSLCipherSuite(0xC049, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_ARIA_256_CBC, + SSL_HASH_SHA384, "TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384"); +static const SSLCipherSuite Cipher216 = + SSLCipherSuite(0xC04A, SSL_KEYX_ECDH, SSL_AUTH_ECDSA, SSL_SYM_ARIA_128_CBC, + SSL_HASH_SHA256, "TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256"); +static const SSLCipherSuite Cipher217 = + SSLCipherSuite(0xC04B, SSL_KEYX_ECDH, SSL_AUTH_ECDSA, SSL_SYM_ARIA_256_CBC, + SSL_HASH_SHA384, "TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384"); +static const SSLCipherSuite Cipher218 = + SSLCipherSuite(0xC04C, SSL_KEYX_ECDHE, SSL_AUTH_RSA, SSL_SYM_ARIA_128_CBC, + SSL_HASH_SHA256, "TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256"); +static const SSLCipherSuite Cipher219 = + SSLCipherSuite(0xC04D, SSL_KEYX_ECDHE, SSL_AUTH_RSA, SSL_SYM_ARIA_256_CBC, + SSL_HASH_SHA384, "TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384"); +static const SSLCipherSuite Cipher220 = + SSLCipherSuite(0xC04E, SSL_KEYX_ECDH, SSL_AUTH_RSA, SSL_SYM_ARIA_128_CBC, + SSL_HASH_SHA256, "TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256"); +static const SSLCipherSuite Cipher221 = + SSLCipherSuite(0xC04F, SSL_KEYX_ECDH, SSL_AUTH_RSA, SSL_SYM_ARIA_256_CBC, + SSL_HASH_SHA384, "TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384"); +static const SSLCipherSuite Cipher222 = + SSLCipherSuite(0xC050, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_ARIA_128_GCM, + SSL_HASH_SHA256, "TLS_RSA_WITH_ARIA_128_GCM_SHA256"); +static const SSLCipherSuite Cipher223 = + SSLCipherSuite(0xC051, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_ARIA_256_GCM, + SSL_HASH_SHA384, "TLS_RSA_WITH_ARIA_256_GCM_SHA384"); +static const SSLCipherSuite Cipher224 = + SSLCipherSuite(0xC052, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_ARIA_128_GCM, + SSL_HASH_SHA256, "TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256"); +static const SSLCipherSuite Cipher225 = + SSLCipherSuite(0xC053, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_ARIA_256_GCM, + SSL_HASH_SHA384, "TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384"); +static const SSLCipherSuite Cipher226 = + SSLCipherSuite(0xC054, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_ARIA_128_GCM, + SSL_HASH_SHA256, "TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256"); +static const SSLCipherSuite Cipher227 = + SSLCipherSuite(0xC055, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_ARIA_256_GCM, + SSL_HASH_SHA384, "TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384"); +static const SSLCipherSuite Cipher228 = + SSLCipherSuite(0xC056, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_ARIA_128_GCM, + SSL_HASH_SHA256, "TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256"); +static const SSLCipherSuite Cipher229 = + SSLCipherSuite(0xC057, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_ARIA_256_GCM, + SSL_HASH_SHA384, "TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384"); +static const SSLCipherSuite Cipher230 = + SSLCipherSuite(0xC058, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_ARIA_128_GCM, + SSL_HASH_SHA256, "TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256"); +static const SSLCipherSuite Cipher231 = + SSLCipherSuite(0xC059, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_ARIA_256_GCM, + SSL_HASH_SHA384, "TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384"); +static const SSLCipherSuite Cipher232 = + SSLCipherSuite(0xC05A, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_ARIA_128_GCM, + SSL_HASH_SHA256, "TLS_DH_anon_WITH_ARIA_128_GCM_SHA256"); +static const SSLCipherSuite Cipher233 = + SSLCipherSuite(0xC05B, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_ARIA_256_GCM, + SSL_HASH_SHA384, "TLS_DH_anon_WITH_ARIA_256_GCM_SHA384"); +static const SSLCipherSuite Cipher234 = + SSLCipherSuite(0xC05C, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_ARIA_128_GCM, + SSL_HASH_SHA256, "TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256"); +static const SSLCipherSuite Cipher235 = + SSLCipherSuite(0xC05D, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_ARIA_256_GCM, + SSL_HASH_SHA384, "TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384"); +static const SSLCipherSuite Cipher236 = + SSLCipherSuite(0xC05E, SSL_KEYX_ECDH, SSL_AUTH_ECDSA, SSL_SYM_ARIA_128_GCM, + SSL_HASH_SHA256, "TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256"); +static const SSLCipherSuite Cipher237 = + SSLCipherSuite(0xC05F, SSL_KEYX_ECDH, SSL_AUTH_ECDSA, SSL_SYM_ARIA_256_GCM, + SSL_HASH_SHA384, "TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384"); +static const SSLCipherSuite Cipher238 = + SSLCipherSuite(0xC060, SSL_KEYX_ECDHE, SSL_AUTH_RSA, SSL_SYM_ARIA_128_GCM, + SSL_HASH_SHA256, "TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256"); +static const SSLCipherSuite Cipher239 = + SSLCipherSuite(0xC061, SSL_KEYX_ECDHE, SSL_AUTH_RSA, SSL_SYM_ARIA_256_GCM, + SSL_HASH_SHA384, "TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384"); +static const SSLCipherSuite Cipher240 = + SSLCipherSuite(0xC062, SSL_KEYX_ECDH, SSL_AUTH_RSA, SSL_SYM_ARIA_128_GCM, + SSL_HASH_SHA256, "TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256"); +static const SSLCipherSuite Cipher241 = + SSLCipherSuite(0xC063, SSL_KEYX_ECDH, SSL_AUTH_RSA, SSL_SYM_ARIA_256_GCM, + SSL_HASH_SHA384, "TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384"); +static const SSLCipherSuite Cipher242 = + SSLCipherSuite(0xC064, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_ARIA_128_CBC, + SSL_HASH_SHA256, "TLS_PSK_WITH_ARIA_128_CBC_SHA256"); +static const SSLCipherSuite Cipher243 = + SSLCipherSuite(0xC065, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_ARIA_256_CBC, + SSL_HASH_SHA384, "TLS_PSK_WITH_ARIA_256_CBC_SHA384"); +static const SSLCipherSuite Cipher244 = + SSLCipherSuite(0xC066, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_ARIA_128_CBC, + SSL_HASH_SHA256, "TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256"); +static const SSLCipherSuite Cipher245 = + SSLCipherSuite(0xC067, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_ARIA_256_CBC, + SSL_HASH_SHA384, "TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384"); +static const SSLCipherSuite Cipher246 = + SSLCipherSuite(0xC068, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_ARIA_128_CBC, + SSL_HASH_SHA256, "TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256"); +static const SSLCipherSuite Cipher247 = + SSLCipherSuite(0xC069, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_ARIA_256_CBC, + SSL_HASH_SHA384, "TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384"); +static const SSLCipherSuite Cipher248 = + SSLCipherSuite(0xC06A, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_ARIA_128_GCM, + SSL_HASH_SHA256, "TLS_PSK_WITH_ARIA_128_GCM_SHA256"); +static const SSLCipherSuite Cipher249 = + SSLCipherSuite(0xC06B, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_ARIA_256_GCM, + SSL_HASH_SHA384, "TLS_PSK_WITH_ARIA_256_GCM_SHA384"); +static const SSLCipherSuite Cipher250 = + SSLCipherSuite(0xC06C, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_ARIA_128_GCM, + SSL_HASH_SHA256, "TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256"); +static const SSLCipherSuite Cipher251 = + SSLCipherSuite(0xC06D, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_ARIA_256_GCM, + SSL_HASH_SHA384, "TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384"); +static const SSLCipherSuite Cipher252 = + SSLCipherSuite(0xC06E, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_ARIA_128_GCM, + SSL_HASH_SHA256, "TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256"); +static const SSLCipherSuite Cipher253 = + SSLCipherSuite(0xC06F, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_ARIA_256_GCM, + SSL_HASH_SHA384, "TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384"); +static const SSLCipherSuite Cipher254 = + SSLCipherSuite(0xC070, SSL_KEYX_ECDHE, SSL_AUTH_PSK, SSL_SYM_ARIA_128_CBC, + SSL_HASH_SHA256, "TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256"); +static const SSLCipherSuite Cipher255 = + SSLCipherSuite(0xC071, SSL_KEYX_ECDHE, SSL_AUTH_PSK, SSL_SYM_ARIA_256_CBC, + SSL_HASH_SHA384, "TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384"); +static const SSLCipherSuite Cipher256 = SSLCipherSuite( + 0xC072, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_CAMELLIA_128_CBC, + SSL_HASH_SHA256, "TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256"); +static const SSLCipherSuite Cipher257 = SSLCipherSuite( + 0xC073, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_CAMELLIA_256_CBC, + SSL_HASH_SHA384, "TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384"); +static const SSLCipherSuite Cipher258 = SSLCipherSuite( + 0xC074, SSL_KEYX_ECDH, SSL_AUTH_ECDSA, SSL_SYM_CAMELLIA_128_CBC, + SSL_HASH_SHA256, "TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256"); +static const SSLCipherSuite Cipher259 = SSLCipherSuite( + 0xC075, SSL_KEYX_ECDH, SSL_AUTH_ECDSA, SSL_SYM_CAMELLIA_256_CBC, + SSL_HASH_SHA384, "TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384"); +static const SSLCipherSuite Cipher260 = SSLCipherSuite( + 0xC076, SSL_KEYX_ECDHE, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_128_CBC, + SSL_HASH_SHA256, "TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256"); +static const SSLCipherSuite Cipher261 = SSLCipherSuite( + 0xC077, SSL_KEYX_ECDHE, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_256_CBC, + SSL_HASH_SHA384, "TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384"); +static const SSLCipherSuite Cipher262 = SSLCipherSuite( + 0xC078, SSL_KEYX_ECDH, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_128_CBC, + SSL_HASH_SHA256, "TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256"); +static const SSLCipherSuite Cipher263 = SSLCipherSuite( + 0xC079, SSL_KEYX_ECDH, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_256_CBC, + SSL_HASH_SHA384, "TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384"); +static const SSLCipherSuite Cipher264 = + SSLCipherSuite(0xC07A, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_128_GCM, + SSL_HASH_SHA256, "TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256"); +static const SSLCipherSuite Cipher265 = + SSLCipherSuite(0xC07B, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_256_GCM, + SSL_HASH_SHA384, "TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384"); +static const SSLCipherSuite Cipher266 = + SSLCipherSuite(0xC07C, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_128_GCM, + SSL_HASH_SHA256, "TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256"); +static const SSLCipherSuite Cipher267 = + SSLCipherSuite(0xC07D, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_256_GCM, + SSL_HASH_SHA384, "TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384"); +static const SSLCipherSuite Cipher268 = + SSLCipherSuite(0xC07E, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_128_GCM, + SSL_HASH_SHA256, "TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256"); +static const SSLCipherSuite Cipher269 = + SSLCipherSuite(0xC07F, SSL_KEYX_DH, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_256_GCM, + SSL_HASH_SHA384, "TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384"); +static const SSLCipherSuite Cipher270 = + SSLCipherSuite(0xC080, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_CAMELLIA_128_GCM, + SSL_HASH_SHA256, "TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256"); +static const SSLCipherSuite Cipher271 = + SSLCipherSuite(0xC081, SSL_KEYX_DHE, SSL_AUTH_DSS, SSL_SYM_CAMELLIA_256_GCM, + SSL_HASH_SHA384, "TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384"); +static const SSLCipherSuite Cipher272 = + SSLCipherSuite(0xC082, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_CAMELLIA_128_GCM, + SSL_HASH_SHA256, "TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256"); +static const SSLCipherSuite Cipher273 = + SSLCipherSuite(0xC083, SSL_KEYX_DH, SSL_AUTH_DSS, SSL_SYM_CAMELLIA_256_GCM, + SSL_HASH_SHA384, "TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384"); +static const SSLCipherSuite Cipher274 = + SSLCipherSuite(0xC084, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_CAMELLIA_128_GCM, + SSL_HASH_SHA256, "TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256"); +static const SSLCipherSuite Cipher275 = + SSLCipherSuite(0xC085, SSL_KEYX_DH, SSL_AUTH_anon, SSL_SYM_CAMELLIA_256_GCM, + SSL_HASH_SHA384, "TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384"); +static const SSLCipherSuite Cipher276 = SSLCipherSuite( + 0xC086, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_CAMELLIA_128_GCM, + SSL_HASH_SHA256, "TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256"); +static const SSLCipherSuite Cipher277 = SSLCipherSuite( + 0xC087, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_CAMELLIA_256_GCM, + SSL_HASH_SHA384, "TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384"); +static const SSLCipherSuite Cipher278 = SSLCipherSuite( + 0xC088, SSL_KEYX_ECDH, SSL_AUTH_ECDSA, SSL_SYM_CAMELLIA_128_GCM, + SSL_HASH_SHA256, "TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256"); +static const SSLCipherSuite Cipher279 = SSLCipherSuite( + 0xC089, SSL_KEYX_ECDH, SSL_AUTH_ECDSA, SSL_SYM_CAMELLIA_256_GCM, + SSL_HASH_SHA384, "TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384"); +static const SSLCipherSuite Cipher280 = SSLCipherSuite( + 0xC08A, SSL_KEYX_ECDHE, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_128_GCM, + SSL_HASH_SHA256, "TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256"); +static const SSLCipherSuite Cipher281 = SSLCipherSuite( + 0xC08B, SSL_KEYX_ECDHE, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_256_GCM, + SSL_HASH_SHA384, "TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384"); +static const SSLCipherSuite Cipher282 = SSLCipherSuite( + 0xC08C, SSL_KEYX_ECDH, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_128_GCM, + SSL_HASH_SHA256, "TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256"); +static const SSLCipherSuite Cipher283 = SSLCipherSuite( + 0xC08D, SSL_KEYX_ECDH, SSL_AUTH_RSA, SSL_SYM_CAMELLIA_256_GCM, + SSL_HASH_SHA384, "TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384"); +static const SSLCipherSuite Cipher284 = + SSLCipherSuite(0xC08E, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_CAMELLIA_128_GCM, + SSL_HASH_SHA256, "TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256"); +static const SSLCipherSuite Cipher285 = + SSLCipherSuite(0xC08F, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_CAMELLIA_256_GCM, + SSL_HASH_SHA384, "TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384"); +static const SSLCipherSuite Cipher286 = + SSLCipherSuite(0xC090, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_CAMELLIA_128_GCM, + SSL_HASH_SHA256, "TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256"); +static const SSLCipherSuite Cipher287 = + SSLCipherSuite(0xC091, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_CAMELLIA_256_GCM, + SSL_HASH_SHA384, "TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384"); +static const SSLCipherSuite Cipher288 = + SSLCipherSuite(0xC092, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_CAMELLIA_128_GCM, + SSL_HASH_SHA256, "TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256"); +static const SSLCipherSuite Cipher289 = + SSLCipherSuite(0xC093, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_CAMELLIA_256_GCM, + SSL_HASH_SHA384, "TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384"); +static const SSLCipherSuite Cipher290 = + SSLCipherSuite(0xC094, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_CAMELLIA_128_CBC, + SSL_HASH_SHA256, "TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256"); +static const SSLCipherSuite Cipher291 = + SSLCipherSuite(0xC095, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_CAMELLIA_256_CBC, + SSL_HASH_SHA384, "TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384"); +static const SSLCipherSuite Cipher292 = + SSLCipherSuite(0xC096, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_CAMELLIA_128_CBC, + SSL_HASH_SHA256, "TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256"); +static const SSLCipherSuite Cipher293 = + SSLCipherSuite(0xC097, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_CAMELLIA_256_CBC, + SSL_HASH_SHA384, "TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384"); +static const SSLCipherSuite Cipher294 = + SSLCipherSuite(0xC098, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_CAMELLIA_128_CBC, + SSL_HASH_SHA256, "TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256"); +static const SSLCipherSuite Cipher295 = + SSLCipherSuite(0xC099, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_CAMELLIA_256_CBC, + SSL_HASH_SHA384, "TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384"); +static const SSLCipherSuite Cipher296 = SSLCipherSuite( + 0xC09A, SSL_KEYX_ECDHE, SSL_AUTH_PSK, SSL_SYM_CAMELLIA_128_CBC, + SSL_HASH_SHA256, "TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256"); +static const SSLCipherSuite Cipher297 = SSLCipherSuite( + 0xC09B, SSL_KEYX_ECDHE, SSL_AUTH_PSK, SSL_SYM_CAMELLIA_256_CBC, + SSL_HASH_SHA384, "TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384"); +static const SSLCipherSuite Cipher298 = + SSLCipherSuite(0xC09C, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_AES_128, + SSL_HASH_CCM, "TLS_RSA_WITH_AES_128_CCM"); +static const SSLCipherSuite Cipher299 = + SSLCipherSuite(0xC09D, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_AES_256, + SSL_HASH_CCM, "TLS_RSA_WITH_AES_256_CCM"); +static const SSLCipherSuite Cipher300 = + SSLCipherSuite(0xC09E, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_AES_128, + SSL_HASH_CCM, "TLS_DHE_RSA_WITH_AES_128_CCM"); +static const SSLCipherSuite Cipher301 = + SSLCipherSuite(0xC09F, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_AES_256, + SSL_HASH_CCM, "TLS_DHE_RSA_WITH_AES_256_CCM"); +static const SSLCipherSuite Cipher302 = + SSLCipherSuite(0xC0A0, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_AES_128, + SSL_HASH_CCM_8, "TLS_RSA_WITH_AES_128_CCM_8"); +static const SSLCipherSuite Cipher303 = + SSLCipherSuite(0xC0A1, SSL_KEYX_RSA, SSL_AUTH_RSA, SSL_SYM_AES_256, + SSL_HASH_CCM_8, "TLS_RSA_WITH_AES_256_CCM_8"); +static const SSLCipherSuite Cipher304 = + SSLCipherSuite(0xC0A2, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_AES_128, + SSL_HASH_CCM_8, "TLS_DHE_RSA_WITH_AES_128_CCM_8"); +static const SSLCipherSuite Cipher305 = + SSLCipherSuite(0xC0A3, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_AES_256, + SSL_HASH_CCM_8, "TLS_DHE_RSA_WITH_AES_256_CCM_8"); +static const SSLCipherSuite Cipher306 = + SSLCipherSuite(0xC0A4, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_AES_128, + SSL_HASH_CCM, "TLS_PSK_WITH_AES_128_CCM"); +static const SSLCipherSuite Cipher307 = + SSLCipherSuite(0xC0A5, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_AES_256, + SSL_HASH_CCM, "TLS_PSK_WITH_AES_256_CCM"); +static const SSLCipherSuite Cipher308 = + SSLCipherSuite(0xC0A6, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_AES_128, + SSL_HASH_CCM, "TLS_DHE_PSK_WITH_AES_128_CCM"); +static const SSLCipherSuite Cipher309 = + SSLCipherSuite(0xC0A7, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_AES_256, + SSL_HASH_CCM, "TLS_DHE_PSK_WITH_AES_256_CCM"); +static const SSLCipherSuite Cipher310 = + SSLCipherSuite(0xC0A8, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_AES_128, + SSL_HASH_CCM_8, "TLS_PSK_WITH_AES_128_CCM_8"); +static const SSLCipherSuite Cipher311 = + SSLCipherSuite(0xC0A9, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_AES_256, + SSL_HASH_CCM_8, "TLS_PSK_WITH_AES_256_CCM_8"); +static const SSLCipherSuite Cipher312 = + SSLCipherSuite(0xC0AA, SSL_KEYX_PSK, SSL_AUTH_DHE, SSL_SYM_AES_128, + SSL_HASH_CCM_8, "TLS_PSK_DHE_WITH_AES_128_CCM_8"); +static const SSLCipherSuite Cipher313 = + SSLCipherSuite(0xC0AB, SSL_KEYX_PSK, SSL_AUTH_DHE, SSL_SYM_AES_256, + SSL_HASH_CCM_8, "TLS_PSK_DHE_WITH_AES_256_CCM_8"); +static const SSLCipherSuite Cipher314 = + SSLCipherSuite(0xC0AC, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_AES_128, + SSL_HASH_CCM, "TLS_ECDHE_ECDSA_WITH_AES_128_CCM"); +static const SSLCipherSuite Cipher315 = + SSLCipherSuite(0xC0AD, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_AES_256, + SSL_HASH_CCM, "TLS_ECDHE_ECDSA_WITH_AES_256_CCM"); +static const SSLCipherSuite Cipher316 = + SSLCipherSuite(0xC0AE, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_AES_128, + SSL_HASH_CCM_8, "TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8"); +static const SSLCipherSuite Cipher317 = + SSLCipherSuite(0xC0AF, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_AES_256, + SSL_HASH_CCM_8, "TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8"); +static const SSLCipherSuite Cipher318 = SSLCipherSuite( + 0xCCA8, SSL_KEYX_ECDHE, SSL_AUTH_RSA, SSL_SYM_CHACHA20_POLY1305, + SSL_HASH_SHA256, "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256"); +static const SSLCipherSuite Cipher319 = SSLCipherSuite( + 0xCCA9, SSL_KEYX_ECDHE, SSL_AUTH_ECDSA, SSL_SYM_CHACHA20_POLY1305, + SSL_HASH_SHA256, "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256"); +static const SSLCipherSuite Cipher320 = SSLCipherSuite( + 0xCCAA, SSL_KEYX_DHE, SSL_AUTH_RSA, SSL_SYM_CHACHA20_POLY1305, + SSL_HASH_SHA256, "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256"); +static const SSLCipherSuite Cipher321 = SSLCipherSuite( + 0xCCAB, SSL_KEYX_PSK, SSL_AUTH_PSK, SSL_SYM_CHACHA20_POLY1305, + SSL_HASH_SHA256, "TLS_PSK_WITH_CHACHA20_POLY1305_SHA256"); +static const SSLCipherSuite Cipher322 = SSLCipherSuite( + 0xCCAC, SSL_KEYX_ECDHE, SSL_AUTH_PSK, SSL_SYM_CHACHA20_POLY1305, + SSL_HASH_SHA256, "TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256"); +static const SSLCipherSuite Cipher323 = SSLCipherSuite( + 0xCCAD, SSL_KEYX_DHE, SSL_AUTH_PSK, SSL_SYM_CHACHA20_POLY1305, + SSL_HASH_SHA256, "TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256"); +static const SSLCipherSuite Cipher324 = SSLCipherSuite( + 0xCCAE, SSL_KEYX_RSA, SSL_AUTH_PSK, SSL_SYM_CHACHA20_POLY1305, + SSL_HASH_SHA256, "TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256"); +static const SSLCipherSuite Cipher325 = + SSLCipherSuite(0x1301, SSL_KEYX_NULL, SSL_AUTH_NULL, SSL_SYM_AES_128_GCM, + SSL_HASH_SHA256, "TLS_AES_128_GCM_SHA256"); +static const SSLCipherSuite Cipher326 = + SSLCipherSuite(0x1302, SSL_KEYX_NULL, SSL_AUTH_NULL, SSL_SYM_AES_256_GCM, + SSL_HASH_SHA384, "TLS_AES_256_GCM_SHA384"); +static const SSLCipherSuite Cipher327 = SSLCipherSuite( + 0x1303, SSL_KEYX_NULL, SSL_AUTH_NULL, SSL_SYM_CHACHA20_POLY1305, + SSL_HASH_SHA256, "TLS_CHACHA20_POLY1305_SHA256"); +static const SSLCipherSuite Cipher328 = + SSLCipherSuite(0x1304, SSL_KEYX_NULL, SSL_AUTH_NULL, SSL_SYM_AES_128_CCM, + SSL_HASH_SHA256, "TLS_AES_128_CCM_SHA256"); +static const SSLCipherSuite Cipher329 = + SSLCipherSuite(0x1305, SSL_KEYX_NULL, SSL_AUTH_NULL, SSL_SYM_AES_128_CCM_8, + SSL_HASH_SHA256, "TLS_AES_128_CCM_8_SHA256"); + +static std::map createCipherSuiteIdToObjectMap() { + std::map result; + + result[0x0000] = (SSLCipherSuite*)&Cipher1; + result[0x0001] = (SSLCipherSuite*)&Cipher2; + result[0x0002] = (SSLCipherSuite*)&Cipher3; + result[0x0003] = (SSLCipherSuite*)&Cipher4; + result[0x0004] = (SSLCipherSuite*)&Cipher5; + result[0x0005] = (SSLCipherSuite*)&Cipher6; + result[0x0006] = (SSLCipherSuite*)&Cipher7; + result[0x0007] = (SSLCipherSuite*)&Cipher8; + result[0x0008] = (SSLCipherSuite*)&Cipher9; + result[0x0009] = (SSLCipherSuite*)&Cipher10; + result[0x000A] = (SSLCipherSuite*)&Cipher11; + result[0x000B] = (SSLCipherSuite*)&Cipher12; + result[0x000C] = (SSLCipherSuite*)&Cipher13; + result[0x000D] = (SSLCipherSuite*)&Cipher14; + result[0x000E] = (SSLCipherSuite*)&Cipher15; + result[0x000F] = (SSLCipherSuite*)&Cipher16; + result[0x0010] = (SSLCipherSuite*)&Cipher17; + result[0x0011] = (SSLCipherSuite*)&Cipher18; + result[0x0012] = (SSLCipherSuite*)&Cipher19; + result[0x0013] = (SSLCipherSuite*)&Cipher20; + result[0x0014] = (SSLCipherSuite*)&Cipher21; + result[0x0015] = (SSLCipherSuite*)&Cipher22; + result[0x0016] = (SSLCipherSuite*)&Cipher23; + result[0x0017] = (SSLCipherSuite*)&Cipher24; + result[0x0018] = (SSLCipherSuite*)&Cipher25; + result[0x0019] = (SSLCipherSuite*)&Cipher26; + result[0x001A] = (SSLCipherSuite*)&Cipher27; + result[0x001B] = (SSLCipherSuite*)&Cipher28; + result[0x001E] = (SSLCipherSuite*)&Cipher29; + result[0x001F] = (SSLCipherSuite*)&Cipher30; + result[0x0020] = (SSLCipherSuite*)&Cipher31; + result[0x0021] = (SSLCipherSuite*)&Cipher32; + result[0x0022] = (SSLCipherSuite*)&Cipher33; + result[0x0023] = (SSLCipherSuite*)&Cipher34; + result[0x0024] = (SSLCipherSuite*)&Cipher35; + result[0x0025] = (SSLCipherSuite*)&Cipher36; + result[0x0026] = (SSLCipherSuite*)&Cipher37; + result[0x0027] = (SSLCipherSuite*)&Cipher38; + result[0x0028] = (SSLCipherSuite*)&Cipher39; + result[0x0029] = (SSLCipherSuite*)&Cipher40; + result[0x002A] = (SSLCipherSuite*)&Cipher41; + result[0x002B] = (SSLCipherSuite*)&Cipher42; + result[0x002C] = (SSLCipherSuite*)&Cipher43; + result[0x002D] = (SSLCipherSuite*)&Cipher44; + result[0x002E] = (SSLCipherSuite*)&Cipher45; + result[0x002F] = (SSLCipherSuite*)&Cipher46; + result[0x0030] = (SSLCipherSuite*)&Cipher47; + result[0x0031] = (SSLCipherSuite*)&Cipher48; + result[0x0032] = (SSLCipherSuite*)&Cipher49; + result[0x0033] = (SSLCipherSuite*)&Cipher50; + result[0x0034] = (SSLCipherSuite*)&Cipher51; + result[0x0035] = (SSLCipherSuite*)&Cipher52; + result[0x0036] = (SSLCipherSuite*)&Cipher53; + result[0x0037] = (SSLCipherSuite*)&Cipher54; + result[0x0038] = (SSLCipherSuite*)&Cipher55; + result[0x0039] = (SSLCipherSuite*)&Cipher56; + result[0x003A] = (SSLCipherSuite*)&Cipher57; + result[0x003B] = (SSLCipherSuite*)&Cipher58; + result[0x003C] = (SSLCipherSuite*)&Cipher59; + result[0x003D] = (SSLCipherSuite*)&Cipher60; + result[0x003E] = (SSLCipherSuite*)&Cipher61; + result[0x003F] = (SSLCipherSuite*)&Cipher62; + result[0x0040] = (SSLCipherSuite*)&Cipher63; + result[0x0041] = (SSLCipherSuite*)&Cipher64; + result[0x0042] = (SSLCipherSuite*)&Cipher65; + result[0x0043] = (SSLCipherSuite*)&Cipher66; + result[0x0044] = (SSLCipherSuite*)&Cipher67; + result[0x0045] = (SSLCipherSuite*)&Cipher68; + result[0x0046] = (SSLCipherSuite*)&Cipher69; + result[0x0067] = (SSLCipherSuite*)&Cipher70; + result[0x0068] = (SSLCipherSuite*)&Cipher71; + result[0x0069] = (SSLCipherSuite*)&Cipher72; + result[0x006A] = (SSLCipherSuite*)&Cipher73; + result[0x006B] = (SSLCipherSuite*)&Cipher74; + result[0x006C] = (SSLCipherSuite*)&Cipher75; + result[0x006D] = (SSLCipherSuite*)&Cipher76; + result[0x0084] = (SSLCipherSuite*)&Cipher77; + result[0x0085] = (SSLCipherSuite*)&Cipher78; + result[0x0086] = (SSLCipherSuite*)&Cipher79; + result[0x0087] = (SSLCipherSuite*)&Cipher80; + result[0x0088] = (SSLCipherSuite*)&Cipher81; + result[0x0089] = (SSLCipherSuite*)&Cipher82; + result[0x008A] = (SSLCipherSuite*)&Cipher83; + result[0x008B] = (SSLCipherSuite*)&Cipher84; + result[0x008C] = (SSLCipherSuite*)&Cipher85; + result[0x008D] = (SSLCipherSuite*)&Cipher86; + result[0x008E] = (SSLCipherSuite*)&Cipher87; + result[0x008F] = (SSLCipherSuite*)&Cipher88; + result[0x0090] = (SSLCipherSuite*)&Cipher89; + result[0x0091] = (SSLCipherSuite*)&Cipher90; + result[0x0092] = (SSLCipherSuite*)&Cipher91; + result[0x0093] = (SSLCipherSuite*)&Cipher92; + result[0x0094] = (SSLCipherSuite*)&Cipher93; + result[0x0095] = (SSLCipherSuite*)&Cipher94; + result[0x0096] = (SSLCipherSuite*)&Cipher95; + result[0x0097] = (SSLCipherSuite*)&Cipher96; + result[0x0098] = (SSLCipherSuite*)&Cipher97; + result[0x0099] = (SSLCipherSuite*)&Cipher98; + result[0x009A] = (SSLCipherSuite*)&Cipher99; + result[0x009B] = (SSLCipherSuite*)&Cipher100; + result[0x009C] = (SSLCipherSuite*)&Cipher101; + result[0x009D] = (SSLCipherSuite*)&Cipher102; + result[0x009E] = (SSLCipherSuite*)&Cipher103; + result[0x009F] = (SSLCipherSuite*)&Cipher104; + result[0x00A0] = (SSLCipherSuite*)&Cipher105; + result[0x00A1] = (SSLCipherSuite*)&Cipher106; + result[0x00A2] = (SSLCipherSuite*)&Cipher107; + result[0x00A3] = (SSLCipherSuite*)&Cipher108; + result[0x00A4] = (SSLCipherSuite*)&Cipher109; + result[0x00A5] = (SSLCipherSuite*)&Cipher110; + result[0x00A6] = (SSLCipherSuite*)&Cipher111; + result[0x00A7] = (SSLCipherSuite*)&Cipher112; + result[0x00A8] = (SSLCipherSuite*)&Cipher113; + result[0x00A9] = (SSLCipherSuite*)&Cipher114; + result[0x00AA] = (SSLCipherSuite*)&Cipher115; + result[0x00AB] = (SSLCipherSuite*)&Cipher116; + result[0x00AC] = (SSLCipherSuite*)&Cipher117; + result[0x00AD] = (SSLCipherSuite*)&Cipher118; + result[0x00AE] = (SSLCipherSuite*)&Cipher119; + result[0x00AF] = (SSLCipherSuite*)&Cipher120; + result[0x00B0] = (SSLCipherSuite*)&Cipher121; + result[0x00B1] = (SSLCipherSuite*)&Cipher122; + result[0x00B2] = (SSLCipherSuite*)&Cipher123; + result[0x00B3] = (SSLCipherSuite*)&Cipher124; + result[0x00B4] = (SSLCipherSuite*)&Cipher125; + result[0x00B5] = (SSLCipherSuite*)&Cipher126; + result[0x00B6] = (SSLCipherSuite*)&Cipher127; + result[0x00B7] = (SSLCipherSuite*)&Cipher128; + result[0x00B8] = (SSLCipherSuite*)&Cipher129; + result[0x00B9] = (SSLCipherSuite*)&Cipher130; + result[0x00BA] = (SSLCipherSuite*)&Cipher131; + result[0x00BB] = (SSLCipherSuite*)&Cipher132; + result[0x00BC] = (SSLCipherSuite*)&Cipher133; + result[0x00BD] = (SSLCipherSuite*)&Cipher134; + result[0x00BE] = (SSLCipherSuite*)&Cipher135; + result[0x00BF] = (SSLCipherSuite*)&Cipher136; + result[0x00C0] = (SSLCipherSuite*)&Cipher137; + result[0x00C1] = (SSLCipherSuite*)&Cipher138; + result[0x00C2] = (SSLCipherSuite*)&Cipher139; + result[0x00C3] = (SSLCipherSuite*)&Cipher140; + result[0x00C4] = (SSLCipherSuite*)&Cipher141; + result[0x00C5] = (SSLCipherSuite*)&Cipher142; + result[0xC001] = (SSLCipherSuite*)&Cipher143; + result[0xC002] = (SSLCipherSuite*)&Cipher144; + result[0xC003] = (SSLCipherSuite*)&Cipher145; + result[0xC004] = (SSLCipherSuite*)&Cipher146; + result[0xC005] = (SSLCipherSuite*)&Cipher147; + result[0xC006] = (SSLCipherSuite*)&Cipher148; + result[0xC007] = (SSLCipherSuite*)&Cipher149; + result[0xC008] = (SSLCipherSuite*)&Cipher150; + result[0xC009] = (SSLCipherSuite*)&Cipher151; + result[0xC00A] = (SSLCipherSuite*)&Cipher152; + result[0xC00B] = (SSLCipherSuite*)&Cipher153; + result[0xC00C] = (SSLCipherSuite*)&Cipher154; + result[0xC00D] = (SSLCipherSuite*)&Cipher155; + result[0xC00E] = (SSLCipherSuite*)&Cipher156; + result[0xC00F] = (SSLCipherSuite*)&Cipher157; + result[0xC010] = (SSLCipherSuite*)&Cipher158; + result[0xC011] = (SSLCipherSuite*)&Cipher159; + result[0xC012] = (SSLCipherSuite*)&Cipher160; + result[0xC013] = (SSLCipherSuite*)&Cipher161; + result[0xC014] = (SSLCipherSuite*)&Cipher162; + result[0xC015] = (SSLCipherSuite*)&Cipher163; + result[0xC016] = (SSLCipherSuite*)&Cipher164; + result[0xC017] = (SSLCipherSuite*)&Cipher165; + result[0xC018] = (SSLCipherSuite*)&Cipher166; + result[0xC019] = (SSLCipherSuite*)&Cipher167; + result[0xC01A] = (SSLCipherSuite*)&Cipher168; + result[0xC01B] = (SSLCipherSuite*)&Cipher169; + result[0xC01C] = (SSLCipherSuite*)&Cipher170; + result[0xC01D] = (SSLCipherSuite*)&Cipher171; + result[0xC01E] = (SSLCipherSuite*)&Cipher172; + result[0xC01F] = (SSLCipherSuite*)&Cipher173; + result[0xC020] = (SSLCipherSuite*)&Cipher174; + result[0xC021] = (SSLCipherSuite*)&Cipher175; + result[0xC022] = (SSLCipherSuite*)&Cipher176; + result[0xC023] = (SSLCipherSuite*)&Cipher177; + result[0xC024] = (SSLCipherSuite*)&Cipher178; + result[0xC025] = (SSLCipherSuite*)&Cipher179; + result[0xC026] = (SSLCipherSuite*)&Cipher180; + result[0xC027] = (SSLCipherSuite*)&Cipher181; + result[0xC028] = (SSLCipherSuite*)&Cipher182; + result[0xC029] = (SSLCipherSuite*)&Cipher183; + result[0xC02A] = (SSLCipherSuite*)&Cipher184; + result[0xC02B] = (SSLCipherSuite*)&Cipher185; + result[0xC02C] = (SSLCipherSuite*)&Cipher186; + result[0xC02D] = (SSLCipherSuite*)&Cipher187; + result[0xC02E] = (SSLCipherSuite*)&Cipher188; + result[0xC02F] = (SSLCipherSuite*)&Cipher189; + result[0xC030] = (SSLCipherSuite*)&Cipher190; + result[0xC031] = (SSLCipherSuite*)&Cipher191; + result[0xC032] = (SSLCipherSuite*)&Cipher192; + result[0xC033] = (SSLCipherSuite*)&Cipher193; + result[0xC034] = (SSLCipherSuite*)&Cipher194; + result[0xC035] = (SSLCipherSuite*)&Cipher195; + result[0xC036] = (SSLCipherSuite*)&Cipher196; + result[0xC037] = (SSLCipherSuite*)&Cipher197; + result[0xC038] = (SSLCipherSuite*)&Cipher198; + result[0xC039] = (SSLCipherSuite*)&Cipher199; + result[0xC03A] = (SSLCipherSuite*)&Cipher200; + result[0xC03B] = (SSLCipherSuite*)&Cipher201; + result[0xC03C] = (SSLCipherSuite*)&Cipher202; + result[0xC03D] = (SSLCipherSuite*)&Cipher203; + result[0xC03E] = (SSLCipherSuite*)&Cipher204; + result[0xC03F] = (SSLCipherSuite*)&Cipher205; + result[0xC040] = (SSLCipherSuite*)&Cipher206; + result[0xC041] = (SSLCipherSuite*)&Cipher207; + result[0xC042] = (SSLCipherSuite*)&Cipher208; + result[0xC043] = (SSLCipherSuite*)&Cipher209; + result[0xC044] = (SSLCipherSuite*)&Cipher210; + result[0xC045] = (SSLCipherSuite*)&Cipher211; + result[0xC046] = (SSLCipherSuite*)&Cipher212; + result[0xC047] = (SSLCipherSuite*)&Cipher213; + result[0xC048] = (SSLCipherSuite*)&Cipher214; + result[0xC049] = (SSLCipherSuite*)&Cipher215; + result[0xC04A] = (SSLCipherSuite*)&Cipher216; + result[0xC04B] = (SSLCipherSuite*)&Cipher217; + result[0xC04C] = (SSLCipherSuite*)&Cipher218; + result[0xC04D] = (SSLCipherSuite*)&Cipher219; + result[0xC04E] = (SSLCipherSuite*)&Cipher220; + result[0xC04F] = (SSLCipherSuite*)&Cipher221; + result[0xC050] = (SSLCipherSuite*)&Cipher222; + result[0xC051] = (SSLCipherSuite*)&Cipher223; + result[0xC052] = (SSLCipherSuite*)&Cipher224; + result[0xC053] = (SSLCipherSuite*)&Cipher225; + result[0xC054] = (SSLCipherSuite*)&Cipher226; + result[0xC055] = (SSLCipherSuite*)&Cipher227; + result[0xC056] = (SSLCipherSuite*)&Cipher228; + result[0xC057] = (SSLCipherSuite*)&Cipher229; + result[0xC058] = (SSLCipherSuite*)&Cipher230; + result[0xC059] = (SSLCipherSuite*)&Cipher231; + result[0xC05A] = (SSLCipherSuite*)&Cipher232; + result[0xC05B] = (SSLCipherSuite*)&Cipher233; + result[0xC05C] = (SSLCipherSuite*)&Cipher234; + result[0xC05D] = (SSLCipherSuite*)&Cipher235; + result[0xC05E] = (SSLCipherSuite*)&Cipher236; + result[0xC05F] = (SSLCipherSuite*)&Cipher237; + result[0xC060] = (SSLCipherSuite*)&Cipher238; + result[0xC061] = (SSLCipherSuite*)&Cipher239; + result[0xC062] = (SSLCipherSuite*)&Cipher240; + result[0xC063] = (SSLCipherSuite*)&Cipher241; + result[0xC064] = (SSLCipherSuite*)&Cipher242; + result[0xC065] = (SSLCipherSuite*)&Cipher243; + result[0xC066] = (SSLCipherSuite*)&Cipher244; + result[0xC067] = (SSLCipherSuite*)&Cipher245; + result[0xC068] = (SSLCipherSuite*)&Cipher246; + result[0xC069] = (SSLCipherSuite*)&Cipher247; + result[0xC06A] = (SSLCipherSuite*)&Cipher248; + result[0xC06B] = (SSLCipherSuite*)&Cipher249; + result[0xC06C] = (SSLCipherSuite*)&Cipher250; + result[0xC06D] = (SSLCipherSuite*)&Cipher251; + result[0xC06E] = (SSLCipherSuite*)&Cipher252; + result[0xC06F] = (SSLCipherSuite*)&Cipher253; + result[0xC070] = (SSLCipherSuite*)&Cipher254; + result[0xC071] = (SSLCipherSuite*)&Cipher255; + result[0xC072] = (SSLCipherSuite*)&Cipher256; + result[0xC073] = (SSLCipherSuite*)&Cipher257; + result[0xC074] = (SSLCipherSuite*)&Cipher258; + result[0xC075] = (SSLCipherSuite*)&Cipher259; + result[0xC076] = (SSLCipherSuite*)&Cipher260; + result[0xC077] = (SSLCipherSuite*)&Cipher261; + result[0xC078] = (SSLCipherSuite*)&Cipher262; + result[0xC079] = (SSLCipherSuite*)&Cipher263; + result[0xC07A] = (SSLCipherSuite*)&Cipher264; + result[0xC07B] = (SSLCipherSuite*)&Cipher265; + result[0xC07C] = (SSLCipherSuite*)&Cipher266; + result[0xC07D] = (SSLCipherSuite*)&Cipher267; + result[0xC07E] = (SSLCipherSuite*)&Cipher268; + result[0xC07F] = (SSLCipherSuite*)&Cipher269; + result[0xC080] = (SSLCipherSuite*)&Cipher270; + result[0xC081] = (SSLCipherSuite*)&Cipher271; + result[0xC082] = (SSLCipherSuite*)&Cipher272; + result[0xC083] = (SSLCipherSuite*)&Cipher273; + result[0xC084] = (SSLCipherSuite*)&Cipher274; + result[0xC085] = (SSLCipherSuite*)&Cipher275; + result[0xC086] = (SSLCipherSuite*)&Cipher276; + result[0xC087] = (SSLCipherSuite*)&Cipher277; + result[0xC088] = (SSLCipherSuite*)&Cipher278; + result[0xC089] = (SSLCipherSuite*)&Cipher279; + result[0xC08A] = (SSLCipherSuite*)&Cipher280; + result[0xC08B] = (SSLCipherSuite*)&Cipher281; + result[0xC08C] = (SSLCipherSuite*)&Cipher282; + result[0xC08D] = (SSLCipherSuite*)&Cipher283; + result[0xC08E] = (SSLCipherSuite*)&Cipher284; + result[0xC08F] = (SSLCipherSuite*)&Cipher285; + result[0xC090] = (SSLCipherSuite*)&Cipher286; + result[0xC091] = (SSLCipherSuite*)&Cipher287; + result[0xC092] = (SSLCipherSuite*)&Cipher288; + result[0xC093] = (SSLCipherSuite*)&Cipher289; + result[0xC094] = (SSLCipherSuite*)&Cipher290; + result[0xC095] = (SSLCipherSuite*)&Cipher291; + result[0xC096] = (SSLCipherSuite*)&Cipher292; + result[0xC097] = (SSLCipherSuite*)&Cipher293; + result[0xC098] = (SSLCipherSuite*)&Cipher294; + result[0xC099] = (SSLCipherSuite*)&Cipher295; + result[0xC09A] = (SSLCipherSuite*)&Cipher296; + result[0xC09B] = (SSLCipherSuite*)&Cipher297; + result[0xC09C] = (SSLCipherSuite*)&Cipher298; + result[0xC09D] = (SSLCipherSuite*)&Cipher299; + result[0xC09E] = (SSLCipherSuite*)&Cipher300; + result[0xC09F] = (SSLCipherSuite*)&Cipher301; + result[0xC0A0] = (SSLCipherSuite*)&Cipher302; + result[0xC0A1] = (SSLCipherSuite*)&Cipher303; + result[0xC0A2] = (SSLCipherSuite*)&Cipher304; + result[0xC0A3] = (SSLCipherSuite*)&Cipher305; + result[0xC0A4] = (SSLCipherSuite*)&Cipher306; + result[0xC0A5] = (SSLCipherSuite*)&Cipher307; + result[0xC0A6] = (SSLCipherSuite*)&Cipher308; + result[0xC0A7] = (SSLCipherSuite*)&Cipher309; + result[0xC0A8] = (SSLCipherSuite*)&Cipher310; + result[0xC0A9] = (SSLCipherSuite*)&Cipher311; + result[0xC0AA] = (SSLCipherSuite*)&Cipher312; + result[0xC0AB] = (SSLCipherSuite*)&Cipher313; + result[0xC0AC] = (SSLCipherSuite*)&Cipher314; + result[0xC0AD] = (SSLCipherSuite*)&Cipher315; + result[0xC0AE] = (SSLCipherSuite*)&Cipher316; + result[0xC0AF] = (SSLCipherSuite*)&Cipher317; + result[0xCCA8] = (SSLCipherSuite*)&Cipher318; + result[0xCCA9] = (SSLCipherSuite*)&Cipher319; + result[0xCCAA] = (SSLCipherSuite*)&Cipher320; + result[0xCCAB] = (SSLCipherSuite*)&Cipher321; + result[0xCCAC] = (SSLCipherSuite*)&Cipher322; + result[0xCCAD] = (SSLCipherSuite*)&Cipher323; + result[0xCCAE] = (SSLCipherSuite*)&Cipher324; + result[0x1301] = (SSLCipherSuite*)&Cipher325; + result[0x1302] = (SSLCipherSuite*)&Cipher326; + result[0x1303] = (SSLCipherSuite*)&Cipher327; + result[0x1304] = (SSLCipherSuite*)&Cipher328; + result[0x1305] = (SSLCipherSuite*)&Cipher329; + return result; +} + +#define A 54059 /* a prime */ +#define B 76963 /* another prime */ +#define C 86969 /* yet another prime */ #define FIRST_HASH 37 /* also prime */ -static uint32_t hashString(std::string str) -{ - unsigned h = FIRST_HASH; - for(std::string::size_type i = 0; i < str.size(); ++i) - { - h = (h * A) ^ (str[i] * B); - } - return h; -} - -static std::map createCipherSuiteStringToObjectMap() -{ - std::map result; - - result[0x9F180F43] = (SSLCipherSuite*)&Cipher1; - result[0x97D9341F] = (SSLCipherSuite*)&Cipher2; - result[0x288FABA1] = (SSLCipherSuite*)&Cipher3; - result[0x9179C5BD] = (SSLCipherSuite*)&Cipher4; - result[0x68DF0C8F] = (SSLCipherSuite*)&Cipher5; - result[0x5FB32DF1] = (SSLCipherSuite*)&Cipher6; - result[0x2A1FC0FC] = (SSLCipherSuite*)&Cipher7; - result[0x5BF6459E] = (SSLCipherSuite*)&Cipher8; - result[0x60D692F4] = (SSLCipherSuite*)&Cipher9; - result[0x26A21427] = (SSLCipherSuite*)&Cipher10; - result[0xD3558C6D] = (SSLCipherSuite*)&Cipher11; - result[0xAE2673E9] = (SSLCipherSuite*)&Cipher12; - result[0xC63B19B0] = (SSLCipherSuite*)&Cipher13; - result[0xFE49B3BC] = (SSLCipherSuite*)&Cipher14; - result[0x625A86D5] = (SSLCipherSuite*)&Cipher15; - result[0x60FF1BD4] = (SSLCipherSuite*)&Cipher16; - result[0xE101D5C8] = (SSLCipherSuite*)&Cipher17; - result[0x422859E8] = (SSLCipherSuite*)&Cipher18; - result[0x88ABC503] = (SSLCipherSuite*)&Cipher19; - result[0x44284B1] = (SSLCipherSuite*)&Cipher20; - result[0xFD71B064] = (SSLCipherSuite*)&Cipher21; - result[0x76F35237] = (SSLCipherSuite*)&Cipher22; - result[0x7D93159D] = (SSLCipherSuite*)&Cipher23; - result[0x6E9D1AE2] = (SSLCipherSuite*)&Cipher24; - result[0xFA0974E4] = (SSLCipherSuite*)&Cipher25; - result[0xEC27ACB1] = (SSLCipherSuite*)&Cipher26; - result[0x6859C7A8] = (SSLCipherSuite*)&Cipher27; - result[0x55FD3D14] = (SSLCipherSuite*)&Cipher28; - result[0xA7650023] = (SSLCipherSuite*)&Cipher29; - result[0xDC042011] = (SSLCipherSuite*)&Cipher30; - result[0x94BFBF4D] = (SSLCipherSuite*)&Cipher31; - result[0x2FE24162] = (SSLCipherSuite*)&Cipher32; - result[0xC449D595] = (SSLCipherSuite*)&Cipher33; - result[0xE11292AF] = (SSLCipherSuite*)&Cipher34; - result[0x47D0643] = (SSLCipherSuite*)&Cipher35; - result[0xC9ABBA3C] = (SSLCipherSuite*)&Cipher36; - result[0x9F323A5F] = (SSLCipherSuite*)&Cipher37; - result[0xFBF78046] = (SSLCipherSuite*)&Cipher38; - result[0x859BD79F] = (SSLCipherSuite*)&Cipher39; - result[0xF9FBBB39] = (SSLCipherSuite*)&Cipher40; - result[0x63587748] = (SSLCipherSuite*)&Cipher41; - result[0xF84CAE79] = (SSLCipherSuite*)&Cipher42; - result[0xCA39F6F1] = (SSLCipherSuite*)&Cipher43; - result[0xDC4D17C1] = (SSLCipherSuite*)&Cipher44; - result[0x955FBE28] = (SSLCipherSuite*)&Cipher45; - result[0x73ED7B86] = (SSLCipherSuite*)&Cipher46; - result[0x14A51855] = (SSLCipherSuite*)&Cipher47; - result[0x2CE54061] = (SSLCipherSuite*)&Cipher48; - result[0x3360789A] = (SSLCipherSuite*)&Cipher49; - result[0xDFEF59B6] = (SSLCipherSuite*)&Cipher50; - result[0xE819855D] = (SSLCipherSuite*)&Cipher51; - result[0x24CC3946] = (SSLCipherSuite*)&Cipher52; - result[0x1CACB5FD] = (SSLCipherSuite*)&Cipher53; - result[0x40193001] = (SSLCipherSuite*)&Cipher54; - result[0xA3846DA2] = (SSLCipherSuite*)&Cipher55; - result[0x8F3B7CF6] = (SSLCipherSuite*)&Cipher56; - result[0xC7B09945] = (SSLCipherSuite*)&Cipher57; - result[0xD8172F82] = (SSLCipherSuite*)&Cipher58; - result[0xB6748503] = (SSLCipherSuite*)&Cipher59; - result[0xDB105043] = (SSLCipherSuite*)&Cipher60; - result[0x21E8AC2E] = (SSLCipherSuite*)&Cipher61; - result[0x55096FC2] = (SSLCipherSuite*)&Cipher62; - result[0x38F955AF] = (SSLCipherSuite*)&Cipher63; - result[0xBA8C1D77] = (SSLCipherSuite*)&Cipher64; - result[0x91128102] = (SSLCipherSuite*)&Cipher65; - result[0xA7ED740E] = (SSLCipherSuite*)&Cipher66; - result[0x75C4908B] = (SSLCipherSuite*)&Cipher67; - result[0xBC6C5E87] = (SSLCipherSuite*)&Cipher68; - result[0xA0499A2A] = (SSLCipherSuite*)&Cipher69; - result[0x4F0FFC13] = (SSLCipherSuite*)&Cipher70; - result[0xCCEE9996] = (SSLCipherSuite*)&Cipher71; - result[0x8570DA22] = (SSLCipherSuite*)&Cipher72; - result[0x75D4FD57] = (SSLCipherSuite*)&Cipher73; - result[0x602E04D3] = (SSLCipherSuite*)&Cipher74; - result[0x5EDC9C36] = (SSLCipherSuite*)&Cipher75; - result[0xE66C167E] = (SSLCipherSuite*)&Cipher76; - result[0x909F6D7B] = (SSLCipherSuite*)&Cipher77; - result[0x3C35B1AA] = (SSLCipherSuite*)&Cipher78; - result[0x6D4D1A2E] = (SSLCipherSuite*)&Cipher79; - result[0xBF788317] = (SSLCipherSuite*)&Cipher80; - result[0x5329738B] = (SSLCipherSuite*)&Cipher81; - result[0x7D11AB2] = (SSLCipherSuite*)&Cipher82; - result[0x461ACA21] = (SSLCipherSuite*)&Cipher83; - result[0x15404ADD] = (SSLCipherSuite*)&Cipher84; - result[0x3806AF6] = (SSLCipherSuite*)&Cipher85; - result[0xB2D80EB6] = (SSLCipherSuite*)&Cipher86; - result[0xE54425D1] = (SSLCipherSuite*)&Cipher87; - result[0x476457CD] = (SSLCipherSuite*)&Cipher88; - result[0x1D55E526] = (SSLCipherSuite*)&Cipher89; - result[0x953C69E6] = (SSLCipherSuite*)&Cipher90; - result[0x6ADE7E16] = (SSLCipherSuite*)&Cipher91; - result[0xE8C7BBE8] = (SSLCipherSuite*)&Cipher92; - result[0x623DC741] = (SSLCipherSuite*)&Cipher93; - result[0xF403E1] = (SSLCipherSuite*)&Cipher94; - result[0x90D8CADC] = (SSLCipherSuite*)&Cipher95; - result[0xC30D1199] = (SSLCipherSuite*)&Cipher96; - result[0x9CFB1B5D] = (SSLCipherSuite*)&Cipher97; - result[0x2D3B99E8] = (SSLCipherSuite*)&Cipher98; - result[0x4A9E8B0C] = (SSLCipherSuite*)&Cipher99; - result[0x16BD2351] = (SSLCipherSuite*)&Cipher100; - result[0x586BC20E] = (SSLCipherSuite*)&Cipher101; - result[0x996B90AA] = (SSLCipherSuite*)&Cipher102; - result[0x2F3871FE] = (SSLCipherSuite*)&Cipher103; - result[0xF2DD519A] = (SSLCipherSuite*)&Cipher104; - result[0x52615F23] = (SSLCipherSuite*)&Cipher105; - result[0xDEE51337] = (SSLCipherSuite*)&Cipher106; - result[0xB30890E2] = (SSLCipherSuite*)&Cipher107; - result[0x40F3FF3E] = (SSLCipherSuite*)&Cipher108; - result[0xE306EE17] = (SSLCipherSuite*)&Cipher109; - result[0x870C6FCB] = (SSLCipherSuite*)&Cipher110; - result[0xEB12CAEF] = (SSLCipherSuite*)&Cipher111; - result[0x68795983] = (SSLCipherSuite*)&Cipher112; - result[0x606BA9BE] = (SSLCipherSuite*)&Cipher113; - result[0x2C33475A] = (SSLCipherSuite*)&Cipher114; - result[0x640CAAEE] = (SSLCipherSuite*)&Cipher115; - result[0x6603488A] = (SSLCipherSuite*)&Cipher116; - result[0x8BA58643] = (SSLCipherSuite*)&Cipher117; - result[0x16059E57] = (SSLCipherSuite*)&Cipher118; - result[0x1B0606D3] = (SSLCipherSuite*)&Cipher119; - result[0x1CF76007] = (SSLCipherSuite*)&Cipher120; - result[0x618CE8F2] = (SSLCipherSuite*)&Cipher121; - result[0xE264D3B6] = (SSLCipherSuite*)&Cipher122; - result[0xB4C5AE63] = (SSLCipherSuite*)&Cipher123; - result[0x95DF4757] = (SSLCipherSuite*)&Cipher124; - result[0x1D1CF062] = (SSLCipherSuite*)&Cipher125; - result[0xE7AA2826] = (SSLCipherSuite*)&Cipher126; - result[0x38D94EE2] = (SSLCipherSuite*)&Cipher127; - result[0x889BA306] = (SSLCipherSuite*)&Cipher128; - result[0x5B816E75] = (SSLCipherSuite*)&Cipher129; - result[0x6F18C4DD] = (SSLCipherSuite*)&Cipher130; - result[0x2E1C05E0] = (SSLCipherSuite*)&Cipher131; - result[0x5592CFF7] = (SSLCipherSuite*)&Cipher132; - result[0x8221D38B] = (SSLCipherSuite*)&Cipher133; - result[0x9538105C] = (SSLCipherSuite*)&Cipher134; - result[0xF1100DD0] = (SSLCipherSuite*)&Cipher135; - result[0xF492EF1F] = (SSLCipherSuite*)&Cipher136; - result[0x226BD52C] = (SSLCipherSuite*)&Cipher137; - result[0xBBACE99F] = (SSLCipherSuite*)&Cipher138; - result[0xB3D4B66B] = (SSLCipherSuite*)&Cipher139; - result[0x8C619440] = (SSLCipherSuite*)&Cipher140; - result[0xE60B95C] = (SSLCipherSuite*)&Cipher141; - result[0x24F48D07] = (SSLCipherSuite*)&Cipher142; - result[0x15C7AF26] = (SSLCipherSuite*)&Cipher143; - result[0xCBA219CC] = (SSLCipherSuite*)&Cipher144; - result[0x9BD946BE] = (SSLCipherSuite*)&Cipher145; - result[0x7CCA46FF] = (SSLCipherSuite*)&Cipher146; - result[0x9FB51FA3] = (SSLCipherSuite*)&Cipher147; - result[0xC82A275B] = (SSLCipherSuite*)&Cipher148; - result[0x4472A583] = (SSLCipherSuite*)&Cipher149; - result[0xDBA3A5CF] = (SSLCipherSuite*)&Cipher150; - result[0x86338128] = (SSLCipherSuite*)&Cipher151; - result[0x8CCE91E4] = (SSLCipherSuite*)&Cipher152; - result[0xA81C6CA0] = (SSLCipherSuite*)&Cipher153; - result[0x6D80815E] = (SSLCipherSuite*)&Cipher154; - result[0xA383DEB0] = (SSLCipherSuite*)&Cipher155; - result[0x52073879] = (SSLCipherSuite*)&Cipher156; - result[0x5BA0B279] = (SSLCipherSuite*)&Cipher157; - result[0xD787CCC9] = (SSLCipherSuite*)&Cipher158; - result[0x9C86C6A9] = (SSLCipherSuite*)&Cipher159; - result[0xDAE424E5] = (SSLCipherSuite*)&Cipher160; - result[0x72C15ECE] = (SSLCipherSuite*)&Cipher161; - result[0xF0E8FB6E] = (SSLCipherSuite*)&Cipher162; - result[0xA2005D44] = (SSLCipherSuite*)&Cipher163; - result[0x77F79962] = (SSLCipherSuite*)&Cipher164; - result[0x25C8184C] = (SSLCipherSuite*)&Cipher165; - result[0x2070F8A5] = (SSLCipherSuite*)&Cipher166; - result[0x4189ED8D] = (SSLCipherSuite*)&Cipher167; - result[0x94C21B1] = (SSLCipherSuite*)&Cipher168; - result[0x1B0CB25C] = (SSLCipherSuite*)&Cipher169; - result[0xF18127A0] = (SSLCipherSuite*)&Cipher170; - result[0xC7FCA79A] = (SSLCipherSuite*)&Cipher171; - result[0xC1DEE135] = (SSLCipherSuite*)&Cipher172; - result[0xDA7143E9] = (SSLCipherSuite*)&Cipher173; - result[0xE82B6A2] = (SSLCipherSuite*)&Cipher174; - result[0x438EC1DD] = (SSLCipherSuite*)&Cipher175; - result[0x6BE32FA9] = (SSLCipherSuite*)&Cipher176; - result[0x18A5C375] = (SSLCipherSuite*)&Cipher177; - result[0x24136C59] = (SSLCipherSuite*)&Cipher178; - result[0x88529408] = (SSLCipherSuite*)&Cipher179; - result[0xADAB33FC] = (SSLCipherSuite*)&Cipher180; - result[0x79407DCB] = (SSLCipherSuite*)&Cipher181; - result[0x64970FFF] = (SSLCipherSuite*)&Cipher182; - result[0x8260DC9A] = (SSLCipherSuite*)&Cipher183; - result[0x4B74FFFE] = (SSLCipherSuite*)&Cipher184; - result[0x350DD5C8] = (SSLCipherSuite*)&Cipher185; - result[0x53E057C] = (SSLCipherSuite*)&Cipher186; - result[0x266020E1] = (SSLCipherSuite*)&Cipher187; - result[0xE6DB4B9D] = (SSLCipherSuite*)&Cipher188; - result[0x5A992E6] = (SSLCipherSuite*)&Cipher189; - result[0x1B33C882] = (SSLCipherSuite*)&Cipher190; - result[0x33579D2B] = (SSLCipherSuite*)&Cipher191; - result[0x1BD7F7FF] = (SSLCipherSuite*)&Cipher192; - result[0x39C59ED9] = (SSLCipherSuite*)&Cipher193; - result[0x4F19FB95] = (SSLCipherSuite*)&Cipher194; - result[0x8F4737BE] = (SSLCipherSuite*)&Cipher195; - result[0x2567AA9E] = (SSLCipherSuite*)&Cipher196; - result[0xEEF843DB] = (SSLCipherSuite*)&Cipher197; - result[0x978C4E4F] = (SSLCipherSuite*)&Cipher198; - result[0x2F8D17D9] = (SSLCipherSuite*)&Cipher199; - result[0x7F80393A] = (SSLCipherSuite*)&Cipher200; - result[0xDCA5AE1E] = (SSLCipherSuite*)&Cipher201; - result[0x74AA95D7] = (SSLCipherSuite*)&Cipher202; - result[0xB93174BB] = (SSLCipherSuite*)&Cipher203; - result[0x46E274FC] = (SSLCipherSuite*)&Cipher204; - result[0x9DC85330] = (SSLCipherSuite*)&Cipher205; - result[0x972847B8] = (SSLCipherSuite*)&Cipher206; - result[0xFCF61DAC] = (SSLCipherSuite*)&Cipher207; - result[0x73C0029B] = (SSLCipherSuite*)&Cipher208; - result[0xDA41D70F] = (SSLCipherSuite*)&Cipher209; - result[0x12CBC4E7] = (SSLCipherSuite*)&Cipher210; - result[0x8B2D5ACB] = (SSLCipherSuite*)&Cipher211; - result[0x28C0C084] = (SSLCipherSuite*)&Cipher212; - result[0x1602C1F8] = (SSLCipherSuite*)&Cipher213; - result[0xF5FB9ED] = (SSLCipherSuite*)&Cipher214; - result[0xE8E30E91] = (SSLCipherSuite*)&Cipher215; - result[0x70BA7792] = (SSLCipherSuite*)&Cipher216; - result[0x94C38076] = (SSLCipherSuite*)&Cipher217; - result[0xE5B3483F] = (SSLCipherSuite*)&Cipher218; - result[0x892DEBE3] = (SSLCipherSuite*)&Cipher219; - result[0x65609E50] = (SSLCipherSuite*)&Cipher220; - result[0xAB4F3F04] = (SSLCipherSuite*)&Cipher221; - result[0x8BFC76DA] = (SSLCipherSuite*)&Cipher222; - result[0xD4BDCD6] = (SSLCipherSuite*)&Cipher223; - result[0xCAB8F54A] = (SSLCipherSuite*)&Cipher224; - result[0xA10DCFC6] = (SSLCipherSuite*)&Cipher225; - result[0xD6B71B71] = (SSLCipherSuite*)&Cipher226; - result[0x6D775A2D] = (SSLCipherSuite*)&Cipher227; - result[0x7997AD16] = (SSLCipherSuite*)&Cipher228; - result[0x5338C632] = (SSLCipherSuite*)&Cipher229; - result[0x45F0598D] = (SSLCipherSuite*)&Cipher230; - result[0x2D8B6A99] = (SSLCipherSuite*)&Cipher231; - result[0xE14DC125] = (SSLCipherSuite*)&Cipher232; - result[0x1538351] = (SSLCipherSuite*)&Cipher233; - result[0x1A8CE530] = (SSLCipherSuite*)&Cipher234; - result[0xB01E69C4] = (SSLCipherSuite*)&Cipher235; - result[0xCCBF70D3] = (SSLCipherSuite*)&Cipher236; - result[0xEF664FE7] = (SSLCipherSuite*)&Cipher237; - result[0xF6ED4F52] = (SSLCipherSuite*)&Cipher238; - result[0x7D6522E] = (SSLCipherSuite*)&Cipher239; - result[0xBDB5C9B9] = (SSLCipherSuite*)&Cipher240; - result[0xD98D5C95] = (SSLCipherSuite*)&Cipher241; - result[0x92B92727] = (SSLCipherSuite*)&Cipher242; - result[0xB4FE570B] = (SSLCipherSuite*)&Cipher243; - result[0x8DCF7F77] = (SSLCipherSuite*)&Cipher244; - result[0x8208545B] = (SSLCipherSuite*)&Cipher245; - result[0x39A13298] = (SSLCipherSuite*)&Cipher246; - result[0xECB7070C] = (SSLCipherSuite*)&Cipher247; - result[0xAFA95F8A] = (SSLCipherSuite*)&Cipher248; - result[0x3D80E106] = (SSLCipherSuite*)&Cipher249; - result[0x83AF9B7A] = (SSLCipherSuite*)&Cipher250; - result[0x1FAAC2F6] = (SSLCipherSuite*)&Cipher251; - result[0x2AF11F51] = (SSLCipherSuite*)&Cipher252; - result[0xEDFD300D] = (SSLCipherSuite*)&Cipher253; - result[0x91AA268F] = (SSLCipherSuite*)&Cipher254; - result[0x9DF0E933] = (SSLCipherSuite*)&Cipher255; - result[0xF3951A6A] = (SSLCipherSuite*)&Cipher256; - result[0xE4FF8DCE] = (SSLCipherSuite*)&Cipher257; - result[0xBE4DFC61] = (SSLCipherSuite*)&Cipher258; - result[0xBB2CF025] = (SSLCipherSuite*)&Cipher259; - result[0x354D38A8] = (SSLCipherSuite*)&Cipher260; - result[0xE2444B9C] = (SSLCipherSuite*)&Cipher261; - result[0xF8298D43] = (SSLCipherSuite*)&Cipher262; - result[0x3EC413B7] = (SSLCipherSuite*)&Cipher263; - result[0xE0C75BE9] = (SSLCipherSuite*)&Cipher264; - result[0x7191BE45] = (SSLCipherSuite*)&Cipher265; - result[0xDDE7C439] = (SSLCipherSuite*)&Cipher266; - result[0xBE715415] = (SSLCipherSuite*)&Cipher267; - result[0x6CF8F9A6] = (SSLCipherSuite*)&Cipher268; - result[0x36D61242] = (SSLCipherSuite*)&Cipher269; - result[0xFA9BA9ED] = (SSLCipherSuite*)&Cipher270; - result[0x4588B179] = (SSLCipherSuite*)&Cipher271; - result[0xB3C246FA] = (SSLCipherSuite*)&Cipher272; - result[0x750EEB76] = (SSLCipherSuite*)&Cipher273; - result[0xC50ACCB2] = (SSLCipherSuite*)&Cipher274; - result[0x9555CD0E] = (SSLCipherSuite*)&Cipher275; - result[0xF25A659B] = (SSLCipherSuite*)&Cipher276; - result[0x1670E72F] = (SSLCipherSuite*)&Cipher277; - result[0xDB0DD6BC] = (SSLCipherSuite*)&Cipher278; - result[0x19CACD70] = (SSLCipherSuite*)&Cipher279; - result[0xC54D5481] = (SSLCipherSuite*)&Cipher280; - result[0x7BCCA2BD] = (SSLCipherSuite*)&Cipher281; - result[0xA851374E] = (SSLCipherSuite*)&Cipher282; - result[0xE887BEA] = (SSLCipherSuite*)&Cipher283; - result[0xDECAA7F9] = (SSLCipherSuite*)&Cipher284; - result[0x29DA73D5] = (SSLCipherSuite*)&Cipher285; - result[0xAC69ECC9] = (SSLCipherSuite*)&Cipher286; - result[0x6AE55625] = (SSLCipherSuite*)&Cipher287; - result[0x2BB24546] = (SSLCipherSuite*)&Cipher288; - result[0x7AB5F262] = (SSLCipherSuite*)&Cipher289; - result[0x3DB83990] = (SSLCipherSuite*)&Cipher290; - result[0xC852A244] = (SSLCipherSuite*)&Cipher291; - result[0xA3C952C0] = (SSLCipherSuite*)&Cipher292; - result[0xAF630C34] = (SSLCipherSuite*)&Cipher293; - result[0xD4EE22B] = (SSLCipherSuite*)&Cipher294; - result[0x83F4C5DF] = (SSLCipherSuite*)&Cipher295; - result[0xCCF6F918] = (SSLCipherSuite*)&Cipher296; - result[0x955C9E8C] = (SSLCipherSuite*)&Cipher297; - result[0xF3559154] = (SSLCipherSuite*)&Cipher298; - result[0xE0991C14] = (SSLCipherSuite*)&Cipher299; - result[0x7F6BF424] = (SSLCipherSuite*)&Cipher300; - result[0x4A129264] = (SSLCipherSuite*)&Cipher301; - result[0xB25E29E3] = (SSLCipherSuite*)&Cipher302; - result[0xA6E15A23] = (SSLCipherSuite*)&Cipher303; - result[0x637C5C53] = (SSLCipherSuite*)&Cipher304; - result[0x22794513] = (SSLCipherSuite*)&Cipher305; - result[0x4CE30464] = (SSLCipherSuite*)&Cipher306; - result[0xFDFE3B24] = (SSLCipherSuite*)&Cipher307; - result[0xDC8A2074] = (SSLCipherSuite*)&Cipher308; - result[0xFD448934] = (SSLCipherSuite*)&Cipher309; - result[0xF4FC2B13] = (SSLCipherSuite*)&Cipher310; - result[0xB10ECD53] = (SSLCipherSuite*)&Cipher311; - result[0xF44F4BC7] = (SSLCipherSuite*)&Cipher312; - result[0x49AF0BF] = (SSLCipherSuite*)&Cipher313; - result[0xDFAF479A] = (SSLCipherSuite*)&Cipher314; - result[0x82BF78CE] = (SSLCipherSuite*)&Cipher315; - result[0x46CD83C9] = (SSLCipherSuite*)&Cipher316; - result[0x8F7D7465] = (SSLCipherSuite*)&Cipher317; - result[0xBD9CDFE5] = (SSLCipherSuite*)&Cipher318; - result[0x92942203] = (SSLCipherSuite*)&Cipher319; - result[0x783C98AD] = (SSLCipherSuite*)&Cipher320; - result[0x92213B6D] = (SSLCipherSuite*)&Cipher321; - result[0xCFCB1A55] = (SSLCipherSuite*)&Cipher322; - result[0x54C2D55D] = (SSLCipherSuite*)&Cipher323; - result[0xDCD6F114] = (SSLCipherSuite*)&Cipher324; - result[0x6AD23C40] = (SSLCipherSuite*)&Cipher325; - result[0x5F5239D4] = (SSLCipherSuite*)&Cipher326; - result[0xAB27704B] = (SSLCipherSuite*)&Cipher327; - result[0xA3178D0C] = (SSLCipherSuite*)&Cipher328; - result[0x5DAAA195] = (SSLCipherSuite*)&Cipher329; - - return result; -} - -std::set createGreaseSet() -{ - uint16_t greaseExtensions[] = {0x0a0a, 0x1a1a, 0x2a2a, 0x3a3a, 0x4a4a, 0x5a5a, 0x6a6a, 0x7a7a, 0x8a8a, 0x9a9a, 0xaaaa, 0xbaba, 0xcaca, 0xdada, 0xeaea, 0xfafa}; - return std::set(greaseExtensions, greaseExtensions + 16); -} - -static const std::map CipherSuiteIdToObjectMap = createCipherSuiteIdToObjectMap(); - -static const std::map CipherSuiteStringToObjectMap = createCipherSuiteStringToObjectMap(); +static uint32_t hashString(std::string str) { + unsigned h = FIRST_HASH; + for (std::string::size_type i = 0; i < str.size(); ++i) { + h = (h * A) ^ (str[i] * B); + } + return h; +} + +static std::map +createCipherSuiteStringToObjectMap() { + std::map result; + + result[0x9F180F43] = (SSLCipherSuite*)&Cipher1; + result[0x97D9341F] = (SSLCipherSuite*)&Cipher2; + result[0x288FABA1] = (SSLCipherSuite*)&Cipher3; + result[0x9179C5BD] = (SSLCipherSuite*)&Cipher4; + result[0x68DF0C8F] = (SSLCipherSuite*)&Cipher5; + result[0x5FB32DF1] = (SSLCipherSuite*)&Cipher6; + result[0x2A1FC0FC] = (SSLCipherSuite*)&Cipher7; + result[0x5BF6459E] = (SSLCipherSuite*)&Cipher8; + result[0x60D692F4] = (SSLCipherSuite*)&Cipher9; + result[0x26A21427] = (SSLCipherSuite*)&Cipher10; + result[0xD3558C6D] = (SSLCipherSuite*)&Cipher11; + result[0xAE2673E9] = (SSLCipherSuite*)&Cipher12; + result[0xC63B19B0] = (SSLCipherSuite*)&Cipher13; + result[0xFE49B3BC] = (SSLCipherSuite*)&Cipher14; + result[0x625A86D5] = (SSLCipherSuite*)&Cipher15; + result[0x60FF1BD4] = (SSLCipherSuite*)&Cipher16; + result[0xE101D5C8] = (SSLCipherSuite*)&Cipher17; + result[0x422859E8] = (SSLCipherSuite*)&Cipher18; + result[0x88ABC503] = (SSLCipherSuite*)&Cipher19; + result[0x44284B1] = (SSLCipherSuite*)&Cipher20; + result[0xFD71B064] = (SSLCipherSuite*)&Cipher21; + result[0x76F35237] = (SSLCipherSuite*)&Cipher22; + result[0x7D93159D] = (SSLCipherSuite*)&Cipher23; + result[0x6E9D1AE2] = (SSLCipherSuite*)&Cipher24; + result[0xFA0974E4] = (SSLCipherSuite*)&Cipher25; + result[0xEC27ACB1] = (SSLCipherSuite*)&Cipher26; + result[0x6859C7A8] = (SSLCipherSuite*)&Cipher27; + result[0x55FD3D14] = (SSLCipherSuite*)&Cipher28; + result[0xA7650023] = (SSLCipherSuite*)&Cipher29; + result[0xDC042011] = (SSLCipherSuite*)&Cipher30; + result[0x94BFBF4D] = (SSLCipherSuite*)&Cipher31; + result[0x2FE24162] = (SSLCipherSuite*)&Cipher32; + result[0xC449D595] = (SSLCipherSuite*)&Cipher33; + result[0xE11292AF] = (SSLCipherSuite*)&Cipher34; + result[0x47D0643] = (SSLCipherSuite*)&Cipher35; + result[0xC9ABBA3C] = (SSLCipherSuite*)&Cipher36; + result[0x9F323A5F] = (SSLCipherSuite*)&Cipher37; + result[0xFBF78046] = (SSLCipherSuite*)&Cipher38; + result[0x859BD79F] = (SSLCipherSuite*)&Cipher39; + result[0xF9FBBB39] = (SSLCipherSuite*)&Cipher40; + result[0x63587748] = (SSLCipherSuite*)&Cipher41; + result[0xF84CAE79] = (SSLCipherSuite*)&Cipher42; + result[0xCA39F6F1] = (SSLCipherSuite*)&Cipher43; + result[0xDC4D17C1] = (SSLCipherSuite*)&Cipher44; + result[0x955FBE28] = (SSLCipherSuite*)&Cipher45; + result[0x73ED7B86] = (SSLCipherSuite*)&Cipher46; + result[0x14A51855] = (SSLCipherSuite*)&Cipher47; + result[0x2CE54061] = (SSLCipherSuite*)&Cipher48; + result[0x3360789A] = (SSLCipherSuite*)&Cipher49; + result[0xDFEF59B6] = (SSLCipherSuite*)&Cipher50; + result[0xE819855D] = (SSLCipherSuite*)&Cipher51; + result[0x24CC3946] = (SSLCipherSuite*)&Cipher52; + result[0x1CACB5FD] = (SSLCipherSuite*)&Cipher53; + result[0x40193001] = (SSLCipherSuite*)&Cipher54; + result[0xA3846DA2] = (SSLCipherSuite*)&Cipher55; + result[0x8F3B7CF6] = (SSLCipherSuite*)&Cipher56; + result[0xC7B09945] = (SSLCipherSuite*)&Cipher57; + result[0xD8172F82] = (SSLCipherSuite*)&Cipher58; + result[0xB6748503] = (SSLCipherSuite*)&Cipher59; + result[0xDB105043] = (SSLCipherSuite*)&Cipher60; + result[0x21E8AC2E] = (SSLCipherSuite*)&Cipher61; + result[0x55096FC2] = (SSLCipherSuite*)&Cipher62; + result[0x38F955AF] = (SSLCipherSuite*)&Cipher63; + result[0xBA8C1D77] = (SSLCipherSuite*)&Cipher64; + result[0x91128102] = (SSLCipherSuite*)&Cipher65; + result[0xA7ED740E] = (SSLCipherSuite*)&Cipher66; + result[0x75C4908B] = (SSLCipherSuite*)&Cipher67; + result[0xBC6C5E87] = (SSLCipherSuite*)&Cipher68; + result[0xA0499A2A] = (SSLCipherSuite*)&Cipher69; + result[0x4F0FFC13] = (SSLCipherSuite*)&Cipher70; + result[0xCCEE9996] = (SSLCipherSuite*)&Cipher71; + result[0x8570DA22] = (SSLCipherSuite*)&Cipher72; + result[0x75D4FD57] = (SSLCipherSuite*)&Cipher73; + result[0x602E04D3] = (SSLCipherSuite*)&Cipher74; + result[0x5EDC9C36] = (SSLCipherSuite*)&Cipher75; + result[0xE66C167E] = (SSLCipherSuite*)&Cipher76; + result[0x909F6D7B] = (SSLCipherSuite*)&Cipher77; + result[0x3C35B1AA] = (SSLCipherSuite*)&Cipher78; + result[0x6D4D1A2E] = (SSLCipherSuite*)&Cipher79; + result[0xBF788317] = (SSLCipherSuite*)&Cipher80; + result[0x5329738B] = (SSLCipherSuite*)&Cipher81; + result[0x7D11AB2] = (SSLCipherSuite*)&Cipher82; + result[0x461ACA21] = (SSLCipherSuite*)&Cipher83; + result[0x15404ADD] = (SSLCipherSuite*)&Cipher84; + result[0x3806AF6] = (SSLCipherSuite*)&Cipher85; + result[0xB2D80EB6] = (SSLCipherSuite*)&Cipher86; + result[0xE54425D1] = (SSLCipherSuite*)&Cipher87; + result[0x476457CD] = (SSLCipherSuite*)&Cipher88; + result[0x1D55E526] = (SSLCipherSuite*)&Cipher89; + result[0x953C69E6] = (SSLCipherSuite*)&Cipher90; + result[0x6ADE7E16] = (SSLCipherSuite*)&Cipher91; + result[0xE8C7BBE8] = (SSLCipherSuite*)&Cipher92; + result[0x623DC741] = (SSLCipherSuite*)&Cipher93; + result[0xF403E1] = (SSLCipherSuite*)&Cipher94; + result[0x90D8CADC] = (SSLCipherSuite*)&Cipher95; + result[0xC30D1199] = (SSLCipherSuite*)&Cipher96; + result[0x9CFB1B5D] = (SSLCipherSuite*)&Cipher97; + result[0x2D3B99E8] = (SSLCipherSuite*)&Cipher98; + result[0x4A9E8B0C] = (SSLCipherSuite*)&Cipher99; + result[0x16BD2351] = (SSLCipherSuite*)&Cipher100; + result[0x586BC20E] = (SSLCipherSuite*)&Cipher101; + result[0x996B90AA] = (SSLCipherSuite*)&Cipher102; + result[0x2F3871FE] = (SSLCipherSuite*)&Cipher103; + result[0xF2DD519A] = (SSLCipherSuite*)&Cipher104; + result[0x52615F23] = (SSLCipherSuite*)&Cipher105; + result[0xDEE51337] = (SSLCipherSuite*)&Cipher106; + result[0xB30890E2] = (SSLCipherSuite*)&Cipher107; + result[0x40F3FF3E] = (SSLCipherSuite*)&Cipher108; + result[0xE306EE17] = (SSLCipherSuite*)&Cipher109; + result[0x870C6FCB] = (SSLCipherSuite*)&Cipher110; + result[0xEB12CAEF] = (SSLCipherSuite*)&Cipher111; + result[0x68795983] = (SSLCipherSuite*)&Cipher112; + result[0x606BA9BE] = (SSLCipherSuite*)&Cipher113; + result[0x2C33475A] = (SSLCipherSuite*)&Cipher114; + result[0x640CAAEE] = (SSLCipherSuite*)&Cipher115; + result[0x6603488A] = (SSLCipherSuite*)&Cipher116; + result[0x8BA58643] = (SSLCipherSuite*)&Cipher117; + result[0x16059E57] = (SSLCipherSuite*)&Cipher118; + result[0x1B0606D3] = (SSLCipherSuite*)&Cipher119; + result[0x1CF76007] = (SSLCipherSuite*)&Cipher120; + result[0x618CE8F2] = (SSLCipherSuite*)&Cipher121; + result[0xE264D3B6] = (SSLCipherSuite*)&Cipher122; + result[0xB4C5AE63] = (SSLCipherSuite*)&Cipher123; + result[0x95DF4757] = (SSLCipherSuite*)&Cipher124; + result[0x1D1CF062] = (SSLCipherSuite*)&Cipher125; + result[0xE7AA2826] = (SSLCipherSuite*)&Cipher126; + result[0x38D94EE2] = (SSLCipherSuite*)&Cipher127; + result[0x889BA306] = (SSLCipherSuite*)&Cipher128; + result[0x5B816E75] = (SSLCipherSuite*)&Cipher129; + result[0x6F18C4DD] = (SSLCipherSuite*)&Cipher130; + result[0x2E1C05E0] = (SSLCipherSuite*)&Cipher131; + result[0x5592CFF7] = (SSLCipherSuite*)&Cipher132; + result[0x8221D38B] = (SSLCipherSuite*)&Cipher133; + result[0x9538105C] = (SSLCipherSuite*)&Cipher134; + result[0xF1100DD0] = (SSLCipherSuite*)&Cipher135; + result[0xF492EF1F] = (SSLCipherSuite*)&Cipher136; + result[0x226BD52C] = (SSLCipherSuite*)&Cipher137; + result[0xBBACE99F] = (SSLCipherSuite*)&Cipher138; + result[0xB3D4B66B] = (SSLCipherSuite*)&Cipher139; + result[0x8C619440] = (SSLCipherSuite*)&Cipher140; + result[0xE60B95C] = (SSLCipherSuite*)&Cipher141; + result[0x24F48D07] = (SSLCipherSuite*)&Cipher142; + result[0x15C7AF26] = (SSLCipherSuite*)&Cipher143; + result[0xCBA219CC] = (SSLCipherSuite*)&Cipher144; + result[0x9BD946BE] = (SSLCipherSuite*)&Cipher145; + result[0x7CCA46FF] = (SSLCipherSuite*)&Cipher146; + result[0x9FB51FA3] = (SSLCipherSuite*)&Cipher147; + result[0xC82A275B] = (SSLCipherSuite*)&Cipher148; + result[0x4472A583] = (SSLCipherSuite*)&Cipher149; + result[0xDBA3A5CF] = (SSLCipherSuite*)&Cipher150; + result[0x86338128] = (SSLCipherSuite*)&Cipher151; + result[0x8CCE91E4] = (SSLCipherSuite*)&Cipher152; + result[0xA81C6CA0] = (SSLCipherSuite*)&Cipher153; + result[0x6D80815E] = (SSLCipherSuite*)&Cipher154; + result[0xA383DEB0] = (SSLCipherSuite*)&Cipher155; + result[0x52073879] = (SSLCipherSuite*)&Cipher156; + result[0x5BA0B279] = (SSLCipherSuite*)&Cipher157; + result[0xD787CCC9] = (SSLCipherSuite*)&Cipher158; + result[0x9C86C6A9] = (SSLCipherSuite*)&Cipher159; + result[0xDAE424E5] = (SSLCipherSuite*)&Cipher160; + result[0x72C15ECE] = (SSLCipherSuite*)&Cipher161; + result[0xF0E8FB6E] = (SSLCipherSuite*)&Cipher162; + result[0xA2005D44] = (SSLCipherSuite*)&Cipher163; + result[0x77F79962] = (SSLCipherSuite*)&Cipher164; + result[0x25C8184C] = (SSLCipherSuite*)&Cipher165; + result[0x2070F8A5] = (SSLCipherSuite*)&Cipher166; + result[0x4189ED8D] = (SSLCipherSuite*)&Cipher167; + result[0x94C21B1] = (SSLCipherSuite*)&Cipher168; + result[0x1B0CB25C] = (SSLCipherSuite*)&Cipher169; + result[0xF18127A0] = (SSLCipherSuite*)&Cipher170; + result[0xC7FCA79A] = (SSLCipherSuite*)&Cipher171; + result[0xC1DEE135] = (SSLCipherSuite*)&Cipher172; + result[0xDA7143E9] = (SSLCipherSuite*)&Cipher173; + result[0xE82B6A2] = (SSLCipherSuite*)&Cipher174; + result[0x438EC1DD] = (SSLCipherSuite*)&Cipher175; + result[0x6BE32FA9] = (SSLCipherSuite*)&Cipher176; + result[0x18A5C375] = (SSLCipherSuite*)&Cipher177; + result[0x24136C59] = (SSLCipherSuite*)&Cipher178; + result[0x88529408] = (SSLCipherSuite*)&Cipher179; + result[0xADAB33FC] = (SSLCipherSuite*)&Cipher180; + result[0x79407DCB] = (SSLCipherSuite*)&Cipher181; + result[0x64970FFF] = (SSLCipherSuite*)&Cipher182; + result[0x8260DC9A] = (SSLCipherSuite*)&Cipher183; + result[0x4B74FFFE] = (SSLCipherSuite*)&Cipher184; + result[0x350DD5C8] = (SSLCipherSuite*)&Cipher185; + result[0x53E057C] = (SSLCipherSuite*)&Cipher186; + result[0x266020E1] = (SSLCipherSuite*)&Cipher187; + result[0xE6DB4B9D] = (SSLCipherSuite*)&Cipher188; + result[0x5A992E6] = (SSLCipherSuite*)&Cipher189; + result[0x1B33C882] = (SSLCipherSuite*)&Cipher190; + result[0x33579D2B] = (SSLCipherSuite*)&Cipher191; + result[0x1BD7F7FF] = (SSLCipherSuite*)&Cipher192; + result[0x39C59ED9] = (SSLCipherSuite*)&Cipher193; + result[0x4F19FB95] = (SSLCipherSuite*)&Cipher194; + result[0x8F4737BE] = (SSLCipherSuite*)&Cipher195; + result[0x2567AA9E] = (SSLCipherSuite*)&Cipher196; + result[0xEEF843DB] = (SSLCipherSuite*)&Cipher197; + result[0x978C4E4F] = (SSLCipherSuite*)&Cipher198; + result[0x2F8D17D9] = (SSLCipherSuite*)&Cipher199; + result[0x7F80393A] = (SSLCipherSuite*)&Cipher200; + result[0xDCA5AE1E] = (SSLCipherSuite*)&Cipher201; + result[0x74AA95D7] = (SSLCipherSuite*)&Cipher202; + result[0xB93174BB] = (SSLCipherSuite*)&Cipher203; + result[0x46E274FC] = (SSLCipherSuite*)&Cipher204; + result[0x9DC85330] = (SSLCipherSuite*)&Cipher205; + result[0x972847B8] = (SSLCipherSuite*)&Cipher206; + result[0xFCF61DAC] = (SSLCipherSuite*)&Cipher207; + result[0x73C0029B] = (SSLCipherSuite*)&Cipher208; + result[0xDA41D70F] = (SSLCipherSuite*)&Cipher209; + result[0x12CBC4E7] = (SSLCipherSuite*)&Cipher210; + result[0x8B2D5ACB] = (SSLCipherSuite*)&Cipher211; + result[0x28C0C084] = (SSLCipherSuite*)&Cipher212; + result[0x1602C1F8] = (SSLCipherSuite*)&Cipher213; + result[0xF5FB9ED] = (SSLCipherSuite*)&Cipher214; + result[0xE8E30E91] = (SSLCipherSuite*)&Cipher215; + result[0x70BA7792] = (SSLCipherSuite*)&Cipher216; + result[0x94C38076] = (SSLCipherSuite*)&Cipher217; + result[0xE5B3483F] = (SSLCipherSuite*)&Cipher218; + result[0x892DEBE3] = (SSLCipherSuite*)&Cipher219; + result[0x65609E50] = (SSLCipherSuite*)&Cipher220; + result[0xAB4F3F04] = (SSLCipherSuite*)&Cipher221; + result[0x8BFC76DA] = (SSLCipherSuite*)&Cipher222; + result[0xD4BDCD6] = (SSLCipherSuite*)&Cipher223; + result[0xCAB8F54A] = (SSLCipherSuite*)&Cipher224; + result[0xA10DCFC6] = (SSLCipherSuite*)&Cipher225; + result[0xD6B71B71] = (SSLCipherSuite*)&Cipher226; + result[0x6D775A2D] = (SSLCipherSuite*)&Cipher227; + result[0x7997AD16] = (SSLCipherSuite*)&Cipher228; + result[0x5338C632] = (SSLCipherSuite*)&Cipher229; + result[0x45F0598D] = (SSLCipherSuite*)&Cipher230; + result[0x2D8B6A99] = (SSLCipherSuite*)&Cipher231; + result[0xE14DC125] = (SSLCipherSuite*)&Cipher232; + result[0x1538351] = (SSLCipherSuite*)&Cipher233; + result[0x1A8CE530] = (SSLCipherSuite*)&Cipher234; + result[0xB01E69C4] = (SSLCipherSuite*)&Cipher235; + result[0xCCBF70D3] = (SSLCipherSuite*)&Cipher236; + result[0xEF664FE7] = (SSLCipherSuite*)&Cipher237; + result[0xF6ED4F52] = (SSLCipherSuite*)&Cipher238; + result[0x7D6522E] = (SSLCipherSuite*)&Cipher239; + result[0xBDB5C9B9] = (SSLCipherSuite*)&Cipher240; + result[0xD98D5C95] = (SSLCipherSuite*)&Cipher241; + result[0x92B92727] = (SSLCipherSuite*)&Cipher242; + result[0xB4FE570B] = (SSLCipherSuite*)&Cipher243; + result[0x8DCF7F77] = (SSLCipherSuite*)&Cipher244; + result[0x8208545B] = (SSLCipherSuite*)&Cipher245; + result[0x39A13298] = (SSLCipherSuite*)&Cipher246; + result[0xECB7070C] = (SSLCipherSuite*)&Cipher247; + result[0xAFA95F8A] = (SSLCipherSuite*)&Cipher248; + result[0x3D80E106] = (SSLCipherSuite*)&Cipher249; + result[0x83AF9B7A] = (SSLCipherSuite*)&Cipher250; + result[0x1FAAC2F6] = (SSLCipherSuite*)&Cipher251; + result[0x2AF11F51] = (SSLCipherSuite*)&Cipher252; + result[0xEDFD300D] = (SSLCipherSuite*)&Cipher253; + result[0x91AA268F] = (SSLCipherSuite*)&Cipher254; + result[0x9DF0E933] = (SSLCipherSuite*)&Cipher255; + result[0xF3951A6A] = (SSLCipherSuite*)&Cipher256; + result[0xE4FF8DCE] = (SSLCipherSuite*)&Cipher257; + result[0xBE4DFC61] = (SSLCipherSuite*)&Cipher258; + result[0xBB2CF025] = (SSLCipherSuite*)&Cipher259; + result[0x354D38A8] = (SSLCipherSuite*)&Cipher260; + result[0xE2444B9C] = (SSLCipherSuite*)&Cipher261; + result[0xF8298D43] = (SSLCipherSuite*)&Cipher262; + result[0x3EC413B7] = (SSLCipherSuite*)&Cipher263; + result[0xE0C75BE9] = (SSLCipherSuite*)&Cipher264; + result[0x7191BE45] = (SSLCipherSuite*)&Cipher265; + result[0xDDE7C439] = (SSLCipherSuite*)&Cipher266; + result[0xBE715415] = (SSLCipherSuite*)&Cipher267; + result[0x6CF8F9A6] = (SSLCipherSuite*)&Cipher268; + result[0x36D61242] = (SSLCipherSuite*)&Cipher269; + result[0xFA9BA9ED] = (SSLCipherSuite*)&Cipher270; + result[0x4588B179] = (SSLCipherSuite*)&Cipher271; + result[0xB3C246FA] = (SSLCipherSuite*)&Cipher272; + result[0x750EEB76] = (SSLCipherSuite*)&Cipher273; + result[0xC50ACCB2] = (SSLCipherSuite*)&Cipher274; + result[0x9555CD0E] = (SSLCipherSuite*)&Cipher275; + result[0xF25A659B] = (SSLCipherSuite*)&Cipher276; + result[0x1670E72F] = (SSLCipherSuite*)&Cipher277; + result[0xDB0DD6BC] = (SSLCipherSuite*)&Cipher278; + result[0x19CACD70] = (SSLCipherSuite*)&Cipher279; + result[0xC54D5481] = (SSLCipherSuite*)&Cipher280; + result[0x7BCCA2BD] = (SSLCipherSuite*)&Cipher281; + result[0xA851374E] = (SSLCipherSuite*)&Cipher282; + result[0xE887BEA] = (SSLCipherSuite*)&Cipher283; + result[0xDECAA7F9] = (SSLCipherSuite*)&Cipher284; + result[0x29DA73D5] = (SSLCipherSuite*)&Cipher285; + result[0xAC69ECC9] = (SSLCipherSuite*)&Cipher286; + result[0x6AE55625] = (SSLCipherSuite*)&Cipher287; + result[0x2BB24546] = (SSLCipherSuite*)&Cipher288; + result[0x7AB5F262] = (SSLCipherSuite*)&Cipher289; + result[0x3DB83990] = (SSLCipherSuite*)&Cipher290; + result[0xC852A244] = (SSLCipherSuite*)&Cipher291; + result[0xA3C952C0] = (SSLCipherSuite*)&Cipher292; + result[0xAF630C34] = (SSLCipherSuite*)&Cipher293; + result[0xD4EE22B] = (SSLCipherSuite*)&Cipher294; + result[0x83F4C5DF] = (SSLCipherSuite*)&Cipher295; + result[0xCCF6F918] = (SSLCipherSuite*)&Cipher296; + result[0x955C9E8C] = (SSLCipherSuite*)&Cipher297; + result[0xF3559154] = (SSLCipherSuite*)&Cipher298; + result[0xE0991C14] = (SSLCipherSuite*)&Cipher299; + result[0x7F6BF424] = (SSLCipherSuite*)&Cipher300; + result[0x4A129264] = (SSLCipherSuite*)&Cipher301; + result[0xB25E29E3] = (SSLCipherSuite*)&Cipher302; + result[0xA6E15A23] = (SSLCipherSuite*)&Cipher303; + result[0x637C5C53] = (SSLCipherSuite*)&Cipher304; + result[0x22794513] = (SSLCipherSuite*)&Cipher305; + result[0x4CE30464] = (SSLCipherSuite*)&Cipher306; + result[0xFDFE3B24] = (SSLCipherSuite*)&Cipher307; + result[0xDC8A2074] = (SSLCipherSuite*)&Cipher308; + result[0xFD448934] = (SSLCipherSuite*)&Cipher309; + result[0xF4FC2B13] = (SSLCipherSuite*)&Cipher310; + result[0xB10ECD53] = (SSLCipherSuite*)&Cipher311; + result[0xF44F4BC7] = (SSLCipherSuite*)&Cipher312; + result[0x49AF0BF] = (SSLCipherSuite*)&Cipher313; + result[0xDFAF479A] = (SSLCipherSuite*)&Cipher314; + result[0x82BF78CE] = (SSLCipherSuite*)&Cipher315; + result[0x46CD83C9] = (SSLCipherSuite*)&Cipher316; + result[0x8F7D7465] = (SSLCipherSuite*)&Cipher317; + result[0xBD9CDFE5] = (SSLCipherSuite*)&Cipher318; + result[0x92942203] = (SSLCipherSuite*)&Cipher319; + result[0x783C98AD] = (SSLCipherSuite*)&Cipher320; + result[0x92213B6D] = (SSLCipherSuite*)&Cipher321; + result[0xCFCB1A55] = (SSLCipherSuite*)&Cipher322; + result[0x54C2D55D] = (SSLCipherSuite*)&Cipher323; + result[0xDCD6F114] = (SSLCipherSuite*)&Cipher324; + result[0x6AD23C40] = (SSLCipherSuite*)&Cipher325; + result[0x5F5239D4] = (SSLCipherSuite*)&Cipher326; + result[0xAB27704B] = (SSLCipherSuite*)&Cipher327; + result[0xA3178D0C] = (SSLCipherSuite*)&Cipher328; + result[0x5DAAA195] = (SSLCipherSuite*)&Cipher329; + + return result; +} + +std::set createGreaseSet() { + uint16_t greaseExtensions[] = {0x0a0a, 0x1a1a, 0x2a2a, 0x3a3a, 0x4a4a, 0x5a5a, + 0x6a6a, 0x7a7a, 0x8a8a, 0x9a9a, 0xaaaa, 0xbaba, + 0xcaca, 0xdada, 0xeaea, 0xfafa}; + return std::set(greaseExtensions, greaseExtensions + 16); +} + +static const std::map CipherSuiteIdToObjectMap = + createCipherSuiteIdToObjectMap(); + +static const std::map CipherSuiteStringToObjectMap = + createCipherSuiteStringToObjectMap(); static const std::set GreaseSet = createGreaseSet(); -SSLCipherSuite* SSLCipherSuite::getCipherSuiteByID(uint16_t id) -{ - std::map::const_iterator pos = CipherSuiteIdToObjectMap.find(id); - if (pos == CipherSuiteIdToObjectMap.end()) - return nullptr; - else - return pos->second; +SSLCipherSuite* SSLCipherSuite::getCipherSuiteByID(uint16_t id) { + std::map::const_iterator pos = + CipherSuiteIdToObjectMap.find(id); + if (pos == CipherSuiteIdToObjectMap.end()) + return nullptr; + else + return pos->second; } -SSLCipherSuite* SSLCipherSuite::getCipherSuiteByName(std::string name) -{ - uint32_t nameHash = hashString(std::move(name)); - std::map::const_iterator pos = CipherSuiteStringToObjectMap.find(nameHash); - if (pos == CipherSuiteStringToObjectMap.end()) - return nullptr; - else - return pos->second; +SSLCipherSuite* SSLCipherSuite::getCipherSuiteByName(std::string name) { + uint32_t nameHash = hashString(std::move(name)); + std::map::const_iterator pos = + CipherSuiteStringToObjectMap.find(nameHash); + if (pos == CipherSuiteStringToObjectMap.end()) + return nullptr; + else + return pos->second; } // -------------------- // SSLExtension methods // -------------------- -SSLExtension::SSLExtension(uint8_t* data) -{ - m_RawData = data; -} +SSLExtension::SSLExtension(uint8_t* data) { m_RawData = data; } -SSLExtensionType SSLExtension::getType() const -{ - uint16_t typeAsInt = getTypeAsInt(); - if (typeAsInt <= 24 || typeAsInt == 35 || typeAsInt == 65281) - return (SSLExtensionType)typeAsInt; +SSLExtensionType SSLExtension::getType() const { + uint16_t typeAsInt = getTypeAsInt(); + if (typeAsInt <= 24 || typeAsInt == 35 || typeAsInt == 65281) + return (SSLExtensionType)typeAsInt; - return SSL_EXT_Unknown; + return SSL_EXT_Unknown; } -uint16_t SSLExtension::getTypeAsInt() const -{ - return be16toh(getExtensionStruct()->extensionType); +uint16_t SSLExtension::getTypeAsInt() const { + return be16toh(getExtensionStruct()->extensionType); } -uint16_t SSLExtension::getLength() const -{ - return be16toh(getExtensionStruct()->extensionDataLength); +uint16_t SSLExtension::getLength() const { + return be16toh(getExtensionStruct()->extensionDataLength); } -uint16_t SSLExtension::getTotalLength() const -{ - return getLength() + 2*sizeof(uint16_t); +uint16_t SSLExtension::getTotalLength() const { + return getLength() + 2 * sizeof(uint16_t); } -uint8_t* SSLExtension::getData() const -{ - if (getLength() > 0) - { - return getExtensionStruct()->extensionData; - } +uint8_t* SSLExtension::getData() const { + if (getLength() > 0) { + return getExtensionStruct()->extensionData; + } - return nullptr; + return nullptr; } - // ---------------------------------------- // SSLServerNameIndicationExtension methods // ---------------------------------------- -std::string SSLServerNameIndicationExtension::getHostName() const -{ - uint8_t* hostNameLengthPos = getData() + sizeof(uint16_t) + sizeof(uint8_t); - uint16_t hostNameLength = be16toh(*(uint16_t*)hostNameLengthPos); +std::string SSLServerNameIndicationExtension::getHostName() const { + uint8_t* hostNameLengthPos = getData() + sizeof(uint16_t) + sizeof(uint8_t); + uint16_t hostNameLength = be16toh(*(uint16_t*)hostNameLengthPos); - char* hostNameAsCharArr = new char[hostNameLength+1]; - memset(hostNameAsCharArr, 0, hostNameLength+1); - memcpy(hostNameAsCharArr, hostNameLengthPos + sizeof(uint16_t), hostNameLength); + char* hostNameAsCharArr = new char[hostNameLength + 1]; + memset(hostNameAsCharArr, 0, hostNameLength + 1); + memcpy(hostNameAsCharArr, hostNameLengthPos + sizeof(uint16_t), + hostNameLength); - std::string res = std::string(hostNameAsCharArr); - delete[] hostNameAsCharArr; - return res; + std::string res = std::string(hostNameAsCharArr); + delete[] hostNameAsCharArr; + return res; } - // ------------------------------------- // SSLSupportedVersionsExtension methods // ------------------------------------- -std::vector SSLSupportedVersionsExtension::getSupportedVersions() const -{ - std::vector result; - uint16_t extensionLength = getLength(); - if (extensionLength == 2) // server hello message - { - result.push_back(SSLVersion(be16toh(*(uint16_t*)getData()))); - } - else // client-hello message - { - uint8_t listLength = *getData(); - if (listLength != static_cast(extensionLength - 1) || listLength % 2 != 0) - return result; // bad extension data - - uint8_t* dataPtr = getData() + sizeof(uint8_t); - for (int i = 0; i < listLength / 2; i++) - { - result.push_back(SSLVersion(be16toh(*(uint16_t*)dataPtr))); - dataPtr += sizeof(uint16_t); - } - } - - return result; +std::vector +SSLSupportedVersionsExtension::getSupportedVersions() const { + std::vector result; + uint16_t extensionLength = getLength(); + if (extensionLength == 2) // server hello message + { + result.push_back(SSLVersion(be16toh(*(uint16_t*)getData()))); + } else // client-hello message + { + uint8_t listLength = *getData(); + if (listLength != static_cast(extensionLength - 1) || + listLength % 2 != 0) + return result; // bad extension data + + uint8_t* dataPtr = getData() + sizeof(uint8_t); + for (int i = 0; i < listLength / 2; i++) { + result.push_back(SSLVersion(be16toh(*(uint16_t*)dataPtr))); + dataPtr += sizeof(uint16_t); + } + } + + return result; } - // ----------------------------------- // TLSSupportedGroupsExtension methods // ----------------------------------- -std::vector TLSSupportedGroupsExtension::getSupportedGroups() const -{ - std::vector result; +std::vector TLSSupportedGroupsExtension::getSupportedGroups() const { + std::vector result; - uint16_t extensionLength = getLength(); - if (extensionLength < sizeof(uint16_t)) - return result; //bad extension data + uint16_t extensionLength = getLength(); + if (extensionLength < sizeof(uint16_t)) + return result; // bad extension data - uint16_t listLength = be16toh(*(uint16_t*)getData()); - if (listLength != (extensionLength - sizeof(uint16_t)) || listLength % 2 != 0) - return result; // bad extension data + uint16_t listLength = be16toh(*(uint16_t*)getData()); + if (listLength != (extensionLength - sizeof(uint16_t)) || listLength % 2 != 0) + return result; // bad extension data - uint8_t* dataPtr = getData() + sizeof(uint16_t); - for (int i = 0; i < listLength / 2; i++) - { - result.push_back(be16toh(*(uint16_t*)dataPtr)); - dataPtr += sizeof(uint16_t); - } + uint8_t* dataPtr = getData() + sizeof(uint16_t); + for (int i = 0; i < listLength / 2; i++) { + result.push_back(be16toh(*(uint16_t*)dataPtr)); + dataPtr += sizeof(uint16_t); + } - return result; + return result; } - // --------------------------------- // TLSECPointFormatExtension methods // --------------------------------- -std::vector TLSECPointFormatExtension::getECPointFormatList() const -{ - std::vector result; +std::vector TLSECPointFormatExtension::getECPointFormatList() const { + std::vector result; - uint16_t extensionLength = getLength(); - uint8_t listLength = *getData(); - if (listLength != static_cast(extensionLength - 1)) - return result; // bad extension data + uint16_t extensionLength = getLength(); + uint8_t listLength = *getData(); + if (listLength != static_cast(extensionLength - 1)) + return result; // bad extension data - uint8_t* dataPtr = getData() + sizeof(uint8_t); - for (int i = 0; i < listLength; i++) - { - result.push_back(*dataPtr); - dataPtr += sizeof(uint8_t); - } + uint8_t* dataPtr = getData() + sizeof(uint8_t); + for (int i = 0; i < listLength; i++) { + result.push_back(*dataPtr); + dataPtr += sizeof(uint8_t); + } - return result; + return result; } - // --------------------------- // SSLHandshakeMessage methods // --------------------------- -SSLHandshakeMessage::SSLHandshakeMessage(uint8_t* data, size_t dataLen, SSLHandshakeLayer* container) -{ - m_Data = data; - m_DataLen = dataLen; - m_Container = container; -} - -SSLHandshakeMessage* SSLHandshakeMessage::createHandshakeMessage(uint8_t* data, size_t dataLen, SSLHandshakeLayer* container) -{ - if (dataLen < sizeof(ssl_tls_handshake_layer)) - return nullptr; - - ssl_tls_handshake_layer* hsMsgHeader = (ssl_tls_handshake_layer*)data; - - if (dataLen >= 16 && (be64toh(*(uint64_t*)data) <= 0xFFFFFF || hsMsgHeader->length1 >= 1)) - // possibly Encrypted Handshake Message - // used heuristic: - // - handshake layer of more than 16 byte - // - first 5 bytes of the handshake message are zeroes - // - or wrong message length is over 64K - // - or message type makes so sense (handled through the switch statement) - return new SSLUnknownMessage(data, dataLen, container); - - switch (hsMsgHeader->handshakeType) - { - case SSL_CLIENT_HELLO: - return new SSLClientHelloMessage(data, dataLen, container); - case SSL_SERVER_HELLO: - return new SSLServerHelloMessage(data, dataLen, container); - case SSL_HELLO_REQUEST: - return new SSLHelloRequestMessage(data, dataLen, container); - case SSL_CERTIFICATE: - return new SSLCertificateMessage(data, dataLen, container); - case SSL_SERVER_KEY_EXCHANGE: - return new SSLServerKeyExchangeMessage(data, dataLen, container); - case SSL_CERTIFICATE_REQUEST: - return new SSLCertificateRequestMessage(data, dataLen, container); - case SSL_CERTIFICATE_VERIFY: - return new SSLCertificateVerifyMessage(data, dataLen, container); - case SSL_CLIENT_KEY_EXCHANGE: - return new SSLClientKeyExchangeMessage(data, dataLen, container); - case SSL_FINISHED: - return new SSLFinishedMessage(data, dataLen, container); - case SSL_SERVER_DONE: - return new SSLServerHelloDoneMessage(data, dataLen, container); - case SSL_NEW_SESSION_TICKET: - return new SSLNewSessionTicketMessage(data, dataLen, container); - default: - return new SSLUnknownMessage(data, dataLen, container); - } -} - -SSLHandshakeType SSLHandshakeMessage::getHandshakeType() const -{ - ssl_tls_handshake_layer* handshakeLayer = (ssl_tls_handshake_layer*)m_Data; - return (SSLHandshakeType)handshakeLayer->handshakeType; -} - -size_t SSLHandshakeMessage::getMessageLength() const -{ - ssl_tls_handshake_layer* handshakeLayer = (ssl_tls_handshake_layer*)m_Data; - //TODO: add handshakeLayer->length1 to the calculation - size_t len = sizeof(ssl_tls_handshake_layer) + be16toh(handshakeLayer->length2); - if (len > m_DataLen) - return m_DataLen; - - return len; -} - -bool SSLHandshakeMessage::isMessageComplete() const -{ - if (m_DataLen < sizeof(ssl_tls_handshake_layer)) - return false; - - ssl_tls_handshake_layer* handshakeLayer = (ssl_tls_handshake_layer*)m_Data; - size_t len = sizeof(ssl_tls_handshake_layer) + be16toh(handshakeLayer->length2); - return len <= m_DataLen; +SSLHandshakeMessage::SSLHandshakeMessage(uint8_t* data, size_t dataLen, + SSLHandshakeLayer* container) { + m_Data = data; + m_DataLen = dataLen; + m_Container = container; +} + +SSLHandshakeMessage* +SSLHandshakeMessage::createHandshakeMessage(uint8_t* data, size_t dataLen, + SSLHandshakeLayer* container) { + if (dataLen < sizeof(ssl_tls_handshake_layer)) + return nullptr; + + ssl_tls_handshake_layer* hsMsgHeader = (ssl_tls_handshake_layer*)data; + + if (dataLen >= 16 && + (be64toh(*(uint64_t*)data) <= 0xFFFFFF || hsMsgHeader->length1 >= 1)) + // possibly Encrypted Handshake Message + // used heuristic: + // - handshake layer of more than 16 byte + // - first 5 bytes of the handshake message are zeroes + // - or wrong message length is over 64K + // - or message type makes so sense (handled through the switch statement) + return new SSLUnknownMessage(data, dataLen, container); + + switch (hsMsgHeader->handshakeType) { + case SSL_CLIENT_HELLO: + return new SSLClientHelloMessage(data, dataLen, container); + case SSL_SERVER_HELLO: + return new SSLServerHelloMessage(data, dataLen, container); + case SSL_HELLO_REQUEST: + return new SSLHelloRequestMessage(data, dataLen, container); + case SSL_CERTIFICATE: + return new SSLCertificateMessage(data, dataLen, container); + case SSL_SERVER_KEY_EXCHANGE: + return new SSLServerKeyExchangeMessage(data, dataLen, container); + case SSL_CERTIFICATE_REQUEST: + return new SSLCertificateRequestMessage(data, dataLen, container); + case SSL_CERTIFICATE_VERIFY: + return new SSLCertificateVerifyMessage(data, dataLen, container); + case SSL_CLIENT_KEY_EXCHANGE: + return new SSLClientKeyExchangeMessage(data, dataLen, container); + case SSL_FINISHED: + return new SSLFinishedMessage(data, dataLen, container); + case SSL_SERVER_DONE: + return new SSLServerHelloDoneMessage(data, dataLen, container); + case SSL_NEW_SESSION_TICKET: + return new SSLNewSessionTicketMessage(data, dataLen, container); + default: + return new SSLUnknownMessage(data, dataLen, container); + } +} + +SSLHandshakeType SSLHandshakeMessage::getHandshakeType() const { + ssl_tls_handshake_layer* handshakeLayer = (ssl_tls_handshake_layer*)m_Data; + return (SSLHandshakeType)handshakeLayer->handshakeType; +} + +size_t SSLHandshakeMessage::getMessageLength() const { + ssl_tls_handshake_layer* handshakeLayer = (ssl_tls_handshake_layer*)m_Data; + // TODO: add handshakeLayer->length1 to the calculation + size_t len = + sizeof(ssl_tls_handshake_layer) + be16toh(handshakeLayer->length2); + if (len > m_DataLen) + return m_DataLen; + + return len; +} + +bool SSLHandshakeMessage::isMessageComplete() const { + if (m_DataLen < sizeof(ssl_tls_handshake_layer)) + return false; + + ssl_tls_handshake_layer* handshakeLayer = (ssl_tls_handshake_layer*)m_Data; + size_t len = + sizeof(ssl_tls_handshake_layer) + be16toh(handshakeLayer->length2); + return len <= m_DataLen; } - // ----------------------------- // SSLClientHelloMessage methods // ----------------------------- -SSLClientHelloMessage::SSLClientHelloMessage(uint8_t* data, size_t dataLen, SSLHandshakeLayer* container) - : SSLHandshakeMessage(data, dataLen, container) -{ - size_t extensionLengthOffset = sizeof(ssl_tls_client_server_hello) + sizeof(uint8_t) + getSessionIDLength() + sizeof(uint16_t) + sizeof(uint16_t)*getCipherSuiteCount() + 2*sizeof(uint8_t); - if (extensionLengthOffset + sizeof(uint16_t) > m_DataLen) - return; - - uint8_t* extensionLengthPos = m_Data + extensionLengthOffset; - uint16_t extensionLength = getExtensionsLength(); - uint8_t* extensionPos = extensionLengthPos + sizeof(uint16_t); - uint8_t* curPos = extensionPos; - size_t messageLen = getMessageLength(); - size_t minSSLExtensionLen = 2*sizeof(uint16_t); - while ((curPos - extensionPos) < (int)extensionLength - && (curPos - m_Data) < (int)messageLen - && (int)messageLen - (curPos - m_Data) >= (int)minSSLExtensionLen) - { - SSLExtension* newExt = nullptr; - uint16_t sslExtType = be16toh(*(uint16_t*)curPos); - switch (sslExtType) - { - case SSL_EXT_SERVER_NAME: - newExt = new SSLServerNameIndicationExtension(curPos); - break; - case SSL_EXT_SUPPORTED_VERSIONS: - newExt = new SSLSupportedVersionsExtension(curPos); - break; - case SSL_EXT_SUPPORTED_GROUPS: - newExt = new TLSSupportedGroupsExtension(curPos); - break; - case SSL_EXT_EC_POINT_FORMATS: - newExt = new TLSECPointFormatExtension(curPos); - break; - default: - newExt = new SSLExtension(curPos); - } - - // Total length can be zero only if getLength() == 0xfffc which is way too large - // and means that this extension (and packet) are malformed - if (newExt->getTotalLength() == 0) - { - delete newExt; - break; - } - - - m_ExtensionList.pushBack(newExt); - curPos += newExt->getTotalLength(); - } -} - -SSLVersion SSLClientHelloMessage::getHandshakeVersion() const -{ - uint16_t handshakeVersion = be16toh(getClientHelloHeader()->handshakeVersion); - return SSLVersion(handshakeVersion); -} - -uint8_t SSLClientHelloMessage::getSessionIDLength() const -{ - if (m_DataLen <= sizeof(ssl_tls_client_server_hello) + sizeof(uint8_t)) - return 0; - - uint8_t val = *(m_Data + sizeof(ssl_tls_client_server_hello)); - if ((size_t)val > m_DataLen - sizeof(ssl_tls_client_server_hello) - 1) - return (uint8_t)(m_DataLen - sizeof(ssl_tls_client_server_hello) - 1); - - return val; -} - -uint8_t* SSLClientHelloMessage::getSessionID() const -{ - if (getSessionIDLength() > 0) - return (m_Data + sizeof(ssl_tls_client_server_hello) + 1); - else - return nullptr; -} - -int SSLClientHelloMessage::getCipherSuiteCount() const -{ - size_t cipherSuiteOffset = sizeof(ssl_tls_client_server_hello) + sizeof(uint8_t) + getSessionIDLength(); - if (cipherSuiteOffset + sizeof(uint16_t) > m_DataLen) - return 0; - - uint16_t cipherSuiteLen = *(uint16_t*)(m_Data + cipherSuiteOffset); - return be16toh(cipherSuiteLen) / 2; -} - -SSLCipherSuite* SSLClientHelloMessage::getCipherSuite(int index) const -{ - bool isValid; - uint16_t id = getCipherSuiteID(index, isValid); - return (isValid ? SSLCipherSuite::getCipherSuiteByID(id) : nullptr); -} - -uint16_t SSLClientHelloMessage::getCipherSuiteID(int index, bool& isValid) const -{ - if (index < 0 || index >= getCipherSuiteCount()) - { - isValid = false; - return 0; - } - - size_t cipherSuiteStartOffset = sizeof(ssl_tls_client_server_hello) + sizeof(uint8_t) + getSessionIDLength() + sizeof(uint16_t); - if (cipherSuiteStartOffset + sizeof(uint16_t) * (index + 1) > m_DataLen) - { - isValid = false; - return 0; - } - - isValid = true; - uint16_t* cipherSuiteStartPos = (uint16_t*)(m_Data + cipherSuiteStartOffset); - return be16toh(*(cipherSuiteStartPos+index)); -} - -uint8_t SSLClientHelloMessage::getCompressionMethodsValue() const -{ - size_t offset = sizeof(ssl_tls_client_server_hello) + sizeof(uint8_t) + getSessionIDLength() + sizeof(uint16_t) + sizeof(uint16_t)*getCipherSuiteCount() + sizeof(uint8_t); - if (offset + sizeof(uint8_t) > m_DataLen) - return 0xff; - - uint8_t* pos = m_Data + offset; - return *pos; -} - -int SSLClientHelloMessage::getExtensionCount() const -{ - return m_ExtensionList.size(); +SSLClientHelloMessage::SSLClientHelloMessage(uint8_t* data, size_t dataLen, + SSLHandshakeLayer* container) + : SSLHandshakeMessage(data, dataLen, container) { + size_t extensionLengthOffset = + sizeof(ssl_tls_client_server_hello) + sizeof(uint8_t) + + getSessionIDLength() + sizeof(uint16_t) + + sizeof(uint16_t) * getCipherSuiteCount() + 2 * sizeof(uint8_t); + if (extensionLengthOffset + sizeof(uint16_t) > m_DataLen) + return; + + uint8_t* extensionLengthPos = m_Data + extensionLengthOffset; + uint16_t extensionLength = getExtensionsLength(); + uint8_t* extensionPos = extensionLengthPos + sizeof(uint16_t); + uint8_t* curPos = extensionPos; + size_t messageLen = getMessageLength(); + size_t minSSLExtensionLen = 2 * sizeof(uint16_t); + while ((curPos - extensionPos) < (int)extensionLength && + (curPos - m_Data) < (int)messageLen && + (int)messageLen - (curPos - m_Data) >= (int)minSSLExtensionLen) { + SSLExtension* newExt = nullptr; + uint16_t sslExtType = be16toh(*(uint16_t*)curPos); + switch (sslExtType) { + case SSL_EXT_SERVER_NAME: + newExt = new SSLServerNameIndicationExtension(curPos); + break; + case SSL_EXT_SUPPORTED_VERSIONS: + newExt = new SSLSupportedVersionsExtension(curPos); + break; + case SSL_EXT_SUPPORTED_GROUPS: + newExt = new TLSSupportedGroupsExtension(curPos); + break; + case SSL_EXT_EC_POINT_FORMATS: + newExt = new TLSECPointFormatExtension(curPos); + break; + default: + newExt = new SSLExtension(curPos); + } + + // Total length can be zero only if getLength() == 0xfffc which is way too + // large and means that this extension (and packet) are malformed + if (newExt->getTotalLength() == 0) { + delete newExt; + break; + } + + m_ExtensionList.pushBack(newExt); + curPos += newExt->getTotalLength(); + } +} + +SSLVersion SSLClientHelloMessage::getHandshakeVersion() const { + uint16_t handshakeVersion = be16toh(getClientHelloHeader()->handshakeVersion); + return SSLVersion(handshakeVersion); +} + +uint8_t SSLClientHelloMessage::getSessionIDLength() const { + if (m_DataLen <= sizeof(ssl_tls_client_server_hello) + sizeof(uint8_t)) + return 0; + + uint8_t val = *(m_Data + sizeof(ssl_tls_client_server_hello)); + if ((size_t)val > m_DataLen - sizeof(ssl_tls_client_server_hello) - 1) + return (uint8_t)(m_DataLen - sizeof(ssl_tls_client_server_hello) - 1); + + return val; +} + +uint8_t* SSLClientHelloMessage::getSessionID() const { + if (getSessionIDLength() > 0) + return (m_Data + sizeof(ssl_tls_client_server_hello) + 1); + else + return nullptr; +} + +int SSLClientHelloMessage::getCipherSuiteCount() const { + size_t cipherSuiteOffset = sizeof(ssl_tls_client_server_hello) + + sizeof(uint8_t) + getSessionIDLength(); + if (cipherSuiteOffset + sizeof(uint16_t) > m_DataLen) + return 0; + + uint16_t cipherSuiteLen = *(uint16_t*)(m_Data + cipherSuiteOffset); + return be16toh(cipherSuiteLen) / 2; +} + +SSLCipherSuite* SSLClientHelloMessage::getCipherSuite(int index) const { + bool isValid; + uint16_t id = getCipherSuiteID(index, isValid); + return (isValid ? SSLCipherSuite::getCipherSuiteByID(id) : nullptr); +} + +uint16_t SSLClientHelloMessage::getCipherSuiteID(int index, + bool& isValid) const { + if (index < 0 || index >= getCipherSuiteCount()) { + isValid = false; + return 0; + } + + size_t cipherSuiteStartOffset = sizeof(ssl_tls_client_server_hello) + + sizeof(uint8_t) + getSessionIDLength() + + sizeof(uint16_t); + if (cipherSuiteStartOffset + sizeof(uint16_t) * (index + 1) > m_DataLen) { + isValid = false; + return 0; + } + + isValid = true; + uint16_t* cipherSuiteStartPos = (uint16_t*)(m_Data + cipherSuiteStartOffset); + return be16toh(*(cipherSuiteStartPos + index)); +} + +uint8_t SSLClientHelloMessage::getCompressionMethodsValue() const { + size_t offset = sizeof(ssl_tls_client_server_hello) + sizeof(uint8_t) + + getSessionIDLength() + sizeof(uint16_t) + + sizeof(uint16_t) * getCipherSuiteCount() + sizeof(uint8_t); + if (offset + sizeof(uint8_t) > m_DataLen) + return 0xff; + + uint8_t* pos = m_Data + offset; + return *pos; +} + +int SSLClientHelloMessage::getExtensionCount() const { + return m_ExtensionList.size(); } -uint16_t SSLClientHelloMessage::getExtensionsLength() const -{ - size_t extensionLengthOffset = sizeof(ssl_tls_client_server_hello) + sizeof(uint8_t) + getSessionIDLength() + sizeof(uint16_t) + sizeof(uint16_t)*getCipherSuiteCount() + 2*sizeof(uint8_t); - if (extensionLengthOffset + sizeof(uint16_t) > m_DataLen) - return 0; +uint16_t SSLClientHelloMessage::getExtensionsLength() const { + size_t extensionLengthOffset = + sizeof(ssl_tls_client_server_hello) + sizeof(uint8_t) + + getSessionIDLength() + sizeof(uint16_t) + + sizeof(uint16_t) * getCipherSuiteCount() + 2 * sizeof(uint8_t); + if (extensionLengthOffset + sizeof(uint16_t) > m_DataLen) + return 0; - uint8_t* extensionLengthPos = m_Data + extensionLengthOffset; - return be16toh(*(uint16_t*)extensionLengthPos); + uint8_t* extensionLengthPos = m_Data + extensionLengthOffset; + return be16toh(*(uint16_t*)extensionLengthPos); } -SSLExtension* SSLClientHelloMessage::getExtension(int index) const -{ - return const_cast(m_ExtensionList.at(index)); +SSLExtension* SSLClientHelloMessage::getExtension(int index) const { + return const_cast(m_ExtensionList.at(index)); } -SSLExtension* SSLClientHelloMessage::getExtensionOfType(uint16_t type) const -{ - size_t vecSize = m_ExtensionList.size(); - for (size_t i = 0; i < vecSize; i++) - { - SSLExtension* curElem = const_cast(m_ExtensionList.at(i)); - if (curElem->getTypeAsInt() == type) - return curElem; - } +SSLExtension* SSLClientHelloMessage::getExtensionOfType(uint16_t type) const { + size_t vecSize = m_ExtensionList.size(); + for (size_t i = 0; i < vecSize; i++) { + SSLExtension* curElem = const_cast(m_ExtensionList.at(i)); + if (curElem->getTypeAsInt() == type) + return curElem; + } - return nullptr; + return nullptr; } -SSLExtension* SSLClientHelloMessage::getExtensionOfType(SSLExtensionType type) const -{ - size_t vecSize = m_ExtensionList.size(); - for (size_t i = 0; i < vecSize; i++) - { - SSLExtension* curElem = const_cast(m_ExtensionList.at(i)); - if (curElem->getType() == type) - return curElem; - } +SSLExtension* +SSLClientHelloMessage::getExtensionOfType(SSLExtensionType type) const { + size_t vecSize = m_ExtensionList.size(); + for (size_t i = 0; i < vecSize; i++) { + SSLExtension* curElem = const_cast(m_ExtensionList.at(i)); + if (curElem->getType() == type) + return curElem; + } - return nullptr; + return nullptr; } -SSLClientHelloMessage::ClientHelloTLSFingerprint SSLClientHelloMessage::generateTLSFingerprint() const -{ - SSLClientHelloMessage::ClientHelloTLSFingerprint result; +SSLClientHelloMessage::ClientHelloTLSFingerprint +SSLClientHelloMessage::generateTLSFingerprint() const { + SSLClientHelloMessage::ClientHelloTLSFingerprint result; - // extract version - result.tlsVersion = getHandshakeVersion().asUInt(); + // extract version + result.tlsVersion = getHandshakeVersion().asUInt(); - // extract cipher suites - int cipherSuiteCount = getCipherSuiteCount(); - for (int i = 0; i < cipherSuiteCount; i++) - { - bool isValid = false; - uint16_t cipherSuiteID = getCipherSuiteID(i, isValid); - if (isValid && GreaseSet.find(cipherSuiteID) == GreaseSet.end()) - result.cipherSuites.push_back(cipherSuiteID); - } + // extract cipher suites + int cipherSuiteCount = getCipherSuiteCount(); + for (int i = 0; i < cipherSuiteCount; i++) { + bool isValid = false; + uint16_t cipherSuiteID = getCipherSuiteID(i, isValid); + if (isValid && GreaseSet.find(cipherSuiteID) == GreaseSet.end()) + result.cipherSuites.push_back(cipherSuiteID); + } - // extract extensions - int extensionCount = getExtensionCount(); - for (int i = 0; i < extensionCount; i++) - { - uint16_t extensionType = getExtension(i)->getTypeAsInt(); - if (GreaseSet.find(extensionType) != GreaseSet.end()) - continue; + // extract extensions + int extensionCount = getExtensionCount(); + for (int i = 0; i < extensionCount; i++) { + uint16_t extensionType = getExtension(i)->getTypeAsInt(); + if (GreaseSet.find(extensionType) != GreaseSet.end()) + continue; - result.extensions.push_back(extensionType); - } + result.extensions.push_back(extensionType); + } - // extract supported groups - TLSSupportedGroupsExtension* supportedGroupsExt = getExtensionOfType(); - if (supportedGroupsExt != nullptr) - { - std::vector supportedGroups = supportedGroupsExt->getSupportedGroups(); - for (std::vector::const_iterator iter = supportedGroups.begin(); iter != supportedGroups.end(); iter++) - if (GreaseSet.find(*iter) == GreaseSet.end()) - result.supportedGroups.push_back(*iter); - } + // extract supported groups + TLSSupportedGroupsExtension* supportedGroupsExt = + getExtensionOfType(); + if (supportedGroupsExt != nullptr) { + std::vector supportedGroups = + supportedGroupsExt->getSupportedGroups(); + for (std::vector::const_iterator iter = supportedGroups.begin(); + iter != supportedGroups.end(); iter++) + if (GreaseSet.find(*iter) == GreaseSet.end()) + result.supportedGroups.push_back(*iter); + } - // extract EC point formats - TLSECPointFormatExtension* ecPointFormatExt = getExtensionOfType(); - if (ecPointFormatExt != nullptr) - { - result.ecPointFormats = ecPointFormatExt->getECPointFormatList(); - } + // extract EC point formats + TLSECPointFormatExtension* ecPointFormatExt = + getExtensionOfType(); + if (ecPointFormatExt != nullptr) { + result.ecPointFormats = ecPointFormatExt->getECPointFormatList(); + } - return result; + return result; } -std::string SSLClientHelloMessage::toString() const -{ - return "Client Hello message"; +std::string SSLClientHelloMessage::toString() const { + return "Client Hello message"; } - // ------------------------------------------------ // SSLClientHelloMessage::ClientHelloTLSFingerprint // ------------------------------------------------ -std::string SSLClientHelloMessage::ClientHelloTLSFingerprint::toString() -{ - std::stringstream tlsFingerprint; - - // add version - tlsFingerprint << tlsVersion << ","; - - // add cipher suites - bool firstCipher = true; - for (std::vector::const_iterator iter = cipherSuites.begin(); iter != cipherSuites.end(); iter++) - { - tlsFingerprint << (firstCipher ? "" : "-") << *iter; - firstCipher = false; - } - tlsFingerprint << ","; - - // add extensions - bool firstExtension = true; - for (std::vector::const_iterator iter = extensions.begin(); iter != extensions.end(); iter++) - { - tlsFingerprint << (firstExtension ? "" : "-") << *iter; - firstExtension = false; - } - tlsFingerprint << ","; +std::string SSLClientHelloMessage::ClientHelloTLSFingerprint::toString() { + std::stringstream tlsFingerprint; + + // add version + tlsFingerprint << tlsVersion << ","; + + // add cipher suites + bool firstCipher = true; + for (std::vector::const_iterator iter = cipherSuites.begin(); + iter != cipherSuites.end(); iter++) { + tlsFingerprint << (firstCipher ? "" : "-") << *iter; + firstCipher = false; + } + tlsFingerprint << ","; + + // add extensions + bool firstExtension = true; + for (std::vector::const_iterator iter = extensions.begin(); + iter != extensions.end(); iter++) { + tlsFingerprint << (firstExtension ? "" : "-") << *iter; + firstExtension = false; + } + tlsFingerprint << ","; + + // add supported groups + bool firstGroup = true; + for (std::vector::const_iterator iter = supportedGroups.begin(); + iter != supportedGroups.end(); iter++) { + tlsFingerprint << (firstGroup ? "" : "-") << (*iter); + firstGroup = false; + } + tlsFingerprint << ","; + + // add EC point formats + bool firstPointFormat = true; + for (std::vector::iterator iter = ecPointFormats.begin(); + iter != ecPointFormats.end(); iter++) { + tlsFingerprint << (firstPointFormat ? "" : "-") << (int)(*iter); + firstPointFormat = false; + } + + return tlsFingerprint.str(); +} + +std::string SSLClientHelloMessage::ClientHelloTLSFingerprint::toMD5() { + return toStringAndMD5().second; +} + +std::pair +SSLClientHelloMessage::ClientHelloTLSFingerprint::toStringAndMD5() { + std::string str = toString(); + MD5 md5; + return std::pair(str, md5(str)); +} - // add supported groups - bool firstGroup = true; - for (std::vector::const_iterator iter = supportedGroups.begin(); iter != supportedGroups.end(); iter++) - { - tlsFingerprint << (firstGroup ? "" : "-") << (*iter); - firstGroup = false; - } - tlsFingerprint << ","; +// ----------------------------- +// SSLServerHelloMessage methods +// ----------------------------- - // add EC point formats - bool firstPointFormat = true; - for (std::vector::iterator iter = ecPointFormats.begin(); iter != ecPointFormats.end(); iter++) - { - tlsFingerprint << (firstPointFormat ? "" : "-") << (int)(*iter); - firstPointFormat = false; - } +SSLServerHelloMessage::SSLServerHelloMessage(uint8_t* data, size_t dataLen, + SSLHandshakeLayer* container) + : SSLHandshakeMessage(data, dataLen, container) { + size_t extensionLengthOffset = sizeof(ssl_tls_client_server_hello) + + sizeof(uint8_t) + getSessionIDLength() + + sizeof(uint16_t) + sizeof(uint8_t); + if (extensionLengthOffset + sizeof(uint16_t) > m_DataLen) + return; + + uint8_t* extensionLengthPos = m_Data + extensionLengthOffset; + uint16_t extensionLength = getExtensionsLength(); + uint8_t* extensionPos = extensionLengthPos + sizeof(uint16_t); + uint8_t* curPos = extensionPos; + size_t messageLen = getMessageLength(); + size_t minSSLExtensionLen = 2 * sizeof(uint16_t); + while ((curPos - extensionPos) < (int)extensionLength && + (curPos - m_Data) < (int)messageLen && + (int)messageLen - (curPos - m_Data) >= (int)minSSLExtensionLen) { + SSLExtension* newExt = nullptr; + uint16_t sslExtType = be16toh(*(uint16_t*)curPos); + switch (sslExtType) { + case SSL_EXT_SERVER_NAME: + newExt = new SSLServerNameIndicationExtension(curPos); + break; + case SSL_EXT_SUPPORTED_VERSIONS: + newExt = new SSLSupportedVersionsExtension(curPos); + break; + case SSL_EXT_SUPPORTED_GROUPS: + newExt = new TLSSupportedGroupsExtension(curPos); + break; + case SSL_EXT_EC_POINT_FORMATS: + newExt = new TLSECPointFormatExtension(curPos); + break; + default: + newExt = new SSLExtension(curPos); + } + + if (newExt->getTotalLength() == 0) { + delete newExt; + break; + } + + m_ExtensionList.pushBack(newExt); + curPos += newExt->getTotalLength(); + } +} + +SSLVersion SSLServerHelloMessage::getHandshakeVersion() const { + SSLSupportedVersionsExtension* supportedVersionsExt = + getExtensionOfType(); + if (supportedVersionsExt != nullptr) { + std::vector supportedVersions = + supportedVersionsExt->getSupportedVersions(); + if (supportedVersions.size() == 1) + return supportedVersions[0]; + } + + uint16_t handshakeVersion = be16toh(getServerHelloHeader()->handshakeVersion); + return SSLVersion(handshakeVersion); +} +uint8_t SSLServerHelloMessage::getSessionIDLength() const { + if (m_DataLen <= sizeof(ssl_tls_client_server_hello) + sizeof(uint8_t)) + return 0; + + uint8_t val = *(m_Data + sizeof(ssl_tls_client_server_hello)); + if ((size_t)val > m_DataLen - sizeof(ssl_tls_client_server_hello) - 1) + return (uint8_t)(m_DataLen - sizeof(ssl_tls_client_server_hello) - 1); + + return val; +} + +uint8_t* SSLServerHelloMessage::getSessionID() const { + if (getSessionIDLength() > 0) + return (m_Data + sizeof(ssl_tls_client_server_hello) + 1); + else + return nullptr; +} - return tlsFingerprint.str(); +SSLCipherSuite* SSLServerHelloMessage::getCipherSuite() const { + bool isValid; + uint16_t id = getCipherSuiteID(isValid); + return (isValid ? SSLCipherSuite::getCipherSuiteByID(id) : nullptr); } -std::string SSLClientHelloMessage::ClientHelloTLSFingerprint::toMD5() -{ - return toStringAndMD5().second; -} +uint16_t SSLServerHelloMessage::getCipherSuiteID(bool& isValid) const { + size_t cipherSuiteStartOffset = sizeof(ssl_tls_client_server_hello) + + sizeof(uint8_t) + getSessionIDLength(); + if (cipherSuiteStartOffset + sizeof(uint16_t) > m_DataLen) { + isValid = false; + return 0; + } -std::pair SSLClientHelloMessage::ClientHelloTLSFingerprint::toStringAndMD5() -{ - std::string str = toString(); - MD5 md5; - return std::pair(str, md5(str)); + isValid = true; + uint16_t* cipherSuiteStartPos = (uint16_t*)(m_Data + cipherSuiteStartOffset); + return be16toh(*(cipherSuiteStartPos)); } +uint8_t SSLServerHelloMessage::getCompressionMethodsValue() const { + size_t offset = sizeof(ssl_tls_client_server_hello) + sizeof(uint8_t) + + getSessionIDLength() + sizeof(uint16_t); + if (offset + sizeof(uint8_t) > m_DataLen) + return 0xff; + + uint8_t* pos = m_Data + offset; + return *pos; +} -// ----------------------------- -// SSLServerHelloMessage methods -// ----------------------------- - -SSLServerHelloMessage::SSLServerHelloMessage(uint8_t* data, size_t dataLen, SSLHandshakeLayer* container) - : SSLHandshakeMessage(data, dataLen, container) -{ - size_t extensionLengthOffset = sizeof(ssl_tls_client_server_hello) + sizeof(uint8_t) + getSessionIDLength() + sizeof(uint16_t) + sizeof(uint8_t); - if (extensionLengthOffset + sizeof(uint16_t) > m_DataLen) - return; - - uint8_t* extensionLengthPos = m_Data + extensionLengthOffset; - uint16_t extensionLength = getExtensionsLength(); - uint8_t* extensionPos = extensionLengthPos + sizeof(uint16_t); - uint8_t* curPos = extensionPos; - size_t messageLen = getMessageLength(); - size_t minSSLExtensionLen = 2*sizeof(uint16_t); - while ((curPos - extensionPos) < (int)extensionLength - && (curPos - m_Data) < (int)messageLen - && (int)messageLen - (curPos - m_Data) >= (int)minSSLExtensionLen) - { - SSLExtension* newExt = nullptr; - uint16_t sslExtType = be16toh(*(uint16_t*)curPos); - switch (sslExtType) - { - case SSL_EXT_SERVER_NAME: - newExt = new SSLServerNameIndicationExtension(curPos); - break; - case SSL_EXT_SUPPORTED_VERSIONS: - newExt = new SSLSupportedVersionsExtension(curPos); - break; - case SSL_EXT_SUPPORTED_GROUPS: - newExt = new TLSSupportedGroupsExtension(curPos); - break; - case SSL_EXT_EC_POINT_FORMATS: - newExt = new TLSECPointFormatExtension(curPos); - break; - default: - newExt = new SSLExtension(curPos); - } - - if (newExt->getTotalLength() == 0) - { - delete newExt; - break; - } - - m_ExtensionList.pushBack(newExt); - curPos += newExt->getTotalLength(); - } -} - -SSLVersion SSLServerHelloMessage::getHandshakeVersion() const -{ - SSLSupportedVersionsExtension* supportedVersionsExt = getExtensionOfType(); - if (supportedVersionsExt != nullptr) - { - std::vector supportedVersions = supportedVersionsExt->getSupportedVersions(); - if (supportedVersions.size() == 1) - return supportedVersions[0]; - } - - uint16_t handshakeVersion = be16toh(getServerHelloHeader()->handshakeVersion); - return SSLVersion(handshakeVersion); - -} -uint8_t SSLServerHelloMessage::getSessionIDLength() const -{ - if (m_DataLen <= sizeof(ssl_tls_client_server_hello) + sizeof(uint8_t)) - return 0; - - uint8_t val = *(m_Data + sizeof(ssl_tls_client_server_hello)); - if ((size_t)val > m_DataLen - sizeof(ssl_tls_client_server_hello) - 1) - return (uint8_t)(m_DataLen - sizeof(ssl_tls_client_server_hello) - 1); - - return val; -} - -uint8_t* SSLServerHelloMessage::getSessionID() const -{ - if (getSessionIDLength() > 0) - return (m_Data + sizeof(ssl_tls_client_server_hello) + 1); - else - return nullptr; -} - -SSLCipherSuite* SSLServerHelloMessage::getCipherSuite() const -{ - bool isValid; - uint16_t id = getCipherSuiteID(isValid); - return (isValid ? SSLCipherSuite::getCipherSuiteByID(id) : nullptr); -} - -uint16_t SSLServerHelloMessage::getCipherSuiteID(bool& isValid) const -{ - size_t cipherSuiteStartOffset = sizeof(ssl_tls_client_server_hello) + sizeof(uint8_t) + getSessionIDLength(); - if (cipherSuiteStartOffset + sizeof(uint16_t) > m_DataLen) - { - isValid = false; - return 0; - } - - isValid = true; - uint16_t* cipherSuiteStartPos = (uint16_t*)(m_Data + cipherSuiteStartOffset); - return be16toh(*(cipherSuiteStartPos)); -} - -uint8_t SSLServerHelloMessage::getCompressionMethodsValue() const -{ - size_t offset = sizeof(ssl_tls_client_server_hello) + sizeof(uint8_t) + getSessionIDLength() + sizeof(uint16_t); - if (offset + sizeof(uint8_t) > m_DataLen) - return 0xff; - - uint8_t* pos = m_Data + offset; - return *pos; -} - -int SSLServerHelloMessage::getExtensionCount() const -{ - return m_ExtensionList.size(); +int SSLServerHelloMessage::getExtensionCount() const { + return m_ExtensionList.size(); } -uint16_t SSLServerHelloMessage::getExtensionsLength() const -{ - size_t extensionLengthOffset = sizeof(ssl_tls_client_server_hello) + sizeof(uint8_t) + getSessionIDLength() + sizeof(uint16_t) + sizeof(uint8_t); - if (extensionLengthOffset + sizeof(uint16_t) > m_DataLen) - return 0; +uint16_t SSLServerHelloMessage::getExtensionsLength() const { + size_t extensionLengthOffset = sizeof(ssl_tls_client_server_hello) + + sizeof(uint8_t) + getSessionIDLength() + + sizeof(uint16_t) + sizeof(uint8_t); + if (extensionLengthOffset + sizeof(uint16_t) > m_DataLen) + return 0; - uint16_t* extensionLengthPos = (uint16_t*)(m_Data + extensionLengthOffset); - return be16toh(*extensionLengthPos); + uint16_t* extensionLengthPos = (uint16_t*)(m_Data + extensionLengthOffset); + return be16toh(*extensionLengthPos); } -SSLExtension* SSLServerHelloMessage::getExtension(int index) const -{ - if (index < 0 || index >= (int)m_ExtensionList.size()) - return nullptr; +SSLExtension* SSLServerHelloMessage::getExtension(int index) const { + if (index < 0 || index >= (int)m_ExtensionList.size()) + return nullptr; - return const_cast(m_ExtensionList.at(index)); + return const_cast(m_ExtensionList.at(index)); } -SSLExtension* SSLServerHelloMessage::getExtensionOfType(uint16_t type) const -{ - size_t vecSize = m_ExtensionList.size(); - for (size_t i = 0; i < vecSize; i++) - { - SSLExtension* curElem = const_cast(m_ExtensionList.at(i)); - if (curElem->getType() == type) - return curElem; - } +SSLExtension* SSLServerHelloMessage::getExtensionOfType(uint16_t type) const { + size_t vecSize = m_ExtensionList.size(); + for (size_t i = 0; i < vecSize; i++) { + SSLExtension* curElem = const_cast(m_ExtensionList.at(i)); + if (curElem->getType() == type) + return curElem; + } - return nullptr; + return nullptr; } -SSLExtension* SSLServerHelloMessage::getExtensionOfType(SSLExtensionType type) const -{ - size_t vecSize = m_ExtensionList.size(); - for (size_t i = 0; i < vecSize; i++) - { - SSLExtension* curElem = const_cast(m_ExtensionList.at(i)); - if (curElem->getType() == type) - return curElem; - } +SSLExtension* +SSLServerHelloMessage::getExtensionOfType(SSLExtensionType type) const { + size_t vecSize = m_ExtensionList.size(); + for (size_t i = 0; i < vecSize; i++) { + SSLExtension* curElem = const_cast(m_ExtensionList.at(i)); + if (curElem->getType() == type) + return curElem; + } - return nullptr; + return nullptr; } -SSLServerHelloMessage::ServerHelloTLSFingerprint SSLServerHelloMessage::generateTLSFingerprint() const -{ - SSLServerHelloMessage::ServerHelloTLSFingerprint result; +SSLServerHelloMessage::ServerHelloTLSFingerprint +SSLServerHelloMessage::generateTLSFingerprint() const { + SSLServerHelloMessage::ServerHelloTLSFingerprint result; - // extract version - result.tlsVersion = getHandshakeVersion().asUInt(); + // extract version + result.tlsVersion = getHandshakeVersion().asUInt(); - // extract cipher suite - bool isValid; - uint16_t cipherSuite = getCipherSuiteID(isValid); - result.cipherSuite = (isValid ? cipherSuite : 0); + // extract cipher suite + bool isValid; + uint16_t cipherSuite = getCipherSuiteID(isValid); + result.cipherSuite = (isValid ? cipherSuite : 0); - // extract extensions - int extensionCount = getExtensionCount(); - for (int i = 0; i < extensionCount; i++) - { - uint16_t extensionType = getExtension(i)->getTypeAsInt(); - result.extensions.push_back(extensionType); - } + // extract extensions + int extensionCount = getExtensionCount(); + for (int i = 0; i < extensionCount; i++) { + uint16_t extensionType = getExtension(i)->getTypeAsInt(); + result.extensions.push_back(extensionType); + } - return result; + return result; } -std::string SSLServerHelloMessage::toString() const -{ - return "Server Hello message"; +std::string SSLServerHelloMessage::toString() const { + return "Server Hello message"; } - // ------------------------------------------------ // SSLServerHelloMessage::ServerHelloTLSFingerprint // ------------------------------------------------ -std::string SSLServerHelloMessage::ServerHelloTLSFingerprint::toString() -{ - std::stringstream tlsFingerprint; +std::string SSLServerHelloMessage::ServerHelloTLSFingerprint::toString() { + std::stringstream tlsFingerprint; - // add version and cipher suite - tlsFingerprint << tlsVersion << "," << cipherSuite << ","; + // add version and cipher suite + tlsFingerprint << tlsVersion << "," << cipherSuite << ","; - // add extensions - bool firstExtension = true; - for (std::vector::const_iterator iter = extensions.begin(); iter != extensions.end(); iter++) - { - tlsFingerprint << (firstExtension ? "" : "-") << *iter; - firstExtension = false; - } + // add extensions + bool firstExtension = true; + for (std::vector::const_iterator iter = extensions.begin(); + iter != extensions.end(); iter++) { + tlsFingerprint << (firstExtension ? "" : "-") << *iter; + firstExtension = false; + } - return tlsFingerprint.str(); + return tlsFingerprint.str(); } -std::string SSLServerHelloMessage::ServerHelloTLSFingerprint::toMD5() -{ - return toStringAndMD5().second; +std::string SSLServerHelloMessage::ServerHelloTLSFingerprint::toMD5() { + return toStringAndMD5().second; } -std::pair SSLServerHelloMessage::ServerHelloTLSFingerprint::toStringAndMD5() -{ - std::string str = toString(); - MD5 md5; - return std::pair(str, md5(str)); +std::pair +SSLServerHelloMessage::ServerHelloTLSFingerprint::toStringAndMD5() { + std::string str = toString(); + MD5 md5; + return std::pair(str, md5(str)); } - // ----------------------------- // SSLCertificateMessage methods // ----------------------------- -SSLCertificateMessage::SSLCertificateMessage(uint8_t* data, size_t dataLen, SSLHandshakeLayer* container) - : SSLHandshakeMessage(data, dataLen, container) -{ - if (dataLen < sizeof(ssl_tls_handshake_layer) + - sizeof(uint8_t)*3) // certificates length (3B) - return; +SSLCertificateMessage::SSLCertificateMessage(uint8_t* data, size_t dataLen, + SSLHandshakeLayer* container) + : SSLHandshakeMessage(data, dataLen, container) { + if (dataLen < sizeof(ssl_tls_handshake_layer) + + sizeof(uint8_t) * 3) // certificates length (3B) + return; - size_t messageLen = getMessageLength(); - // read certificates length - // TODO: certificates length is 3B. Currently assuming the MSB is 0 and reading only 2 LSBs - uint8_t* curPos = data + sizeof(ssl_tls_handshake_layer) + sizeof(uint8_t); - uint16_t certificatesLength = be16toh(*(uint16_t*)(curPos)); - if (certificatesLength == 0) - return; + size_t messageLen = getMessageLength(); + // read certificates length + // TODO: certificates length is 3B. Currently assuming the MSB is 0 and + // reading only 2 LSBs + uint8_t* curPos = data + sizeof(ssl_tls_handshake_layer) + sizeof(uint8_t); + uint16_t certificatesLength = be16toh(*(uint16_t*)(curPos)); + if (certificatesLength == 0) + return; - // advance to position of first certificate - curPos += sizeof(uint16_t); + // advance to position of first certificate + curPos += sizeof(uint16_t); - while (true) - { - // try to read certificate length (3B) - // TODO: certificate length is 3B. Currently assuming the MSB is 0 and reading only 2 LSBs - if (curPos + 3*sizeof(uint8_t) - data > (int)messageLen) - break; + while (true) { + // try to read certificate length (3B) + // TODO: certificate length is 3B. Currently assuming the MSB is 0 and + // reading only 2 LSBs + if (curPos + 3 * sizeof(uint8_t) - data > (int)messageLen) + break; - // read certificate length - curPos += sizeof(uint8_t); - uint16_t certificateLength = be16toh(*(uint16_t*)(curPos)); + // read certificate length + curPos += sizeof(uint8_t); + uint16_t certificateLength = be16toh(*(uint16_t*)(curPos)); - // advance to start position of certificate - curPos += sizeof(uint16_t); + // advance to start position of certificate + curPos += sizeof(uint16_t); - // if packet doesn't contain the full certificate, read only what you got from current position till the - // end of the packet - bool certificateFull = true; - if (curPos - data + certificateLength > (int)messageLen) - { - certificateLength = messageLen - (curPos - data); - certificateFull = false; - } + // if packet doesn't contain the full certificate, read only what you got + // from current position till the end of the packet + bool certificateFull = true; + if (curPos - data + certificateLength > (int)messageLen) { + certificateLength = messageLen - (curPos - data); + certificateFull = false; + } - PCPP_LOG_DEBUG("Parsing certificate: pos=" << (int)(curPos-data) << "; len=" << certificateLength); - SSLx509Certificate* newCert = new SSLx509Certificate(curPos, certificateLength, certificateFull); - m_CertificateList.pushBack(newCert); + PCPP_LOG_DEBUG("Parsing certificate: pos=" + << (int)(curPos - data) << "; len=" << certificateLength); + SSLx509Certificate* newCert = + new SSLx509Certificate(curPos, certificateLength, certificateFull); + m_CertificateList.pushBack(newCert); - curPos += certificateLength; - } + curPos += certificateLength; + } } -std::string SSLCertificateMessage::toString() const -{ - return "Certificate message"; +std::string SSLCertificateMessage::toString() const { + return "Certificate message"; } -int SSLCertificateMessage::getNumOfCertificates() const -{ - return m_CertificateList.size(); +int SSLCertificateMessage::getNumOfCertificates() const { + return m_CertificateList.size(); } -SSLx509Certificate* SSLCertificateMessage::getCertificate(int index) const -{ - if (index < 0 || index > (int)m_CertificateList.size()) - { - PCPP_LOG_DEBUG("certificate index out of range: asked for index " << index << ", total size is " << m_CertificateList.size()); - return nullptr; - } +SSLx509Certificate* SSLCertificateMessage::getCertificate(int index) const { + if (index < 0 || index > (int)m_CertificateList.size()) { + PCPP_LOG_DEBUG("certificate index out of range: asked for index " + << index << ", total size is " << m_CertificateList.size()); + return nullptr; + } - return const_cast(m_CertificateList.at(index)); + return const_cast(m_CertificateList.at(index)); } - - // ------------------------------ // SSLHelloRequestMessage methods // ------------------------------ -std::string SSLHelloRequestMessage::toString() const -{ - return "Hello Request message"; +std::string SSLHelloRequestMessage::toString() const { + return "Hello Request message"; } - // --------------------------------- // SSLServerHelloDoneMessage methods // --------------------------------- -std::string SSLServerHelloDoneMessage::toString() const -{ - return "Server Hello Done message"; +std::string SSLServerHelloDoneMessage::toString() const { + return "Server Hello Done message"; } // ----------------------------------- // SSLServerKeyExchangeMessage methods // ----------------------------------- -uint8_t* SSLServerKeyExchangeMessage::getServerKeyExchangeParams() const -{ - if (getMessageLength() > sizeof(ssl_tls_handshake_layer)) - return (m_Data + sizeof(ssl_tls_handshake_layer)); +uint8_t* SSLServerKeyExchangeMessage::getServerKeyExchangeParams() const { + if (getMessageLength() > sizeof(ssl_tls_handshake_layer)) + return (m_Data + sizeof(ssl_tls_handshake_layer)); - return nullptr; + return nullptr; } -size_t SSLServerKeyExchangeMessage::getServerKeyExchangeParamsLength() const -{ - size_t msgLength = getMessageLength(); - if (msgLength <= sizeof(ssl_tls_handshake_layer)) - return 0; +size_t SSLServerKeyExchangeMessage::getServerKeyExchangeParamsLength() const { + size_t msgLength = getMessageLength(); + if (msgLength <= sizeof(ssl_tls_handshake_layer)) + return 0; - return msgLength - sizeof(ssl_tls_handshake_layer); + return msgLength - sizeof(ssl_tls_handshake_layer); } -std::string SSLServerKeyExchangeMessage::toString() const -{ - return "Server Key Exchange message"; +std::string SSLServerKeyExchangeMessage::toString() const { + return "Server Key Exchange message"; } // ----------------------------------- // SSLClientKeyExchangeMessage methods // ----------------------------------- -uint8_t* SSLClientKeyExchangeMessage::getClientKeyExchangeParams() const -{ - if (getMessageLength() > sizeof(ssl_tls_handshake_layer)) - return (m_Data + sizeof(ssl_tls_handshake_layer)); +uint8_t* SSLClientKeyExchangeMessage::getClientKeyExchangeParams() const { + if (getMessageLength() > sizeof(ssl_tls_handshake_layer)) + return (m_Data + sizeof(ssl_tls_handshake_layer)); - return nullptr; + return nullptr; } -size_t SSLClientKeyExchangeMessage::getClientKeyExchangeParamsLength() const -{ - size_t msgLength = getMessageLength(); - if (msgLength <= sizeof(ssl_tls_handshake_layer)) - return 0; +size_t SSLClientKeyExchangeMessage::getClientKeyExchangeParamsLength() const { + size_t msgLength = getMessageLength(); + if (msgLength <= sizeof(ssl_tls_handshake_layer)) + return 0; - return msgLength - sizeof(ssl_tls_handshake_layer); + return msgLength - sizeof(ssl_tls_handshake_layer); } -std::string SSLClientKeyExchangeMessage::toString() const -{ - return "Client Key Exchange message"; +std::string SSLClientKeyExchangeMessage::toString() const { + return "Client Key Exchange message"; } - // ------------------------------------ // SSLCertificateRequestMessage methods // ------------------------------------ -SSLCertificateRequestMessage::SSLCertificateRequestMessage(uint8_t* data, size_t dataLen, SSLHandshakeLayer* container) - : SSLHandshakeMessage(data, dataLen, container) -{ - size_t minMessageSize = sizeof(ssl_tls_handshake_layer) + sizeof(uint8_t); // certificate types count (1B) - if (dataLen < minMessageSize) - return; +SSLCertificateRequestMessage::SSLCertificateRequestMessage( + uint8_t* data, size_t dataLen, SSLHandshakeLayer* container) + : SSLHandshakeMessage(data, dataLen, container) { + size_t minMessageSize = sizeof(ssl_tls_handshake_layer) + + sizeof(uint8_t); // certificate types count (1B) + if (dataLen < minMessageSize) + return; - size_t messageLen = getMessageLength(); - if (messageLen < minMessageSize) - return; + size_t messageLen = getMessageLength(); + if (messageLen < minMessageSize) + return; - uint8_t certificateTypesCount = *(uint8_t*)(data + sizeof(ssl_tls_handshake_layer)); + uint8_t certificateTypesCount = + *(uint8_t*)(data + sizeof(ssl_tls_handshake_layer)); - if (certificateTypesCount > messageLen - minMessageSize) - certificateTypesCount = messageLen - minMessageSize; + if (certificateTypesCount > messageLen - minMessageSize) + certificateTypesCount = messageLen - minMessageSize; - uint8_t* pos = data + sizeof(ssl_tls_handshake_layer) + sizeof(uint8_t); - for (uint8_t i = 0; i < certificateTypesCount; i++) - { - uint8_t certType = *(uint8_t*)(pos + i); - if (certType == 0 || - (certType > 6 && certType < 20) || - (certType > 20 && certType < 64) || - certType > 64) - m_ClientCertificateTypes.push_back(SSL_CCT_UNKNOWN); - else - m_ClientCertificateTypes.push_back(static_cast(certType)); - } + uint8_t* pos = data + sizeof(ssl_tls_handshake_layer) + sizeof(uint8_t); + for (uint8_t i = 0; i < certificateTypesCount; i++) { + uint8_t certType = *(uint8_t*)(pos + i); + if (certType == 0 || (certType > 6 && certType < 20) || + (certType > 20 && certType < 64) || certType > 64) + m_ClientCertificateTypes.push_back(SSL_CCT_UNKNOWN); + else + m_ClientCertificateTypes.push_back( + static_cast(certType)); + } } -std::vector& SSLCertificateRequestMessage::getCertificateTypes() -{ - return m_ClientCertificateTypes; +std::vector& +SSLCertificateRequestMessage::getCertificateTypes() { + return m_ClientCertificateTypes; } -uint8_t* SSLCertificateRequestMessage::getCertificateAuthorityData() const -{ - size_t messageLen = getMessageLength(); - size_t offset = sizeof(ssl_tls_handshake_layer) + sizeof(uint8_t) + m_ClientCertificateTypes.size() + sizeof(uint16_t); - if (offset >= messageLen) - return nullptr; +uint8_t* SSLCertificateRequestMessage::getCertificateAuthorityData() const { + size_t messageLen = getMessageLength(); + size_t offset = sizeof(ssl_tls_handshake_layer) + sizeof(uint8_t) + + m_ClientCertificateTypes.size() + sizeof(uint16_t); + if (offset >= messageLen) + return nullptr; - return m_Data + offset; + return m_Data + offset; } -size_t SSLCertificateRequestMessage::getCertificateAuthorityLength() const -{ - size_t messageLen = getMessageLength(); - size_t offset = sizeof(ssl_tls_handshake_layer) + sizeof(uint8_t) + m_ClientCertificateTypes.size(); - if (offset + sizeof(uint16_t) >= messageLen) - return 0; +size_t SSLCertificateRequestMessage::getCertificateAuthorityLength() const { + size_t messageLen = getMessageLength(); + size_t offset = sizeof(ssl_tls_handshake_layer) + sizeof(uint8_t) + + m_ClientCertificateTypes.size(); + if (offset + sizeof(uint16_t) >= messageLen) + return 0; - uint16_t certAuthLen = be16toh(*(uint16_t*)(m_Data + offset)); + uint16_t certAuthLen = be16toh(*(uint16_t*)(m_Data + offset)); - offset += sizeof(uint16_t); + offset += sizeof(uint16_t); - if (messageLen - offset < certAuthLen) - return messageLen - offset; + if (messageLen - offset < certAuthLen) + return messageLen - offset; - return certAuthLen; + return certAuthLen; } -std::string SSLCertificateRequestMessage::toString() const -{ - return "Certificate Request message"; +std::string SSLCertificateRequestMessage::toString() const { + return "Certificate Request message"; } - // ----------------------------------- // SSLCertificateVerifyMessage methods // ----------------------------------- -uint8_t* SSLCertificateVerifyMessage::getSignedHash() const -{ - if (getMessageLength() > sizeof(ssl_tls_handshake_layer)) - return (m_Data + sizeof(ssl_tls_handshake_layer)); +uint8_t* SSLCertificateVerifyMessage::getSignedHash() const { + if (getMessageLength() > sizeof(ssl_tls_handshake_layer)) + return (m_Data + sizeof(ssl_tls_handshake_layer)); - return nullptr; + return nullptr; } -size_t SSLCertificateVerifyMessage::getSignedHashLength() const -{ - size_t msgLength = getMessageLength(); - if (msgLength <= sizeof(ssl_tls_handshake_layer)) - return 0; +size_t SSLCertificateVerifyMessage::getSignedHashLength() const { + size_t msgLength = getMessageLength(); + if (msgLength <= sizeof(ssl_tls_handshake_layer)) + return 0; - return msgLength - sizeof(ssl_tls_handshake_layer); + return msgLength - sizeof(ssl_tls_handshake_layer); } -std::string SSLCertificateVerifyMessage::toString() const -{ - return "Certificate Verify message"; +std::string SSLCertificateVerifyMessage::toString() const { + return "Certificate Verify message"; } // -------------------------- // SSLFinishedMessage methods // -------------------------- -uint8_t* SSLFinishedMessage::getSignedHash() const -{ - if (getMessageLength() > sizeof(ssl_tls_handshake_layer)) - return (m_Data + sizeof(ssl_tls_handshake_layer)); +uint8_t* SSLFinishedMessage::getSignedHash() const { + if (getMessageLength() > sizeof(ssl_tls_handshake_layer)) + return (m_Data + sizeof(ssl_tls_handshake_layer)); - return nullptr; + return nullptr; } -size_t SSLFinishedMessage::getSignedHashLength() const -{ - size_t msgLength = getMessageLength(); - if (msgLength <= sizeof(ssl_tls_handshake_layer)) - return 0; +size_t SSLFinishedMessage::getSignedHashLength() const { + size_t msgLength = getMessageLength(); + if (msgLength <= sizeof(ssl_tls_handshake_layer)) + return 0; - return msgLength - sizeof(ssl_tls_handshake_layer); -} - -std::string SSLFinishedMessage::toString() const -{ - return "Finished message"; + return msgLength - sizeof(ssl_tls_handshake_layer); } +std::string SSLFinishedMessage::toString() const { return "Finished message"; } // ---------------------------------- // SSLNewSessionTicketMessage methods // ---------------------------------- -uint8_t* SSLNewSessionTicketMessage::getSessionTicketData() const -{ - if (getMessageLength() > sizeof(ssl_tls_handshake_layer)) - return (m_Data + sizeof(ssl_tls_handshake_layer)); +uint8_t* SSLNewSessionTicketMessage::getSessionTicketData() const { + if (getMessageLength() > sizeof(ssl_tls_handshake_layer)) + return (m_Data + sizeof(ssl_tls_handshake_layer)); - return nullptr; + return nullptr; } -size_t SSLNewSessionTicketMessage::getSessionTicketDataLength() const -{ - size_t msgLength = getMessageLength(); - if (msgLength <= sizeof(ssl_tls_handshake_layer)) - return 0; +size_t SSLNewSessionTicketMessage::getSessionTicketDataLength() const { + size_t msgLength = getMessageLength(); + if (msgLength <= sizeof(ssl_tls_handshake_layer)) + return 0; - return msgLength - sizeof(ssl_tls_handshake_layer); + return msgLength - sizeof(ssl_tls_handshake_layer); } -std::string SSLNewSessionTicketMessage::toString() const -{ - return "New Session Ticket message"; +std::string SSLNewSessionTicketMessage::toString() const { + return "New Session Ticket message"; } - // ------------------------- // SSLUnknownMessage methods // ------------------------- -SSLHandshakeType SSLUnknownMessage::getHandshakeType() const -{ - // if message type is unknown, it may be some encrypted message so message type isn't necessarily written - // in clear in the first byte. So always return SSL_HANDSHAKE_UNKNOWN - return SSL_HANDSHAKE_UNKNOWN; +SSLHandshakeType SSLUnknownMessage::getHandshakeType() const { + // if message type is unknown, it may be some encrypted message so message + // type isn't necessarily written in clear in the first byte. So always return + // SSL_HANDSHAKE_UNKNOWN + return SSL_HANDSHAKE_UNKNOWN; } -size_t SSLUnknownMessage::getMessageLength() const -{ - // if message type is unknown, it may be some encrypted message so message length isn't necessarily written - // in clear. So in this case assume message is in length of all remaining data - return m_DataLen; +size_t SSLUnknownMessage::getMessageLength() const { + // if message type is unknown, it may be some encrypted message so message + // length isn't necessarily written in clear. So in this case assume message + // is in length of all remaining data + return m_DataLen; } - -std::string SSLUnknownMessage::toString() const -{ - return "Unknown message"; -} +std::string SSLUnknownMessage::toString() const { return "Unknown message"; } } // namespace pcpp diff --git a/Packet++/src/SSLLayer.cpp b/Packet++/src/SSLLayer.cpp index 422d0938e2..0ae7ddba99 100644 --- a/Packet++/src/SSLLayer.cpp +++ b/Packet++/src/SSLLayer.cpp @@ -1,258 +1,237 @@ #define LOG_MODULE PacketLogModuleSSLLayer -#include "Logger.h" #include "SSLLayer.h" #include "EndianPortable.h" +#include "Logger.h" #include - -namespace pcpp -{ +namespace pcpp { // ---------------- // SSLLayer methods // ---------------- -bool SSLLayer::IsSSLMessage(uint16_t srcPort, uint16_t dstPort, uint8_t* data, size_t dataLen, bool ignorePorts) -{ - // check the port map first - if (!ignorePorts && !isSSLPort(srcPort) && !isSSLPort(dstPort)) - return false; +bool SSLLayer::IsSSLMessage(uint16_t srcPort, uint16_t dstPort, uint8_t* data, + size_t dataLen, bool ignorePorts) { + // check the port map first + if (!ignorePorts && !isSSLPort(srcPort) && !isSSLPort(dstPort)) + return false; - if (dataLen < sizeof(ssl_tls_record_layer)) - return false; + if (dataLen < sizeof(ssl_tls_record_layer)) + return false; - ssl_tls_record_layer* recordLayer = (ssl_tls_record_layer*)data; + ssl_tls_record_layer* recordLayer = (ssl_tls_record_layer*)data; - // there is no SSL message with length 0 - if (recordLayer->length == 0) - return false; + // there is no SSL message with length 0 + if (recordLayer->length == 0) + return false; - if (recordLayer->recordType < 20 || recordLayer->recordType > 23) - return false; + if (recordLayer->recordType < 20 || recordLayer->recordType > 23) + return false; - SSLVersion::SSLVersionEnum recordVersion = SSLVersion(be16toh(recordLayer->recordVersion)).asEnum(true); + SSLVersion::SSLVersionEnum recordVersion = + SSLVersion(be16toh(recordLayer->recordVersion)).asEnum(true); - if (recordVersion == SSLVersion::TLS1_3 || - recordVersion == SSLVersion::TLS1_2 || - recordVersion == SSLVersion::TLS1_1 || - recordVersion == SSLVersion::TLS1_0 || - recordVersion == SSLVersion::SSL3) - return true; - else - return false; + if (recordVersion == SSLVersion::TLS1_3 || + recordVersion == SSLVersion::TLS1_2 || + recordVersion == SSLVersion::TLS1_1 || + recordVersion == SSLVersion::TLS1_0 || recordVersion == SSLVersion::SSL3) + return true; + else + return false; } -SSLLayer* SSLLayer::createSSLMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) -{ - ssl_tls_record_layer* recordLayer = (ssl_tls_record_layer*)data; - switch (recordLayer->recordType) - { - case SSL_HANDSHAKE: - { - return new SSLHandshakeLayer(data, dataLen, prevLayer, packet); - } - - case SSL_ALERT: - { - return new SSLAlertLayer(data, dataLen, prevLayer, packet); - } - - case SSL_CHANGE_CIPHER_SPEC: - { - return new SSLChangeCipherSpecLayer(data, dataLen, prevLayer, packet); - } - - case SSL_APPLICATION_DATA: - { - return new SSLApplicationDataLayer(data, dataLen, prevLayer, packet); - } - - default: - return nullptr; - } +SSLLayer* SSLLayer::createSSLMessage(uint8_t* data, size_t dataLen, + Layer* prevLayer, Packet* packet) { + ssl_tls_record_layer* recordLayer = (ssl_tls_record_layer*)data; + switch (recordLayer->recordType) { + case SSL_HANDSHAKE: { + return new SSLHandshakeLayer(data, dataLen, prevLayer, packet); + } + + case SSL_ALERT: { + return new SSLAlertLayer(data, dataLen, prevLayer, packet); + } + + case SSL_CHANGE_CIPHER_SPEC: { + return new SSLChangeCipherSpecLayer(data, dataLen, prevLayer, packet); + } + + case SSL_APPLICATION_DATA: { + return new SSLApplicationDataLayer(data, dataLen, prevLayer, packet); + } + + default: + return nullptr; + } } -SSLVersion SSLLayer::getRecordVersion() const -{ - uint16_t recordVersion = be16toh(getRecordLayer()->recordVersion); - return SSLVersion(recordVersion); +SSLVersion SSLLayer::getRecordVersion() const { + uint16_t recordVersion = be16toh(getRecordLayer()->recordVersion); + return SSLVersion(recordVersion); } -SSLRecordType SSLLayer::getRecordType() const -{ - return (SSLRecordType)(getRecordLayer()->recordType); +SSLRecordType SSLLayer::getRecordType() const { + return (SSLRecordType)(getRecordLayer()->recordType); } -size_t SSLLayer::getHeaderLen() const -{ - size_t len = sizeof(ssl_tls_record_layer) + be16toh(getRecordLayer()->length); - if (len > m_DataLen) - return m_DataLen; - return len; +size_t SSLLayer::getHeaderLen() const { + size_t len = sizeof(ssl_tls_record_layer) + be16toh(getRecordLayer()->length); + if (len > m_DataLen) + return m_DataLen; + return len; } -void SSLLayer::parseNextLayer() -{ - size_t headerLen = getHeaderLen(); - if (m_DataLen <= headerLen) - return; +void SSLLayer::parseNextLayer() { + size_t headerLen = getHeaderLen(); + if (m_DataLen <= headerLen) + return; - if (SSLLayer::IsSSLMessage(0, 0, m_Data + headerLen, m_DataLen - headerLen, true)) - m_NextLayer = SSLLayer::createSSLMessage(m_Data + headerLen, m_DataLen - headerLen, this, m_Packet); + if (SSLLayer::IsSSLMessage(0, 0, m_Data + headerLen, m_DataLen - headerLen, + true)) + m_NextLayer = SSLLayer::createSSLMessage( + m_Data + headerLen, m_DataLen - headerLen, this, m_Packet); } - // ------------------------- // SSLHandshakeLayer methods // ------------------------- -std::string SSLHandshakeLayer::toString() const -{ - std::stringstream result; - result << getRecordVersion().toString(true) << " Layer, Handshake:"; - for(size_t i = 0; i < m_MessageList.size(); i++) - { - if (i == 0) - result << " " << m_MessageList.at(i)->toString(); - else - result << ", " << m_MessageList.at(i)->toString(); - } - return result.str(); +std::string SSLHandshakeLayer::toString() const { + std::stringstream result; + result << getRecordVersion().toString(true) << " Layer, Handshake:"; + for (size_t i = 0; i < m_MessageList.size(); i++) { + if (i == 0) + result << " " << m_MessageList.at(i)->toString(); + else + result << ", " << m_MessageList.at(i)->toString(); + } + return result.str(); } -SSLHandshakeLayer::SSLHandshakeLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) - : SSLLayer(data, dataLen, prevLayer, packet) -{ - uint8_t* curPos = m_Data + sizeof(ssl_tls_record_layer); - size_t recordDataLen = be16toh(getRecordLayer()->length); - if (recordDataLen > m_DataLen - sizeof(ssl_tls_record_layer)) - recordDataLen = m_DataLen - sizeof(ssl_tls_record_layer); - - size_t curPosIndex = 0; - while (true) - { - SSLHandshakeMessage* message = SSLHandshakeMessage::createHandshakeMessage(curPos, recordDataLen-curPosIndex, this); - if (message == nullptr) - break; - - m_MessageList.pushBack(message); - curPos += message->getMessageLength(); - curPosIndex += message->getMessageLength(); - } -} +SSLHandshakeLayer::SSLHandshakeLayer(uint8_t* data, size_t dataLen, + Layer* prevLayer, Packet* packet) + : SSLLayer(data, dataLen, prevLayer, packet) { + uint8_t* curPos = m_Data + sizeof(ssl_tls_record_layer); + size_t recordDataLen = be16toh(getRecordLayer()->length); + if (recordDataLen > m_DataLen - sizeof(ssl_tls_record_layer)) + recordDataLen = m_DataLen - sizeof(ssl_tls_record_layer); -SSLHandshakeMessage* SSLHandshakeLayer::getHandshakeMessageAt(int index) const -{ - if (index < 0 || index >= (int)(m_MessageList.size())) - return nullptr; + size_t curPosIndex = 0; + while (true) { + SSLHandshakeMessage* message = SSLHandshakeMessage::createHandshakeMessage( + curPos, recordDataLen - curPosIndex, this); + if (message == nullptr) + break; - return const_cast(m_MessageList.at(index)); + m_MessageList.pushBack(message); + curPos += message->getMessageLength(); + curPosIndex += message->getMessageLength(); + } } +SSLHandshakeMessage* SSLHandshakeLayer::getHandshakeMessageAt(int index) const { + if (index < 0 || index >= (int)(m_MessageList.size())) + return nullptr; + + return const_cast(m_MessageList.at(index)); +} // -------------------------------- // SSLChangeCipherSpecLayer methods // -------------------------------- -std::string SSLChangeCipherSpecLayer::toString() const -{ - std::stringstream result; - result << getRecordVersion().toString(true) << " Layer, Change Cipher Spec"; - return result.str(); +std::string SSLChangeCipherSpecLayer::toString() const { + std::stringstream result; + result << getRecordVersion().toString(true) << " Layer, Change Cipher Spec"; + return result.str(); } // --------------------- // SSLAlertLayer methods // --------------------- -SSLAlertLevel SSLAlertLayer::getAlertLevel() const -{ - uint8_t* pos = m_Data + sizeof(ssl_tls_record_layer); - uint8_t alertLevel = *pos; - if (alertLevel == SSL_ALERT_LEVEL_WARNING || alertLevel == SSL_ALERT_LEVEL_FATAL) - return (SSLAlertLevel)alertLevel; - else - return SSL_ALERT_LEVEL_ENCRYPTED; -} - -SSLAlertDescription SSLAlertLayer::getAlertDescription() -{ - if (getAlertLevel() == SSL_ALERT_LEVEL_ENCRYPTED) - return SSL_ALERT_ENCRYPTED; - - uint8_t* pos = m_Data + sizeof(ssl_tls_record_layer) + sizeof(uint8_t); - uint8_t alertDesc = *pos; - - switch (alertDesc) - { - case SSL_ALERT_CLOSE_NOTIFY: - case SSL_ALERT_UNEXPECTED_MESSAGE: - case SSL_ALERT_BAD_RECORD_MAC: - case SSL_ALERT_DECRYPTION_FAILED: - case SSL_ALERT_RECORD_OVERFLOW: - case SSL_ALERT_DECOMPRESSION_FAILURE: - case SSL_ALERT_HANDSHAKE_FAILURE: - case SSL_ALERT_NO_CERTIFICATE: - case SSL_ALERT_BAD_CERTIFICATE: - case SSL_ALERT_UNSUPPORTED_CERTIFICATE: - case SSL_ALERT_CERTIFICATE_REVOKED: - case SSL_ALERT_CERTIFICATE_EXPIRED: - case SSL_ALERT_CERTIFICATE_UNKNOWN: - case SSL_ALERT_ILLEGAL_PARAMETER: - case SSL_ALERT_UNKNOWN_CA: - case SSL_ALERT_ACCESS_DENIED: - case SSL_ALERT_DECODE_ERROR: - case SSL_ALERT_DECRYPT_ERROR: - case SSL_ALERT_EXPORT_RESTRICTION: - case SSL_ALERT_PROTOCOL_VERSION: - case SSL_ALERT_INSUFFICIENT_SECURITY: - case SSL_ALERT_INTERNAL_ERROR: - case SSL_ALERT_USER_CANCELLED: - case SSL_ALERT_NO_RENEGOTIATION: - return (SSLAlertDescription)alertDesc; - break; - default: - return SSL_ALERT_ENCRYPTED; - } -} - -std::string SSLAlertLayer::toString() const -{ - std::stringstream result; - result << getRecordVersion().toString(true) << " Layer, "; - if (getAlertLevel() == SSL_ALERT_LEVEL_ENCRYPTED) - result << "Encrypted Alert"; - else - //TODO: add alert level and description here - result << "Alert"; - return result.str(); +SSLAlertLevel SSLAlertLayer::getAlertLevel() const { + uint8_t* pos = m_Data + sizeof(ssl_tls_record_layer); + uint8_t alertLevel = *pos; + if (alertLevel == SSL_ALERT_LEVEL_WARNING || + alertLevel == SSL_ALERT_LEVEL_FATAL) + return (SSLAlertLevel)alertLevel; + else + return SSL_ALERT_LEVEL_ENCRYPTED; +} + +SSLAlertDescription SSLAlertLayer::getAlertDescription() { + if (getAlertLevel() == SSL_ALERT_LEVEL_ENCRYPTED) + return SSL_ALERT_ENCRYPTED; + + uint8_t* pos = m_Data + sizeof(ssl_tls_record_layer) + sizeof(uint8_t); + uint8_t alertDesc = *pos; + + switch (alertDesc) { + case SSL_ALERT_CLOSE_NOTIFY: + case SSL_ALERT_UNEXPECTED_MESSAGE: + case SSL_ALERT_BAD_RECORD_MAC: + case SSL_ALERT_DECRYPTION_FAILED: + case SSL_ALERT_RECORD_OVERFLOW: + case SSL_ALERT_DECOMPRESSION_FAILURE: + case SSL_ALERT_HANDSHAKE_FAILURE: + case SSL_ALERT_NO_CERTIFICATE: + case SSL_ALERT_BAD_CERTIFICATE: + case SSL_ALERT_UNSUPPORTED_CERTIFICATE: + case SSL_ALERT_CERTIFICATE_REVOKED: + case SSL_ALERT_CERTIFICATE_EXPIRED: + case SSL_ALERT_CERTIFICATE_UNKNOWN: + case SSL_ALERT_ILLEGAL_PARAMETER: + case SSL_ALERT_UNKNOWN_CA: + case SSL_ALERT_ACCESS_DENIED: + case SSL_ALERT_DECODE_ERROR: + case SSL_ALERT_DECRYPT_ERROR: + case SSL_ALERT_EXPORT_RESTRICTION: + case SSL_ALERT_PROTOCOL_VERSION: + case SSL_ALERT_INSUFFICIENT_SECURITY: + case SSL_ALERT_INTERNAL_ERROR: + case SSL_ALERT_USER_CANCELLED: + case SSL_ALERT_NO_RENEGOTIATION: + return (SSLAlertDescription)alertDesc; + break; + default: + return SSL_ALERT_ENCRYPTED; + } +} + +std::string SSLAlertLayer::toString() const { + std::stringstream result; + result << getRecordVersion().toString(true) << " Layer, "; + if (getAlertLevel() == SSL_ALERT_LEVEL_ENCRYPTED) + result << "Encrypted Alert"; + else + // TODO: add alert level and description here + result << "Alert"; + return result.str(); } // ------------------------------- // SSLApplicationDataLayer methods // ------------------------------- -uint8_t* SSLApplicationDataLayer::getEncryptedData() const -{ - if (getHeaderLen() <= sizeof(ssl_tls_record_layer)) - return nullptr; +uint8_t* SSLApplicationDataLayer::getEncryptedData() const { + if (getHeaderLen() <= sizeof(ssl_tls_record_layer)) + return nullptr; - return m_Data + sizeof(ssl_tls_record_layer); + return m_Data + sizeof(ssl_tls_record_layer); } -size_t SSLApplicationDataLayer::getEncryptedDataLen() const -{ - int result = (int)getHeaderLen() - (int)sizeof(ssl_tls_record_layer); - if (result < 0) - return 0; +size_t SSLApplicationDataLayer::getEncryptedDataLen() const { + int result = (int)getHeaderLen() - (int)sizeof(ssl_tls_record_layer); + if (result < 0) + return 0; - return (size_t)result; + return (size_t)result; } -std::string SSLApplicationDataLayer::toString() const -{ - return getRecordVersion().toString(true) + " Layer, Application Data"; +std::string SSLApplicationDataLayer::toString() const { + return getRecordVersion().toString(true) + " Layer, Application Data"; } } // namespace pcpp diff --git a/Packet++/src/SdpLayer.cpp b/Packet++/src/SdpLayer.cpp index b90d61c095..007dda325f 100644 --- a/Packet++/src/SdpLayer.cpp +++ b/Packet++/src/SdpLayer.cpp @@ -1,136 +1,135 @@ #define LOG_MODULE PacketLogModuleSdpLayer - #include "SdpLayer.h" #include "Logger.h" +#include #include #include -#include -namespace pcpp -{ +namespace pcpp { -std::vector splitByWhiteSpaces(const std::string& str) -{ - std::string buf; - std::stringstream stream(str); - std::vector result; - while (stream >> buf) - result.push_back(buf); +std::vector splitByWhiteSpaces(const std::string& str) { + std::string buf; + std::stringstream stream(str); + std::vector result; + while (stream >> buf) + result.push_back(buf); - return result; + return result; } - -SdpLayer::SdpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : TextBasedProtocolMessage(data, dataLen, prevLayer, packet) -{ - m_Protocol = SDP; - m_FieldsOffset = 0; - parseFields(); +SdpLayer::SdpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet) + : TextBasedProtocolMessage(data, dataLen, prevLayer, packet) { + m_Protocol = SDP; + m_FieldsOffset = 0; + parseFields(); } -SdpLayer::SdpLayer() -{ - m_Protocol = SDP; - m_FieldsOffset = 0; +SdpLayer::SdpLayer() { + m_Protocol = SDP; + m_FieldsOffset = 0; } -SdpLayer::SdpLayer(const std::string& username, long sessionID, long sessionVersion, IPv4Address ipAddress, const std::string& sessionName, long startTime, long stopTime) -{ - m_Protocol = SDP; - m_FieldsOffset = 0; - - // must initialize m_Data otherwise addField() will fail while trying to extend the layer - // initializing in length of 1 but keeping m_DataLen with value of 0. - // when extending the field m_Data is purged so there isn't a memory leak here - m_Data = new uint8_t[1]; - m_DataLen = 0; - - addField(PCPP_SDP_PROTOCOL_VERSION_FIELD, "0"); - - std::stringstream sessionIDStream; - sessionIDStream << sessionID; - std::stringstream sessionVersionStream; - sessionVersionStream << sessionVersion; - std::string networkInfo = "IN IP4 " + ipAddress.toString(); - std::string originatorFieldValue = username + " " + sessionIDStream.str() + " " + sessionVersionStream.str() + " " + networkInfo; - addField(PCPP_SDP_ORIGINATOR_FIELD, originatorFieldValue); - - addField(PCPP_SDP_SESSION_NAME_FIELD, sessionName); - - addField(PCPP_SDP_CONNECTION_INFO_FIELD, networkInfo); - - std::stringstream startTimeStream; - startTimeStream << startTime; - std::stringstream stopTimeStream; - stopTimeStream << stopTime; - addField(PCPP_SDP_TIME_FIELD, startTimeStream.str() + " " + stopTimeStream.str()); +SdpLayer::SdpLayer(const std::string& username, long sessionID, + long sessionVersion, IPv4Address ipAddress, + const std::string& sessionName, long startTime, + long stopTime) { + m_Protocol = SDP; + m_FieldsOffset = 0; + + // must initialize m_Data otherwise addField() will fail while trying to + // extend the layer initializing in length of 1 but keeping m_DataLen with + // value of 0. when extending the field m_Data is purged so there isn't a + // memory leak here + m_Data = new uint8_t[1]; + m_DataLen = 0; + + addField(PCPP_SDP_PROTOCOL_VERSION_FIELD, "0"); + + std::stringstream sessionIDStream; + sessionIDStream << sessionID; + std::stringstream sessionVersionStream; + sessionVersionStream << sessionVersion; + std::string networkInfo = "IN IP4 " + ipAddress.toString(); + std::string originatorFieldValue = username + " " + sessionIDStream.str() + + " " + sessionVersionStream.str() + " " + + networkInfo; + addField(PCPP_SDP_ORIGINATOR_FIELD, originatorFieldValue); + + addField(PCPP_SDP_SESSION_NAME_FIELD, sessionName); + + addField(PCPP_SDP_CONNECTION_INFO_FIELD, networkInfo); + + std::stringstream startTimeStream; + startTimeStream << startTime; + std::stringstream stopTimeStream; + stopTimeStream << stopTime; + addField(PCPP_SDP_TIME_FIELD, + startTimeStream.str() + " " + stopTimeStream.str()); } -std::string SdpLayer::toString() const -{ - return "SDP Layer"; -} +std::string SdpLayer::toString() const { return "SDP Layer"; } -IPv4Address SdpLayer::getOwnerIPv4Address() const -{ - HeaderField* originator = getFieldByName(PCPP_SDP_ORIGINATOR_FIELD); - if (originator == nullptr) - return IPv4Address::Zero; +IPv4Address SdpLayer::getOwnerIPv4Address() const { + HeaderField* originator = getFieldByName(PCPP_SDP_ORIGINATOR_FIELD); + if (originator == nullptr) + return IPv4Address::Zero; - std::vector tokens = splitByWhiteSpaces(originator->getFieldValue()); - if (tokens.size() < 6) - return IPv4Address::Zero; + std::vector tokens = + splitByWhiteSpaces(originator->getFieldValue()); + if (tokens.size() < 6) + return IPv4Address::Zero; - if (tokens[3] != "IN" || tokens[4] != "IP4") - return IPv4Address::Zero; + if (tokens[3] != "IN" || tokens[4] != "IP4") + return IPv4Address::Zero; - return IPv4Address(tokens[5]); + return IPv4Address(tokens[5]); } -uint16_t SdpLayer::getMediaPort(const std::string& mediaType) const -{ - int mediaFieldIndex = 0; - HeaderField* mediaDesc = getFieldByName(PCPP_SDP_MEDIA_NAME_FIELD, mediaFieldIndex); +uint16_t SdpLayer::getMediaPort(const std::string& mediaType) const { + int mediaFieldIndex = 0; + HeaderField* mediaDesc = + getFieldByName(PCPP_SDP_MEDIA_NAME_FIELD, mediaFieldIndex); - while (mediaDesc != nullptr) - { - std::vector tokens = splitByWhiteSpaces(mediaDesc->getFieldValue()); + while (mediaDesc != nullptr) { + std::vector tokens = + splitByWhiteSpaces(mediaDesc->getFieldValue()); - if (tokens.size() >= 2 && tokens[0] == mediaType) - return atoi(tokens[1].c_str()); + if (tokens.size() >= 2 && tokens[0] == mediaType) + return atoi(tokens[1].c_str()); - mediaFieldIndex++; - mediaDesc = getFieldByName(PCPP_SDP_MEDIA_NAME_FIELD, mediaFieldIndex); - } + mediaFieldIndex++; + mediaDesc = getFieldByName(PCPP_SDP_MEDIA_NAME_FIELD, mediaFieldIndex); + } - return 0; + return 0; } -bool SdpLayer::addMediaDescription(const std::string& mediaType, uint16_t mediaPort, const std::string& mediaProtocol, const std::string& mediaFormat, std::vector mediaAttributes) -{ - std::stringstream portStream; - portStream << mediaPort; - - std::string mediaFieldValue = mediaType + " " + portStream.str() + " " + mediaProtocol + " " + mediaFormat; - if (addField(PCPP_SDP_MEDIA_NAME_FIELD, mediaFieldValue) == nullptr) - { - PCPP_LOG_ERROR("Failed to add media description field"); - return false; - } - - - for (std::vector::iterator iter = mediaAttributes.begin(); iter != mediaAttributes.end(); iter++) - { - if (addField(PCPP_SDP_MEDIA_ATTRIBUTE_FIELD, *iter) == nullptr) - { - PCPP_LOG_ERROR("Failed to add media attribute '" << *iter << "'"); - return false; - } - } - - return true; +bool SdpLayer::addMediaDescription(const std::string& mediaType, + uint16_t mediaPort, + const std::string& mediaProtocol, + const std::string& mediaFormat, + std::vector mediaAttributes) { + std::stringstream portStream; + portStream << mediaPort; + + std::string mediaFieldValue = mediaType + " " + portStream.str() + " " + + mediaProtocol + " " + mediaFormat; + if (addField(PCPP_SDP_MEDIA_NAME_FIELD, mediaFieldValue) == nullptr) { + PCPP_LOG_ERROR("Failed to add media description field"); + return false; + } + + for (std::vector::iterator iter = mediaAttributes.begin(); + iter != mediaAttributes.end(); iter++) { + if (addField(PCPP_SDP_MEDIA_ATTRIBUTE_FIELD, *iter) == nullptr) { + PCPP_LOG_ERROR("Failed to add media attribute '" << *iter << "'"); + return false; + } + } + + return true; } - -} +} // namespace pcpp diff --git a/Packet++/src/SingleCommandTextProtocol.cpp b/Packet++/src/SingleCommandTextProtocol.cpp index d020eca484..f64e354b99 100644 --- a/Packet++/src/SingleCommandTextProtocol.cpp +++ b/Packet++/src/SingleCommandTextProtocol.cpp @@ -6,135 +6,123 @@ #define ASCII_HYPHEN 0x2d #define ASCII_SPACE 0x20 -namespace pcpp -{ - - size_t SingleCommandTextProtocol::getArgumentFieldOffset() const - { - size_t maxLen; - if (m_DataLen < 5) - maxLen = m_DataLen; - else - maxLen = 5; - - // Find if exists - uint8_t *pos = (uint8_t *)memchr(m_Data, ASCII_SPACE, maxLen); - if (pos) - return pos - m_Data; - - // Find Hyphen "-" if exists - pos = (uint8_t *)memchr(m_Data, ASCII_HYPHEN, maxLen); - if (pos) - return pos - m_Data; - - return m_DataLen - 1; - } - - void SingleCommandTextProtocol::setDelimiter(bool hyphen) - { - if (hyphen) - memset(&m_Data[getArgumentFieldOffset()], ASCII_HYPHEN, 1); - else - memset(&m_Data[getArgumentFieldOffset()], ASCII_SPACE, 1); - } - - bool SingleCommandTextProtocol::hyphenRequired(const std::string& value) - { - size_t firstPos = value.find_first_of("\r\n"); - size_t lastPos = value.find_last_of("\r\n"); - return (firstPos != std::string::npos) && (lastPos != std::string::npos) && (firstPos != lastPos - 1); - } - - SingleCommandTextProtocol::SingleCommandTextProtocol(const std::string &command, const std::string &option) - { - m_Data = new uint8_t[6]; - m_DataLen = 6; - if (!command.empty()) - setCommandInternal(command); - if (!option.empty()) - setCommandOptionInternal(option); - } - - bool SingleCommandTextProtocol::setCommandInternal(std::string value) - { - size_t currentOffset = getArgumentFieldOffset(); - if (currentOffset == m_DataLen - 1) - currentOffset = 0; - if (!currentOffset) - value += " \r\n"; - - if (value.size() < currentOffset) - { - if (!shortenLayer(0, currentOffset - value.size())) - return false; - } - else if (m_Data && value.size() > currentOffset) - { - if (!extendLayer(0, value.size() - currentOffset)) - return false; - } - - memcpy(m_Data, value.c_str(), value.size()); - return true; - } - - bool SingleCommandTextProtocol::setCommandOptionInternal(std::string value) - { - size_t lastPos = value.find_last_of("\r\n"); - if (lastPos == std::string::npos || lastPos != value.size() - 2) - value += "\r\n"; - - size_t currentOffset = getArgumentFieldOffset() + 1; - - if (value.size() < (m_DataLen - currentOffset)) - { - if (!shortenLayer(currentOffset, (m_DataLen - currentOffset) - value.size())) - return false; - } - else if (m_Data && value.size() > (m_DataLen - currentOffset)) - { - if (!extendLayer(currentOffset, value.size() - (m_DataLen - currentOffset))) - return false; - } - - memcpy(&m_Data[currentOffset], value.c_str(), value.size()); - - if (hyphenRequired(value)) - setDelimiter(true); - else - setDelimiter(false); - return true; - } - - std::string SingleCommandTextProtocol::getCommandInternal() const - { - size_t offset = getArgumentFieldOffset(); - - // If there is no option remove trailing newline characters - if (offset == (m_DataLen - 1) && offset > 1) - return std::string((char *)m_Data, offset - 1); - return std::string((char *)m_Data, offset); - } - - std::string SingleCommandTextProtocol::getCommandOptionInternal() const - { - if (getArgumentFieldOffset() != (m_DataLen - 1)) - return std::string((char *)&m_Data[getArgumentFieldOffset() + 1], m_DataLen - getArgumentFieldOffset() - 2); - return ""; - } - - bool SingleCommandTextProtocol::isMultiLine() const - { - return m_Data[getArgumentFieldOffset()] == ASCII_HYPHEN; - } - - bool SingleCommandTextProtocol::isDataValid(const uint8_t *data, size_t dataSize) - { - if (data == nullptr || dataSize < 6) - return false; - - std::string payload = std::string((char *)data, dataSize); - return payload.find_last_of("\r\n") == dataSize - 1; - } +namespace pcpp { + +size_t SingleCommandTextProtocol::getArgumentFieldOffset() const { + size_t maxLen; + if (m_DataLen < 5) + maxLen = m_DataLen; + else + maxLen = 5; + + // Find if exists + uint8_t* pos = (uint8_t*)memchr(m_Data, ASCII_SPACE, maxLen); + if (pos) + return pos - m_Data; + + // Find Hyphen "-" if exists + pos = (uint8_t*)memchr(m_Data, ASCII_HYPHEN, maxLen); + if (pos) + return pos - m_Data; + + return m_DataLen - 1; +} + +void SingleCommandTextProtocol::setDelimiter(bool hyphen) { + if (hyphen) + memset(&m_Data[getArgumentFieldOffset()], ASCII_HYPHEN, 1); + else + memset(&m_Data[getArgumentFieldOffset()], ASCII_SPACE, 1); +} + +bool SingleCommandTextProtocol::hyphenRequired(const std::string& value) { + size_t firstPos = value.find_first_of("\r\n"); + size_t lastPos = value.find_last_of("\r\n"); + return (firstPos != std::string::npos) && (lastPos != std::string::npos) && + (firstPos != lastPos - 1); +} + +SingleCommandTextProtocol::SingleCommandTextProtocol( + const std::string& command, const std::string& option) { + m_Data = new uint8_t[6]; + m_DataLen = 6; + if (!command.empty()) + setCommandInternal(command); + if (!option.empty()) + setCommandOptionInternal(option); +} + +bool SingleCommandTextProtocol::setCommandInternal(std::string value) { + size_t currentOffset = getArgumentFieldOffset(); + if (currentOffset == m_DataLen - 1) + currentOffset = 0; + if (!currentOffset) + value += " \r\n"; + + if (value.size() < currentOffset) { + if (!shortenLayer(0, currentOffset - value.size())) + return false; + } else if (m_Data && value.size() > currentOffset) { + if (!extendLayer(0, value.size() - currentOffset)) + return false; + } + + memcpy(m_Data, value.c_str(), value.size()); + return true; +} + +bool SingleCommandTextProtocol::setCommandOptionInternal(std::string value) { + size_t lastPos = value.find_last_of("\r\n"); + if (lastPos == std::string::npos || lastPos != value.size() - 2) + value += "\r\n"; + + size_t currentOffset = getArgumentFieldOffset() + 1; + + if (value.size() < (m_DataLen - currentOffset)) { + if (!shortenLayer(currentOffset, + (m_DataLen - currentOffset) - value.size())) + return false; + } else if (m_Data && value.size() > (m_DataLen - currentOffset)) { + if (!extendLayer(currentOffset, value.size() - (m_DataLen - currentOffset))) + return false; + } + + memcpy(&m_Data[currentOffset], value.c_str(), value.size()); + + if (hyphenRequired(value)) + setDelimiter(true); + else + setDelimiter(false); + return true; +} + +std::string SingleCommandTextProtocol::getCommandInternal() const { + size_t offset = getArgumentFieldOffset(); + + // If there is no option remove trailing newline characters + if (offset == (m_DataLen - 1) && offset > 1) + return std::string((char*)m_Data, offset - 1); + return std::string((char*)m_Data, offset); +} + +std::string SingleCommandTextProtocol::getCommandOptionInternal() const { + if (getArgumentFieldOffset() != (m_DataLen - 1)) + return std::string((char*)&m_Data[getArgumentFieldOffset() + 1], + m_DataLen - getArgumentFieldOffset() - 2); + return ""; +} + +bool SingleCommandTextProtocol::isMultiLine() const { + return m_Data[getArgumentFieldOffset()] == ASCII_HYPHEN; +} + +bool SingleCommandTextProtocol::isDataValid(const uint8_t* data, + size_t dataSize) { + if (data == nullptr || dataSize < 6) + return false; + + std::string payload = std::string((char*)data, dataSize); + return payload.find_last_of("\r\n") == dataSize - 1; +} } // namespace pcpp diff --git a/Packet++/src/SipLayer.cpp b/Packet++/src/SipLayer.cpp index 7ba815755d..f751288052 100644 --- a/Packet++/src/SipLayer.cpp +++ b/Packet++/src/SipLayer.cpp @@ -1,969 +1,848 @@ #define LOG_MODULE PacketLogModuleSipLayer #include "SipLayer.h" -#include "SdpLayer.h" -#include "PayloadLayer.h" -#include "Logger.h" #include "GeneralUtils.h" -#include +#include "Logger.h" +#include "PayloadLayer.h" +#include "SdpLayer.h" #include -#include #include -#include +#include +#include #include +#include -namespace pcpp -{ +namespace pcpp { const std::string SipMethodEnumToString[14] = { - "INVITE", - "ACK", - "BYE", - "CANCEL", - "REGISTER", - "PRACK", - "OPTIONS", - "SUBSCRIBE", - "NOTIFY", - "PUBLISH", - "INFO", - "REFER", - "MESSAGE", - "UPDATE" -}; - -const std::unordered_map SipMethodStringToEnum { - {"INVITE", SipRequestLayer::SipMethod::SipINVITE }, - {"ACK", SipRequestLayer::SipMethod::SipACK }, - {"BYE", SipRequestLayer::SipMethod::SipBYE }, - {"CANCEL", SipRequestLayer::SipMethod::SipCANCEL }, - {"REGISTER", SipRequestLayer::SipMethod::SipREGISTER }, - {"PRACK", SipRequestLayer::SipMethod::SipPRACK }, - {"OPTIONS", SipRequestLayer::SipMethod::SipOPTIONS }, - {"SUBSCRIBE", SipRequestLayer::SipMethod::SipSUBSCRIBE }, - {"NOTIFY", SipRequestLayer::SipMethod::SipNOTIFY }, - {"PUBLISH", SipRequestLayer::SipMethod::SipPUBLISH }, - {"INFO", SipRequestLayer::SipMethod::SipINFO }, - {"REFER", SipRequestLayer::SipMethod::SipREFER }, - {"MESSAGE", SipRequestLayer::SipMethod::SipMESSAGE }, - {"UPDATE", SipRequestLayer::SipMethod::SipUPDATE }, -}; - - - + "INVITE", "ACK", "BYE", "CANCEL", "REGISTER", + "PRACK", "OPTIONS", "SUBSCRIBE", "NOTIFY", "PUBLISH", + "INFO", "REFER", "MESSAGE", "UPDATE"}; + +const std::unordered_map + SipMethodStringToEnum{ + {"INVITE", SipRequestLayer::SipMethod::SipINVITE}, + {"ACK", SipRequestLayer::SipMethod::SipACK}, + {"BYE", SipRequestLayer::SipMethod::SipBYE}, + {"CANCEL", SipRequestLayer::SipMethod::SipCANCEL}, + {"REGISTER", SipRequestLayer::SipMethod::SipREGISTER}, + {"PRACK", SipRequestLayer::SipMethod::SipPRACK}, + {"OPTIONS", SipRequestLayer::SipMethod::SipOPTIONS}, + {"SUBSCRIBE", SipRequestLayer::SipMethod::SipSUBSCRIBE}, + {"NOTIFY", SipRequestLayer::SipMethod::SipNOTIFY}, + {"PUBLISH", SipRequestLayer::SipMethod::SipPUBLISH}, + {"INFO", SipRequestLayer::SipMethod::SipINFO}, + {"REFER", SipRequestLayer::SipMethod::SipREFER}, + {"MESSAGE", SipRequestLayer::SipMethod::SipMESSAGE}, + {"UPDATE", SipRequestLayer::SipMethod::SipUPDATE}, + }; // -------- Class SipLayer ----------------- -int SipLayer::getContentLength() const -{ - std::string contentLengthFieldName(PCPP_SIP_CONTENT_LENGTH_FIELD); - std::transform(contentLengthFieldName.begin(), contentLengthFieldName.end(), contentLengthFieldName.begin(), ::tolower); - HeaderField* contentLengthField = getFieldByName(contentLengthFieldName); - if (contentLengthField != nullptr) - return atoi(contentLengthField->getFieldValue().c_str()); - return 0; +int SipLayer::getContentLength() const { + std::string contentLengthFieldName(PCPP_SIP_CONTENT_LENGTH_FIELD); + std::transform(contentLengthFieldName.begin(), contentLengthFieldName.end(), + contentLengthFieldName.begin(), ::tolower); + HeaderField* contentLengthField = getFieldByName(contentLengthFieldName); + if (contentLengthField != nullptr) + return atoi(contentLengthField->getFieldValue().c_str()); + return 0; +} + +HeaderField* SipLayer::setContentLength(int contentLength, + const std::string& prevFieldName) { + std::ostringstream contentLengthAsString; + contentLengthAsString << contentLength; + std::string contentLengthFieldName(PCPP_SIP_CONTENT_LENGTH_FIELD); + HeaderField* contentLengthField = getFieldByName(contentLengthFieldName); + if (contentLengthField == nullptr) { + HeaderField* prevField = getFieldByName(prevFieldName); + contentLengthField = insertField(prevField, PCPP_SIP_CONTENT_LENGTH_FIELD, + contentLengthAsString.str()); + } else + contentLengthField->setFieldValue(contentLengthAsString.str()); + + return contentLengthField; +} + +void SipLayer::parseNextLayer() { + if (getLayerPayloadSize() == 0) + return; + + size_t headerLen = getHeaderLen(); + if (getContentLength() > 0) { + m_NextLayer = + new SdpLayer(m_Data + headerLen, m_DataLen - headerLen, this, m_Packet); + } else { + m_NextLayer = new PayloadLayer(m_Data + headerLen, m_DataLen - headerLen, + this, m_Packet); + } +} + +void SipLayer::computeCalculateFields() { + HeaderField* contentLengthField = + getFieldByName(PCPP_SIP_CONTENT_LENGTH_FIELD); + if (contentLengthField == nullptr) + return; + + size_t headerLen = getHeaderLen(); + if (m_DataLen > headerLen) { + int currentContentLength = getContentLength(); + if (currentContentLength != (int)(m_DataLen - headerLen)) + setContentLength(m_DataLen - headerLen); + } } -HeaderField* SipLayer::setContentLength(int contentLength, const std::string &prevFieldName) -{ - std::ostringstream contentLengthAsString; - contentLengthAsString << contentLength; - std::string contentLengthFieldName(PCPP_SIP_CONTENT_LENGTH_FIELD); - HeaderField* contentLengthField = getFieldByName(contentLengthFieldName); - if (contentLengthField == nullptr) - { - HeaderField* prevField = getFieldByName(prevFieldName); - contentLengthField = insertField(prevField, PCPP_SIP_CONTENT_LENGTH_FIELD, contentLengthAsString.str()); - } - else - contentLengthField->setFieldValue(contentLengthAsString.str()); - - return contentLengthField; -} - -void SipLayer::parseNextLayer() -{ - if (getLayerPayloadSize() == 0) - return; - - size_t headerLen = getHeaderLen(); - if (getContentLength() > 0) - { - m_NextLayer = new SdpLayer(m_Data + headerLen, m_DataLen - headerLen, this, m_Packet); - } - else - { - m_NextLayer = new PayloadLayer(m_Data + headerLen, m_DataLen - headerLen, this, m_Packet); - } -} - -void SipLayer::computeCalculateFields() -{ - HeaderField* contentLengthField = getFieldByName(PCPP_SIP_CONTENT_LENGTH_FIELD); - if (contentLengthField == nullptr) - return; - - size_t headerLen = getHeaderLen(); - if (m_DataLen > headerLen) - { - int currentContentLength = getContentLength(); - if (currentContentLength != (int)(m_DataLen - headerLen)) - setContentLength(m_DataLen - headerLen); - } -} - - - - - - - - // -------- Class SipRequestFirstLine ----------------- -SipRequestFirstLine::SipRequestFirstLine(SipRequestLayer* sipRequest) : m_SipRequest(sipRequest) -{ - m_Method = parseMethod((char*)m_SipRequest->m_Data, m_SipRequest->getDataLen()); - if (m_Method == SipRequestLayer::SipMethodUnknown) - { - m_UriOffset = -1; - PCPP_LOG_DEBUG("Couldn't resolve SIP request method"); - } - else - m_UriOffset = SipMethodEnumToString[m_Method].length() + 1; - - parseVersion(); - - char* endOfFirstLine; - if ((endOfFirstLine = (char *)memchr((char*)(m_SipRequest->m_Data + m_VersionOffset), '\n', m_SipRequest->m_DataLen-(size_t)m_VersionOffset)) != nullptr) - { - m_FirstLineEndOffset = endOfFirstLine - (char*)m_SipRequest->m_Data + 1; - m_IsComplete = true; - } - else - { - m_FirstLineEndOffset = m_SipRequest->getDataLen(); - m_IsComplete = false; - } - - if (Logger::getInstance().isDebugEnabled(PacketLogModuleSipLayer)) - { - std::string method = (m_Method == SipRequestLayer::SipMethodUnknown? "Unknown" : SipMethodEnumToString[m_Method]); - PCPP_LOG_DEBUG("Method='" << method << "'; SIP version='" << m_Version << "'; URI='" << getUri() << "'"); - } -} - -SipRequestFirstLine::SipRequestFirstLine(SipRequestLayer* sipRequest, SipRequestLayer::SipMethod method, const std::string& version, const std::string& uri) -try // throw(SipRequestFirstLineException) -{ - if (method == SipRequestLayer::SipMethodUnknown) - { - m_Exception.setMessage("Method supplied was SipMethodUnknown"); - throw m_Exception; - } - - if (version == "") - { - m_Exception.setMessage("Version supplied was empty string"); - throw m_Exception; - } - - m_SipRequest = sipRequest; - - m_Method = method; - m_Version = version; - - std::string firstLine = SipMethodEnumToString[m_Method] + " " + uri + " " + version + "\r\n"; - - m_UriOffset = SipMethodEnumToString[m_Method].length() + 1; - m_FirstLineEndOffset = firstLine.length(); - m_VersionOffset = m_UriOffset + uri.length() + 6; - - m_SipRequest->m_DataLen = firstLine.length(); - m_SipRequest->m_Data = new uint8_t[m_SipRequest->m_DataLen]; - memcpy(m_SipRequest->m_Data, firstLine.c_str(), m_SipRequest->m_DataLen); - - m_IsComplete = true; -} -catch(const SipRequestFirstLineException&) -{ - throw; -} -catch(...) -{ - std::terminate(); +SipRequestFirstLine::SipRequestFirstLine(SipRequestLayer* sipRequest) + : m_SipRequest(sipRequest) { + m_Method = + parseMethod((char*)m_SipRequest->m_Data, m_SipRequest->getDataLen()); + if (m_Method == SipRequestLayer::SipMethodUnknown) { + m_UriOffset = -1; + PCPP_LOG_DEBUG("Couldn't resolve SIP request method"); + } else + m_UriOffset = SipMethodEnumToString[m_Method].length() + 1; + + parseVersion(); + + char* endOfFirstLine; + if ((endOfFirstLine = (char*)memchr( + (char*)(m_SipRequest->m_Data + m_VersionOffset), '\n', + m_SipRequest->m_DataLen - (size_t)m_VersionOffset)) != nullptr) { + m_FirstLineEndOffset = endOfFirstLine - (char*)m_SipRequest->m_Data + 1; + m_IsComplete = true; + } else { + m_FirstLineEndOffset = m_SipRequest->getDataLen(); + m_IsComplete = false; + } + + if (Logger::getInstance().isDebugEnabled(PacketLogModuleSipLayer)) { + std::string method = (m_Method == SipRequestLayer::SipMethodUnknown + ? "Unknown" + : SipMethodEnumToString[m_Method]); + PCPP_LOG_DEBUG("Method='" << method << "'; SIP version='" << m_Version + << "'; URI='" << getUri() << "'"); + } +} + +SipRequestFirstLine::SipRequestFirstLine( + SipRequestLayer* sipRequest, SipRequestLayer::SipMethod method, + const std::string& version, + const std::string& uri) try // throw(SipRequestFirstLineException) +{ + if (method == SipRequestLayer::SipMethodUnknown) { + m_Exception.setMessage("Method supplied was SipMethodUnknown"); + throw m_Exception; + } + + if (version == "") { + m_Exception.setMessage("Version supplied was empty string"); + throw m_Exception; + } + + m_SipRequest = sipRequest; + + m_Method = method; + m_Version = version; + + std::string firstLine = + SipMethodEnumToString[m_Method] + " " + uri + " " + version + "\r\n"; + + m_UriOffset = SipMethodEnumToString[m_Method].length() + 1; + m_FirstLineEndOffset = firstLine.length(); + m_VersionOffset = m_UriOffset + uri.length() + 6; + + m_SipRequest->m_DataLen = firstLine.length(); + m_SipRequest->m_Data = new uint8_t[m_SipRequest->m_DataLen]; + memcpy(m_SipRequest->m_Data, firstLine.c_str(), m_SipRequest->m_DataLen); + + m_IsComplete = true; +} +catch (const SipRequestFirstLineException&) { + throw; +} +catch (...) { + std::terminate(); +} + +SipRequestLayer::SipMethod SipRequestFirstLine::parseMethod(const char* data, + size_t dataLen) { + if (!data || dataLen < 4) { + return SipRequestLayer::SipMethodUnknown; + } + + size_t spaceIndex = 0; + while (spaceIndex < dataLen && data[spaceIndex] != ' ') { + spaceIndex++; + } + + if (spaceIndex == 0 || spaceIndex == dataLen) { + return SipRequestLayer::SipMethodUnknown; + } + + auto methodAdEnum = + SipMethodStringToEnum.find(std::string(data, data + spaceIndex)); + if (methodAdEnum == SipMethodStringToEnum.end()) { + return SipRequestLayer::SipMethodUnknown; + } + return methodAdEnum->second; +} + +void SipRequestFirstLine::parseVersion() { + if (m_SipRequest->getDataLen() < static_cast(m_UriOffset)) { + m_Version = ""; + m_VersionOffset = -1; + return; + } + + char* data = (char*)(m_SipRequest->m_Data + m_UriOffset); + char* verPos = (char*)cross_platform_memmem( + data, m_SipRequest->getDataLen() - m_UriOffset, " SIP/", 5); + if (verPos == nullptr) { + m_Version = ""; + m_VersionOffset = -1; + return; + } + + // verify packet doesn't end before the version, meaning still left place for + // " SIP/x.y" (7 chars) + if ((uint16_t)(verPos + 7 - (char*)m_SipRequest->m_Data) > + m_SipRequest->getDataLen()) { + m_Version = ""; + m_VersionOffset = -1; + return; + } + + // skip the space char + verPos++; + + int endOfVerPos = 0; + while (((verPos + endOfVerPos) < + (char*)(m_SipRequest->m_Data + m_SipRequest->m_DataLen)) && + ((verPos + endOfVerPos)[0] != '\r') && + ((verPos + endOfVerPos)[0] != '\n')) + endOfVerPos++; + + m_Version = std::string(verPos, endOfVerPos); + + m_VersionOffset = verPos - (char*)m_SipRequest->m_Data; +} + +bool SipRequestFirstLine::setMethod(SipRequestLayer::SipMethod newMethod) { + if (newMethod == SipRequestLayer::SipMethodUnknown) { + PCPP_LOG_ERROR("Requested method is SipMethodUnknown"); + return false; + } + + // extend or shorten layer + int lengthDifference = SipMethodEnumToString[newMethod].length() - + SipMethodEnumToString[m_Method].length(); + if (lengthDifference > 0) { + if (!m_SipRequest->extendLayer(0, lengthDifference)) { + PCPP_LOG_ERROR("Cannot change layer size"); + return false; + } + } else if (lengthDifference < 0) { + if (!m_SipRequest->shortenLayer(0, 0 - lengthDifference)) { + PCPP_LOG_ERROR("Cannot change layer size"); + return false; + } + } + + if (lengthDifference != 0) { + m_SipRequest->shiftFieldsOffset(m_SipRequest->getFirstField(), + lengthDifference); + m_SipRequest->m_FieldsOffset += lengthDifference; + } + + memcpy(m_SipRequest->m_Data, SipMethodEnumToString[newMethod].c_str(), + SipMethodEnumToString[newMethod].length()); + + m_UriOffset += lengthDifference; + m_VersionOffset += lengthDifference; + m_FirstLineEndOffset += lengthDifference; + + m_Method = newMethod; + + return true; +} + +std::string SipRequestFirstLine::getUri() const { + std::string result; + if (m_UriOffset != -1 && m_VersionOffset != -1) + result.assign((char*)(m_SipRequest->m_Data + m_UriOffset), + m_VersionOffset - 1 - m_UriOffset); + + // else first line is illegal, return empty string + + return result; +} + +bool SipRequestFirstLine::setUri(const std::string& newUri) { + if (newUri == "") { + PCPP_LOG_ERROR("URI cannot be empty"); + return false; + } + + // extend or shorten layer + std::string currentUri = getUri(); + int lengthDifference = newUri.length() - currentUri.length(); + if (lengthDifference > 0) { + if (!m_SipRequest->extendLayer(m_UriOffset, lengthDifference)) { + PCPP_LOG_ERROR("Cannot change layer size"); + return false; + } + } else if (lengthDifference < 0) { + if (!m_SipRequest->shortenLayer(m_UriOffset, 0 - lengthDifference)) { + PCPP_LOG_ERROR("Cannot change layer size"); + return false; + } + } + + if (lengthDifference != 0) { + m_SipRequest->shiftFieldsOffset(m_SipRequest->getFirstField(), + lengthDifference); + m_SipRequest->m_FieldsOffset += lengthDifference; + } + + memcpy(m_SipRequest->m_Data + m_UriOffset, newUri.c_str(), newUri.length()); + + m_VersionOffset += lengthDifference; + m_FirstLineEndOffset += lengthDifference; + + return true; } -SipRequestLayer::SipMethod SipRequestFirstLine::parseMethod(const char* data, size_t dataLen) -{ - if (!data || dataLen < 4) - { - return SipRequestLayer::SipMethodUnknown; - } - - size_t spaceIndex = 0; - while (spaceIndex < dataLen && data[spaceIndex] != ' ' ) - { - spaceIndex++; - } - - if (spaceIndex == 0 || spaceIndex == dataLen) - { - return SipRequestLayer::SipMethodUnknown; - } - - auto methodAdEnum = SipMethodStringToEnum.find(std::string(data, data + spaceIndex)); - if (methodAdEnum == SipMethodStringToEnum.end()) - { - return SipRequestLayer::SipMethodUnknown; - } - return methodAdEnum->second; -} - -void SipRequestFirstLine::parseVersion() -{ - if (m_SipRequest->getDataLen() < static_cast(m_UriOffset)) - { - m_Version = ""; - m_VersionOffset = -1; - return; - } - - char* data = (char*)(m_SipRequest->m_Data + m_UriOffset); - char* verPos = (char*)cross_platform_memmem(data, m_SipRequest->getDataLen() - m_UriOffset, " SIP/", 5); - if (verPos == nullptr) - { - m_Version = ""; - m_VersionOffset = -1; - return; - } - - // verify packet doesn't end before the version, meaning still left place for " SIP/x.y" (7 chars) - if ((uint16_t)(verPos + 7 - (char*)m_SipRequest->m_Data) > m_SipRequest->getDataLen()) - { - m_Version = ""; - m_VersionOffset = -1; - return; - } - - //skip the space char - verPos++; - - int endOfVerPos = 0; - while (((verPos + endOfVerPos) < (char *) (m_SipRequest->m_Data + m_SipRequest->m_DataLen)) && ((verPos+endOfVerPos)[0] != '\r') && ((verPos+endOfVerPos)[0] != '\n')) - endOfVerPos++; - - m_Version = std::string(verPos, endOfVerPos); - - m_VersionOffset = verPos - (char*)m_SipRequest->m_Data; -} - -bool SipRequestFirstLine::setMethod(SipRequestLayer::SipMethod newMethod) -{ - if (newMethod == SipRequestLayer::SipMethodUnknown) - { - PCPP_LOG_ERROR("Requested method is SipMethodUnknown"); - return false; - } - - //extend or shorten layer - int lengthDifference = SipMethodEnumToString[newMethod].length() - SipMethodEnumToString[m_Method].length(); - if (lengthDifference > 0) - { - if (!m_SipRequest->extendLayer(0, lengthDifference)) - { - PCPP_LOG_ERROR("Cannot change layer size"); - return false; - } - } - else if (lengthDifference < 0) - { - if (!m_SipRequest->shortenLayer(0, 0-lengthDifference)) - { - PCPP_LOG_ERROR("Cannot change layer size"); - return false; - - } - } - - if (lengthDifference != 0) - { - m_SipRequest->shiftFieldsOffset(m_SipRequest->getFirstField(), lengthDifference); - m_SipRequest->m_FieldsOffset += lengthDifference; - } - - memcpy(m_SipRequest->m_Data, SipMethodEnumToString[newMethod].c_str(), SipMethodEnumToString[newMethod].length()); - - m_UriOffset += lengthDifference; - m_VersionOffset += lengthDifference; - m_FirstLineEndOffset += lengthDifference; - - m_Method = newMethod; - - return true; -} - -std::string SipRequestFirstLine::getUri() const -{ - std::string result; - if (m_UriOffset != -1 && m_VersionOffset != -1) - result.assign((char*)(m_SipRequest->m_Data + m_UriOffset), m_VersionOffset-1-m_UriOffset); - - //else first line is illegal, return empty string - - return result; -} - -bool SipRequestFirstLine::setUri(const std::string& newUri) -{ - if (newUri == "") - { - PCPP_LOG_ERROR("URI cannot be empty"); - return false; - } - - //extend or shorten layer - std::string currentUri = getUri(); - int lengthDifference = newUri.length() - currentUri.length(); - if (lengthDifference > 0) - { - if (!m_SipRequest->extendLayer(m_UriOffset, lengthDifference)) - { - PCPP_LOG_ERROR("Cannot change layer size"); - return false; - } - } - else if (lengthDifference < 0) - { - if (!m_SipRequest->shortenLayer(m_UriOffset, 0-lengthDifference)) - { - PCPP_LOG_ERROR("Cannot change layer size"); - return false; - } - } - - if (lengthDifference != 0) - { - m_SipRequest->shiftFieldsOffset(m_SipRequest->getFirstField(), lengthDifference); - m_SipRequest->m_FieldsOffset += lengthDifference; - } - - memcpy(m_SipRequest->m_Data + m_UriOffset, newUri.c_str(), newUri.length()); - - m_VersionOffset += lengthDifference; - m_FirstLineEndOffset += lengthDifference; - - return true; -} - - - - - // -------- Class SipRequestLayer ----------------- -SipRequestLayer::SipRequestLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : SipLayer(data, dataLen, prevLayer, packet) -{ - m_Protocol = SIPRequest; - m_FirstLine = new SipRequestFirstLine(this); - m_FieldsOffset = m_FirstLine->getSize(); - parseFields(); +SipRequestLayer::SipRequestLayer(uint8_t* data, size_t dataLen, + Layer* prevLayer, Packet* packet) + : SipLayer(data, dataLen, prevLayer, packet) { + m_Protocol = SIPRequest; + m_FirstLine = new SipRequestFirstLine(this); + m_FieldsOffset = m_FirstLine->getSize(); + parseFields(); } -SipRequestLayer::SipRequestLayer(SipMethod method, const std::string& requestUri, const std::string& version) -{ - m_Protocol = SIPRequest; - m_FirstLine = new SipRequestFirstLine(this, method, std::move(version), std::move(requestUri)); - m_FieldsOffset = m_FirstLine->getSize(); +SipRequestLayer::SipRequestLayer(SipMethod method, + const std::string& requestUri, + const std::string& version) { + m_Protocol = SIPRequest; + m_FirstLine = new SipRequestFirstLine(this, method, std::move(version), + std::move(requestUri)); + m_FieldsOffset = m_FirstLine->getSize(); } -SipRequestLayer::SipRequestLayer(const SipRequestLayer& other) : SipLayer(other) -{ - m_FirstLine = new SipRequestFirstLine(this); +SipRequestLayer::SipRequestLayer(const SipRequestLayer& other) + : SipLayer(other) { + m_FirstLine = new SipRequestFirstLine(this); } -SipRequestLayer& SipRequestLayer::operator=(const SipRequestLayer& other) -{ - SipLayer::operator=(other); +SipRequestLayer& SipRequestLayer::operator=(const SipRequestLayer& other) { + SipLayer::operator=(other); - if (m_FirstLine != nullptr) - delete m_FirstLine; + if (m_FirstLine != nullptr) + delete m_FirstLine; - m_FirstLine = new SipRequestFirstLine(this); + m_FirstLine = new SipRequestFirstLine(this); - return *this; -} - -SipRequestLayer::~SipRequestLayer() -{ - delete m_FirstLine; -} - -std::string SipRequestLayer::toString() const -{ - static const int maxLengthToPrint = 120; - std::string result = "SIP request, "; - int size = m_FirstLine->getSize() - 2; // the -2 is to remove \r\n at the end of the first line - if (size <= 0) - { - result += std::string("CORRUPT DATA"); - return result; - } - if (size <= maxLengthToPrint) - { - char* firstLine = new char[size+1]; - strncpy(firstLine, (char*)m_Data, size); - firstLine[size] = 0; - result += std::string(firstLine); - delete[] firstLine; - } - else - { - char firstLine[maxLengthToPrint+1]; - strncpy(firstLine, (char*)m_Data, maxLengthToPrint-3); - firstLine[maxLengthToPrint-3] = '.'; - firstLine[maxLengthToPrint-2] = '.'; - firstLine[maxLengthToPrint-1] = '.'; - firstLine[maxLengthToPrint] = 0; - result += std::string(firstLine); - } - - return result; + return *this; } +SipRequestLayer::~SipRequestLayer() { delete m_FirstLine; } +std::string SipRequestLayer::toString() const { + static const int maxLengthToPrint = 120; + std::string result = "SIP request, "; + int size = m_FirstLine->getSize() - + 2; // the -2 is to remove \r\n at the end of the first line + if (size <= 0) { + result += std::string("CORRUPT DATA"); + return result; + } + if (size <= maxLengthToPrint) { + char* firstLine = new char[size + 1]; + strncpy(firstLine, (char*)m_Data, size); + firstLine[size] = 0; + result += std::string(firstLine); + delete[] firstLine; + } else { + char firstLine[maxLengthToPrint + 1]; + strncpy(firstLine, (char*)m_Data, maxLengthToPrint - 3); + firstLine[maxLengthToPrint - 3] = '.'; + firstLine[maxLengthToPrint - 2] = '.'; + firstLine[maxLengthToPrint - 1] = '.'; + firstLine[maxLengthToPrint] = 0; + result += std::string(firstLine); + } - - + return result; +} // -------- Class SipResponseLayer ----------------- - - const std::string StatusCodeEnumToString[77] = { - "Trying", - "Ringing", - "Call is Being Forwarded", - "Queued", - "Session in Progress", - "Early Dialog Terminated", - "OK", - "Accepted", - "No Notification", - "Multiple Choices", - "Moved Permanently", - "Moved Temporarily", - "Use Proxy", - "Alternative Service", - "Bad Request", - "Unauthorized", - "Payment Required", - "Forbidden", - "Not Found", - "Method Not Allowed", - "Not Acceptable", - "Proxy Authentication Required", - "Request Timeout", - "Conflict", - "Gone", - "Length Required", - "Conditional Request Failed", - "Request Entity Too Large", - "Request-URI Too Long", - "Unsupported Media Type", - "Unsupported URI Scheme", - "Unknown Resource-Priority", - "Bad Extension", - "Extension Required", - "Session Interval Too Small", - "Interval Too Brief", - "Bad Location Information", - "Bad Alert Message", - "Use Identity Header", - "Provide Referrer Identity", - "Flow Failed", - "Anonymity Disallowed", - "Bad Identity-Info", - "Unsupported Certificate", - "Invalid Identity Header", - "First Hop Lacks Outbound Support", - "Max-Breadth Exceeded", - "Bad Info Package", - "Consent Needed", - "Temporarily Unavailable", - "Call_Transaction Does Not Exist", - "Loop Detected", - "Too Many Hops", - "Address Incomplete", - "Ambiguous", - "Busy Here", - "Request Terminated", - "Not Acceptable Here", - "Bad Event", - "Request Pending", - "Undecipherable", - "Security Agreement Required", - "Server Internal Error", - "Not Implemented", - "Bad Gateway", - "Service Unavailable", - "Server Timeout", - "Version Not Supported", - "Message Too Large", - "Push Notification Service Not Supported", - "Precondition Failure", - "Busy Everywhere", - "Decline", - "Does Not Exist Anywhere", - "Not Acceptable", - "Unwanted", - "Rejected" -}; - + "Trying", + "Ringing", + "Call is Being Forwarded", + "Queued", + "Session in Progress", + "Early Dialog Terminated", + "OK", + "Accepted", + "No Notification", + "Multiple Choices", + "Moved Permanently", + "Moved Temporarily", + "Use Proxy", + "Alternative Service", + "Bad Request", + "Unauthorized", + "Payment Required", + "Forbidden", + "Not Found", + "Method Not Allowed", + "Not Acceptable", + "Proxy Authentication Required", + "Request Timeout", + "Conflict", + "Gone", + "Length Required", + "Conditional Request Failed", + "Request Entity Too Large", + "Request-URI Too Long", + "Unsupported Media Type", + "Unsupported URI Scheme", + "Unknown Resource-Priority", + "Bad Extension", + "Extension Required", + "Session Interval Too Small", + "Interval Too Brief", + "Bad Location Information", + "Bad Alert Message", + "Use Identity Header", + "Provide Referrer Identity", + "Flow Failed", + "Anonymity Disallowed", + "Bad Identity-Info", + "Unsupported Certificate", + "Invalid Identity Header", + "First Hop Lacks Outbound Support", + "Max-Breadth Exceeded", + "Bad Info Package", + "Consent Needed", + "Temporarily Unavailable", + "Call_Transaction Does Not Exist", + "Loop Detected", + "Too Many Hops", + "Address Incomplete", + "Ambiguous", + "Busy Here", + "Request Terminated", + "Not Acceptable Here", + "Bad Event", + "Request Pending", + "Undecipherable", + "Security Agreement Required", + "Server Internal Error", + "Not Implemented", + "Bad Gateway", + "Service Unavailable", + "Server Timeout", + "Version Not Supported", + "Message Too Large", + "Push Notification Service Not Supported", + "Precondition Failure", + "Busy Everywhere", + "Decline", + "Does Not Exist Anywhere", + "Not Acceptable", + "Unwanted", + "Rejected"}; const int StatusCodeEnumToInt[77] = { - 100, - 180, - 181, - 182, - 183, - 199, - 200, - 202, - 204, - 300, - 301, - 302, - 305, - 380, - 400, - 401, - 402, - 403, - 404, - 405, - 406, - 407, - 408, - 409, - 410, - 411, - 412, - 413, - 414, - 415, - 416, - 417, - 420, - 421, - 422, - 425, - 423, - 424, - 428, - 429, - 430, - 433, - 436, - 437, - 438, - 439, - 440, - 469, - 470, - 480, - 481, - 482, - 483, - 484, - 485, - 486, - 487, - 488, - 489, - 491, - 493, - 494, - 500, - 501, - 502, - 503, - 504, - 505, - 513, - 555, - 580, - 600, - 603, - 604, - 606, - 607, - 608 -}; - - -const std::unordered_map StatusCodeStringToEnumMap { - {"100", SipResponseLayer::SipResponseStatusCode::Sip100Trying }, - {"180", SipResponseLayer::SipResponseStatusCode::Sip180Ringing }, - {"181", SipResponseLayer::SipResponseStatusCode::Sip181CallisBeingForwarded }, - {"182", SipResponseLayer::SipResponseStatusCode::Sip182Queued }, - {"183", SipResponseLayer::SipResponseStatusCode::Sip183SessioninProgress }, - {"199", SipResponseLayer::SipResponseStatusCode::Sip199EarlyDialogTerminated }, - {"200", SipResponseLayer::SipResponseStatusCode::Sip200OK }, - {"202", SipResponseLayer::SipResponseStatusCode::Sip202Accepted }, - {"204", SipResponseLayer::SipResponseStatusCode::Sip204NoNotification }, - {"300", SipResponseLayer::SipResponseStatusCode::Sip300MultipleChoices }, - {"301", SipResponseLayer::SipResponseStatusCode::Sip301MovedPermanently }, - {"302", SipResponseLayer::SipResponseStatusCode::Sip302MovedTemporarily }, - {"305", SipResponseLayer::SipResponseStatusCode::Sip305UseProxy }, - {"380", SipResponseLayer::SipResponseStatusCode::Sip380AlternativeService }, - {"400", SipResponseLayer::SipResponseStatusCode::Sip400BadRequest }, - {"401", SipResponseLayer::SipResponseStatusCode::Sip401Unauthorized }, - {"402", SipResponseLayer::SipResponseStatusCode::Sip402PaymentRequired }, - {"403", SipResponseLayer::SipResponseStatusCode::Sip403Forbidden }, - {"404", SipResponseLayer::SipResponseStatusCode::Sip404NotFound }, - {"405", SipResponseLayer::SipResponseStatusCode::Sip405MethodNotAllowed }, - {"406", SipResponseLayer::SipResponseStatusCode::Sip406NotAcceptable }, - {"407", SipResponseLayer::SipResponseStatusCode::Sip407ProxyAuthenticationRequired }, - {"408", SipResponseLayer::SipResponseStatusCode::Sip408RequestTimeout }, - {"409", SipResponseLayer::SipResponseStatusCode::Sip409Conflict }, - {"410", SipResponseLayer::SipResponseStatusCode::Sip410Gone }, - {"411", SipResponseLayer::SipResponseStatusCode::Sip411LengthRequired }, - {"412", SipResponseLayer::SipResponseStatusCode::Sip412ConditionalRequestFailed }, - {"413", SipResponseLayer::SipResponseStatusCode::Sip413RequestEntityTooLarge }, - {"414", SipResponseLayer::SipResponseStatusCode::Sip414RequestURITooLong }, - {"415", SipResponseLayer::SipResponseStatusCode::Sip415UnsupportedMediaType }, - {"416", SipResponseLayer::SipResponseStatusCode::Sip416UnsupportedURIScheme }, - {"417", SipResponseLayer::SipResponseStatusCode::Sip417UnknownResourcePriority }, - {"420", SipResponseLayer::SipResponseStatusCode::Sip420BadExtension }, - {"421", SipResponseLayer::SipResponseStatusCode::Sip421ExtensionRequired }, - {"422", SipResponseLayer::SipResponseStatusCode::Sip422SessionIntervalTooSmall }, - {"423", SipResponseLayer::SipResponseStatusCode::Sip423IntervalTooBrief }, - {"424", SipResponseLayer::SipResponseStatusCode::Sip424BadLocationInformation }, - {"425", SipResponseLayer::SipResponseStatusCode::Sip425BadAlertMessage }, - {"428", SipResponseLayer::SipResponseStatusCode::Sip428UseIdentityHeader }, - {"429", SipResponseLayer::SipResponseStatusCode::Sip429ProvideReferrerIdentity }, - {"430", SipResponseLayer::SipResponseStatusCode::Sip430FlowFailed }, - {"433", SipResponseLayer::SipResponseStatusCode::Sip433AnonymityDisallowed }, - {"436", SipResponseLayer::SipResponseStatusCode::Sip436BadIdentityInfo }, - {"437", SipResponseLayer::SipResponseStatusCode::Sip437UnsupportedCertificate }, - {"438", SipResponseLayer::SipResponseStatusCode::Sip438InvalidIdentityHeader }, - {"439", SipResponseLayer::SipResponseStatusCode::Sip439FirstHopLacksOutboundSupport }, - {"440", SipResponseLayer::SipResponseStatusCode::Sip440MaxBreadthExceeded }, - {"469", SipResponseLayer::SipResponseStatusCode::Sip469BadInfoPackage }, - {"470", SipResponseLayer::SipResponseStatusCode::Sip470ConsentNeeded }, - {"480", SipResponseLayer::SipResponseStatusCode::Sip480TemporarilyUnavailable }, - {"481", SipResponseLayer::SipResponseStatusCode::Sip481Call_TransactionDoesNotExist }, - {"482", SipResponseLayer::SipResponseStatusCode::Sip482LoopDetected }, - {"483", SipResponseLayer::SipResponseStatusCode::Sip483TooManyHops }, - {"484", SipResponseLayer::SipResponseStatusCode::Sip484AddressIncomplete }, - {"485", SipResponseLayer::SipResponseStatusCode::Sip485Ambiguous }, - {"486", SipResponseLayer::SipResponseStatusCode::Sip486BusyHere }, - {"487", SipResponseLayer::SipResponseStatusCode::Sip487RequestTerminated }, - {"488", SipResponseLayer::SipResponseStatusCode::Sip488NotAcceptableHere }, - {"489", SipResponseLayer::SipResponseStatusCode::Sip489BadEvent }, - {"491", SipResponseLayer::SipResponseStatusCode::Sip491RequestPending }, - {"493", SipResponseLayer::SipResponseStatusCode::Sip493Undecipherable }, - {"494", SipResponseLayer::SipResponseStatusCode::Sip494SecurityAgreementRequired }, - {"500", SipResponseLayer::SipResponseStatusCode::Sip500ServerInternalError }, - {"501", SipResponseLayer::SipResponseStatusCode::Sip501NotImplemented }, - {"502", SipResponseLayer::SipResponseStatusCode::Sip502BadGateway }, - {"503", SipResponseLayer::SipResponseStatusCode::Sip503ServiceUnavailable }, - {"504", SipResponseLayer::SipResponseStatusCode::Sip504ServerTimeout }, - {"505", SipResponseLayer::SipResponseStatusCode::Sip505VersionNotSupported }, - {"513", SipResponseLayer::SipResponseStatusCode::Sip513MessageTooLarge }, - {"555", SipResponseLayer::SipResponseStatusCode::Sip555PushNotificationServiceNotSupported }, - {"580", SipResponseLayer::SipResponseStatusCode::Sip580PreconditionFailure }, - {"600", SipResponseLayer::SipResponseStatusCode::Sip600BusyEverywhere }, - {"603", SipResponseLayer::SipResponseStatusCode::Sip603Decline }, - {"604", SipResponseLayer::SipResponseStatusCode::Sip604DoesNotExistAnywhere }, - {"606", SipResponseLayer::SipResponseStatusCode::Sip606NotAcceptable }, - {"607", SipResponseLayer::SipResponseStatusCode::Sip607Unwanted }, - {"608", SipResponseLayer::SipResponseStatusCode::Sip608Rejected }, -}; - - - -SipResponseLayer::SipResponseLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : SipLayer(data, dataLen, prevLayer, packet) -{ - m_Protocol = SIPResponse; - m_FirstLine = new SipResponseFirstLine(this); - m_FieldsOffset = m_FirstLine->getSize(); - parseFields(); -} - -SipResponseLayer::SipResponseLayer(SipResponseLayer::SipResponseStatusCode statusCode, std::string statusCodeString, const std::string& sipVersion) -{ - m_Protocol = SIPResponse; - m_FirstLine = new SipResponseFirstLine(this, std::move(sipVersion), statusCode, std::move(statusCodeString)); - m_FieldsOffset = m_FirstLine->getSize(); + 100, 180, 181, 182, 183, 199, 200, 202, 204, 300, 301, 302, 305, + 380, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, + 412, 413, 414, 415, 416, 417, 420, 421, 422, 425, 423, 424, 428, + 429, 430, 433, 436, 437, 438, 439, 440, 469, 470, 480, 481, 482, + 483, 484, 485, 486, 487, 488, 489, 491, 493, 494, 500, 501, 502, + 503, 504, 505, 513, 555, 580, 600, 603, 604, 606, 607, 608}; + +const std::unordered_map + StatusCodeStringToEnumMap{ + {"100", SipResponseLayer::SipResponseStatusCode::Sip100Trying}, + {"180", SipResponseLayer::SipResponseStatusCode::Sip180Ringing}, + {"181", + SipResponseLayer::SipResponseStatusCode::Sip181CallisBeingForwarded}, + {"182", SipResponseLayer::SipResponseStatusCode::Sip182Queued}, + {"183", + SipResponseLayer::SipResponseStatusCode::Sip183SessioninProgress}, + {"199", + SipResponseLayer::SipResponseStatusCode::Sip199EarlyDialogTerminated}, + {"200", SipResponseLayer::SipResponseStatusCode::Sip200OK}, + {"202", SipResponseLayer::SipResponseStatusCode::Sip202Accepted}, + {"204", SipResponseLayer::SipResponseStatusCode::Sip204NoNotification}, + {"300", SipResponseLayer::SipResponseStatusCode::Sip300MultipleChoices}, + {"301", + SipResponseLayer::SipResponseStatusCode::Sip301MovedPermanently}, + {"302", + SipResponseLayer::SipResponseStatusCode::Sip302MovedTemporarily}, + {"305", SipResponseLayer::SipResponseStatusCode::Sip305UseProxy}, + {"380", + SipResponseLayer::SipResponseStatusCode::Sip380AlternativeService}, + {"400", SipResponseLayer::SipResponseStatusCode::Sip400BadRequest}, + {"401", SipResponseLayer::SipResponseStatusCode::Sip401Unauthorized}, + {"402", SipResponseLayer::SipResponseStatusCode::Sip402PaymentRequired}, + {"403", SipResponseLayer::SipResponseStatusCode::Sip403Forbidden}, + {"404", SipResponseLayer::SipResponseStatusCode::Sip404NotFound}, + {"405", + SipResponseLayer::SipResponseStatusCode::Sip405MethodNotAllowed}, + {"406", SipResponseLayer::SipResponseStatusCode::Sip406NotAcceptable}, + {"407", SipResponseLayer::SipResponseStatusCode:: + Sip407ProxyAuthenticationRequired}, + {"408", SipResponseLayer::SipResponseStatusCode::Sip408RequestTimeout}, + {"409", SipResponseLayer::SipResponseStatusCode::Sip409Conflict}, + {"410", SipResponseLayer::SipResponseStatusCode::Sip410Gone}, + {"411", SipResponseLayer::SipResponseStatusCode::Sip411LengthRequired}, + {"412", SipResponseLayer::SipResponseStatusCode:: + Sip412ConditionalRequestFailed}, + {"413", + SipResponseLayer::SipResponseStatusCode::Sip413RequestEntityTooLarge}, + {"414", + SipResponseLayer::SipResponseStatusCode::Sip414RequestURITooLong}, + {"415", + SipResponseLayer::SipResponseStatusCode::Sip415UnsupportedMediaType}, + {"416", + SipResponseLayer::SipResponseStatusCode::Sip416UnsupportedURIScheme}, + {"417", SipResponseLayer::SipResponseStatusCode:: + Sip417UnknownResourcePriority}, + {"420", SipResponseLayer::SipResponseStatusCode::Sip420BadExtension}, + {"421", + SipResponseLayer::SipResponseStatusCode::Sip421ExtensionRequired}, + {"422", SipResponseLayer::SipResponseStatusCode:: + Sip422SessionIntervalTooSmall}, + {"423", + SipResponseLayer::SipResponseStatusCode::Sip423IntervalTooBrief}, + {"424", + SipResponseLayer::SipResponseStatusCode::Sip424BadLocationInformation}, + {"425", SipResponseLayer::SipResponseStatusCode::Sip425BadAlertMessage}, + {"428", + SipResponseLayer::SipResponseStatusCode::Sip428UseIdentityHeader}, + {"429", SipResponseLayer::SipResponseStatusCode:: + Sip429ProvideReferrerIdentity}, + {"430", SipResponseLayer::SipResponseStatusCode::Sip430FlowFailed}, + {"433", + SipResponseLayer::SipResponseStatusCode::Sip433AnonymityDisallowed}, + {"436", SipResponseLayer::SipResponseStatusCode::Sip436BadIdentityInfo}, + {"437", + SipResponseLayer::SipResponseStatusCode::Sip437UnsupportedCertificate}, + {"438", + SipResponseLayer::SipResponseStatusCode::Sip438InvalidIdentityHeader}, + {"439", SipResponseLayer::SipResponseStatusCode:: + Sip439FirstHopLacksOutboundSupport}, + {"440", + SipResponseLayer::SipResponseStatusCode::Sip440MaxBreadthExceeded}, + {"469", SipResponseLayer::SipResponseStatusCode::Sip469BadInfoPackage}, + {"470", SipResponseLayer::SipResponseStatusCode::Sip470ConsentNeeded}, + {"480", + SipResponseLayer::SipResponseStatusCode::Sip480TemporarilyUnavailable}, + {"481", SipResponseLayer::SipResponseStatusCode:: + Sip481Call_TransactionDoesNotExist}, + {"482", SipResponseLayer::SipResponseStatusCode::Sip482LoopDetected}, + {"483", SipResponseLayer::SipResponseStatusCode::Sip483TooManyHops}, + {"484", + SipResponseLayer::SipResponseStatusCode::Sip484AddressIncomplete}, + {"485", SipResponseLayer::SipResponseStatusCode::Sip485Ambiguous}, + {"486", SipResponseLayer::SipResponseStatusCode::Sip486BusyHere}, + {"487", + SipResponseLayer::SipResponseStatusCode::Sip487RequestTerminated}, + {"488", + SipResponseLayer::SipResponseStatusCode::Sip488NotAcceptableHere}, + {"489", SipResponseLayer::SipResponseStatusCode::Sip489BadEvent}, + {"491", SipResponseLayer::SipResponseStatusCode::Sip491RequestPending}, + {"493", SipResponseLayer::SipResponseStatusCode::Sip493Undecipherable}, + {"494", SipResponseLayer::SipResponseStatusCode:: + Sip494SecurityAgreementRequired}, + {"500", + SipResponseLayer::SipResponseStatusCode::Sip500ServerInternalError}, + {"501", SipResponseLayer::SipResponseStatusCode::Sip501NotImplemented}, + {"502", SipResponseLayer::SipResponseStatusCode::Sip502BadGateway}, + {"503", + SipResponseLayer::SipResponseStatusCode::Sip503ServiceUnavailable}, + {"504", SipResponseLayer::SipResponseStatusCode::Sip504ServerTimeout}, + {"505", + SipResponseLayer::SipResponseStatusCode::Sip505VersionNotSupported}, + {"513", SipResponseLayer::SipResponseStatusCode::Sip513MessageTooLarge}, + {"555", SipResponseLayer::SipResponseStatusCode:: + Sip555PushNotificationServiceNotSupported}, + {"580", + SipResponseLayer::SipResponseStatusCode::Sip580PreconditionFailure}, + {"600", SipResponseLayer::SipResponseStatusCode::Sip600BusyEverywhere}, + {"603", SipResponseLayer::SipResponseStatusCode::Sip603Decline}, + {"604", + SipResponseLayer::SipResponseStatusCode::Sip604DoesNotExistAnywhere}, + {"606", SipResponseLayer::SipResponseStatusCode::Sip606NotAcceptable}, + {"607", SipResponseLayer::SipResponseStatusCode::Sip607Unwanted}, + {"608", SipResponseLayer::SipResponseStatusCode::Sip608Rejected}, + }; + +SipResponseLayer::SipResponseLayer(uint8_t* data, size_t dataLen, + Layer* prevLayer, Packet* packet) + : SipLayer(data, dataLen, prevLayer, packet) { + m_Protocol = SIPResponse; + m_FirstLine = new SipResponseFirstLine(this); + m_FieldsOffset = m_FirstLine->getSize(); + parseFields(); +} + +SipResponseLayer::SipResponseLayer( + SipResponseLayer::SipResponseStatusCode statusCode, + std::string statusCodeString, const std::string& sipVersion) { + m_Protocol = SIPResponse; + m_FirstLine = new SipResponseFirstLine( + this, std::move(sipVersion), statusCode, std::move(statusCodeString)); + m_FieldsOffset = m_FirstLine->getSize(); +} + +SipResponseLayer::~SipResponseLayer() { delete m_FirstLine; } + +SipResponseLayer::SipResponseLayer(const SipResponseLayer& other) + : SipLayer(other) { + m_FirstLine = new SipResponseFirstLine(this); +} + +SipResponseLayer& SipResponseLayer::operator=(const SipResponseLayer& other) { + SipLayer::operator=(other); + + if (m_FirstLine != nullptr) + delete m_FirstLine; + + m_FirstLine = new SipResponseFirstLine(this); + + return *this; +} + +std::string SipResponseLayer::toString() const { + static const int maxLengthToPrint = 120; + std::string result = "SIP response, "; + int size = m_FirstLine->getSize() - + 2; // the -2 is to remove \r\n at the end of the first line + if (size <= 0) { + result += std::string("CORRUPT DATA"); + return result; + } + if (size <= maxLengthToPrint) { + char* firstLine = new char[size + 1]; + strncpy(firstLine, (char*)m_Data, size); + firstLine[size] = 0; + result += std::string(firstLine); + delete[] firstLine; + } else { + char firstLine[maxLengthToPrint + 1]; + strncpy(firstLine, (char*)m_Data, maxLengthToPrint - 3); + firstLine[maxLengthToPrint - 3] = '.'; + firstLine[maxLengthToPrint - 2] = '.'; + firstLine[maxLengthToPrint - 1] = '.'; + firstLine[maxLengthToPrint] = 0; + result += std::string(firstLine); + } + + return result; } -SipResponseLayer::~SipResponseLayer() -{ - delete m_FirstLine; -} - - -SipResponseLayer::SipResponseLayer(const SipResponseLayer& other) : SipLayer(other) -{ - m_FirstLine = new SipResponseFirstLine(this); -} - -SipResponseLayer& SipResponseLayer::operator=(const SipResponseLayer& other) -{ - SipLayer::operator=(other); - - if (m_FirstLine != nullptr) - delete m_FirstLine; - - m_FirstLine = new SipResponseFirstLine(this); - - return *this; -} - -std::string SipResponseLayer::toString() const -{ - static const int maxLengthToPrint = 120; - std::string result = "SIP response, "; - int size = m_FirstLine->getSize() - 2; // the -2 is to remove \r\n at the end of the first line - if (size <= 0) - { - result += std::string("CORRUPT DATA"); - return result; - } - if (size <= maxLengthToPrint) - { - char* firstLine = new char[size+1]; - strncpy(firstLine, (char*)m_Data, size); - firstLine[size] = 0; - result += std::string(firstLine); - delete[] firstLine; - } - else - { - char firstLine[maxLengthToPrint+1]; - strncpy(firstLine, (char*)m_Data, maxLengthToPrint-3); - firstLine[maxLengthToPrint-3] = '.'; - firstLine[maxLengthToPrint-2] = '.'; - firstLine[maxLengthToPrint-1] = '.'; - firstLine[maxLengthToPrint] = 0; - result += std::string(firstLine); - } - - return result; -} - - - - - - - - // -------- Class SipResponseFirstLine ----------------- -int SipResponseFirstLine::getStatusCodeAsInt() const -{ - return StatusCodeEnumToInt[m_StatusCode]; -} - -std::string SipResponseFirstLine::getStatusCodeString() const -{ - std::string result; - const int statusStringOffset = 12; - if (m_StatusCode != SipResponseLayer::SipStatusCodeUnknown) - { - int statusStringEndOffset = m_FirstLineEndOffset - 2; - if ((*(m_SipResponse->m_Data + statusStringEndOffset)) != '\r') - statusStringEndOffset++; - result.assign((char*)(m_SipResponse->m_Data + statusStringOffset), statusStringEndOffset-statusStringOffset); - } - - //else first line is illegal, return empty string - - return result; -} - -bool SipResponseFirstLine::setStatusCode(SipResponseLayer::SipResponseStatusCode newStatusCode, std::string statusCodeString) -{ - if (newStatusCode == SipResponseLayer::SipStatusCodeUnknown) - { - PCPP_LOG_ERROR("Requested status code is SipStatusCodeUnknown"); - return false; - } - - //extend or shorten layer - - size_t statusStringOffset = 12; - if (statusCodeString == "") - statusCodeString = StatusCodeEnumToString[newStatusCode]; - int lengthDifference = statusCodeString.length() - getStatusCodeString().length(); - - if (lengthDifference > 0) - { - if (!m_SipResponse->extendLayer(statusStringOffset, lengthDifference)) - { - PCPP_LOG_ERROR("Cannot change layer size"); - return false; - } - } - else if (lengthDifference < 0) - { - if (!m_SipResponse->shortenLayer(statusStringOffset, 0-lengthDifference)) - { - PCPP_LOG_ERROR("Cannot change layer size"); - return false; - - } - } - - if (lengthDifference != 0) - { - m_SipResponse->shiftFieldsOffset(m_SipResponse->getFirstField(), lengthDifference); - m_SipResponse->m_FieldsOffset += lengthDifference; - } - - // copy status string - memcpy(m_SipResponse->m_Data+statusStringOffset, statusCodeString.c_str(), statusCodeString.length()); - - // change status code - std::ostringstream statusCodeAsString; - statusCodeAsString << StatusCodeEnumToInt[newStatusCode]; - memcpy(m_SipResponse->m_Data+8, statusCodeAsString.str().c_str(), 3); - - m_StatusCode = newStatusCode; - m_FirstLineEndOffset += lengthDifference; - - return true; - -} - -void SipResponseFirstLine::setVersion(const std::string& newVersion) -{ - if (newVersion == "") - return; - - if (newVersion.length() != m_Version.length()) - { - PCPP_LOG_ERROR("Expected version length is " << m_Version.length() << " characters in the format of SIP/x.y"); - return; - } - - char* verPos = (char*)m_SipResponse->m_Data; - memcpy(verPos, newVersion.c_str(), newVersion.length()); - m_Version = newVersion; -} - -SipResponseLayer::SipResponseStatusCode SipResponseFirstLine::parseStatusCode(const char* data, size_t dataLen) -{ - // minimum data should be 12B long: "SIP/x.y XXX " - if (!data || dataLen < 12) - { - return SipResponseLayer::SipStatusCodeUnknown; - } - - const char* statusCodeData = data + 8; - if (statusCodeData[3] != ' ') - { - return SipResponseLayer::SipStatusCodeUnknown; - } - - auto codeAsEnum = StatusCodeStringToEnumMap.find(std::string(statusCodeData, 3)); - if (codeAsEnum == StatusCodeStringToEnumMap.end()) - { - return SipResponseLayer::SipStatusCodeUnknown; - } - return codeAsEnum->second; -} - -SipResponseFirstLine::SipResponseFirstLine(SipResponseLayer* sipResponse) : m_SipResponse(sipResponse) -{ - m_Version = parseVersion((char*)m_SipResponse->m_Data, m_SipResponse->getDataLen()); - if (m_Version == "") - { - m_StatusCode = SipResponseLayer::SipStatusCodeUnknown; - } - else - { - m_StatusCode = parseStatusCode((char*)m_SipResponse->m_Data, m_SipResponse->getDataLen()); - } - - - char* endOfFirstLine; - if ((endOfFirstLine = (char *)memchr((char*)(m_SipResponse->m_Data), '\n', m_SipResponse->m_DataLen)) != nullptr) - { - m_FirstLineEndOffset = endOfFirstLine - (char*)m_SipResponse->m_Data + 1; - m_IsComplete = true; - } - else - { - m_FirstLineEndOffset = m_SipResponse->getDataLen(); - m_IsComplete = false; - } - - if (Logger::getInstance().isDebugEnabled(PacketLogModuleSipLayer)) - { - int statusCode = (m_StatusCode == SipResponseLayer::SipStatusCodeUnknown ? 0 : StatusCodeEnumToInt[m_StatusCode]); - PCPP_LOG_DEBUG("Version='" << m_Version << "'; Status code=" << statusCode << " '" << getStatusCodeString() << "'"); - } -} - - -SipResponseFirstLine::SipResponseFirstLine(SipResponseLayer* sipResponse, const std::string& version, SipResponseLayer::SipResponseStatusCode statusCode, std::string statusCodeString) -{ - if (statusCode == SipResponseLayer::SipStatusCodeUnknown) - { - m_Exception.setMessage("Status code supplied was SipStatusCodeUnknown"); - throw m_Exception; - } - - if (version == "") - { - m_Exception.setMessage("Version supplied was unknown"); - throw m_Exception; - } - - m_SipResponse = sipResponse; - - m_StatusCode = statusCode; - m_Version = version; - - std::ostringstream statusCodeAsString; - statusCodeAsString << StatusCodeEnumToInt[m_StatusCode]; - if (statusCodeString == "") - statusCodeString = StatusCodeEnumToString[m_StatusCode]; - std::string firstLine = m_Version + " " + statusCodeAsString.str() + " " + statusCodeString + "\r\n"; - - m_FirstLineEndOffset = firstLine.length(); - - m_SipResponse->m_DataLen = firstLine.length(); - m_SipResponse->m_Data = new uint8_t[m_SipResponse->m_DataLen]; - memcpy(m_SipResponse->m_Data, firstLine.c_str(), m_SipResponse->m_DataLen); - - m_IsComplete = true; -} - -std::string SipResponseFirstLine::parseVersion(const char* data, size_t dataLen) -{ - if (!data || dataLen < 8) // "SIP/x.y " - { - PCPP_LOG_DEBUG("SIP response length < 8, cannot identify version"); - return ""; - } - - if (data[0] != 'S' || data[1] != 'I' || data[2] != 'P' || data[3] != '/') - { - PCPP_LOG_DEBUG("SIP response does not begin with 'SIP/'"); - return ""; - } - - char* nextSpace = (char*)memchr(data, ' ', dataLen); - if (nextSpace == nullptr) - return ""; - - return std::string(data, nextSpace - data); -} - - -} +int SipResponseFirstLine::getStatusCodeAsInt() const { + return StatusCodeEnumToInt[m_StatusCode]; +} + +std::string SipResponseFirstLine::getStatusCodeString() const { + std::string result; + const int statusStringOffset = 12; + if (m_StatusCode != SipResponseLayer::SipStatusCodeUnknown) { + int statusStringEndOffset = m_FirstLineEndOffset - 2; + if ((*(m_SipResponse->m_Data + statusStringEndOffset)) != '\r') + statusStringEndOffset++; + result.assign((char*)(m_SipResponse->m_Data + statusStringOffset), + statusStringEndOffset - statusStringOffset); + } + + // else first line is illegal, return empty string + + return result; +} + +bool SipResponseFirstLine::setStatusCode( + SipResponseLayer::SipResponseStatusCode newStatusCode, + std::string statusCodeString) { + if (newStatusCode == SipResponseLayer::SipStatusCodeUnknown) { + PCPP_LOG_ERROR("Requested status code is SipStatusCodeUnknown"); + return false; + } + + // extend or shorten layer + + size_t statusStringOffset = 12; + if (statusCodeString == "") + statusCodeString = StatusCodeEnumToString[newStatusCode]; + int lengthDifference = + statusCodeString.length() - getStatusCodeString().length(); + + if (lengthDifference > 0) { + if (!m_SipResponse->extendLayer(statusStringOffset, lengthDifference)) { + PCPP_LOG_ERROR("Cannot change layer size"); + return false; + } + } else if (lengthDifference < 0) { + if (!m_SipResponse->shortenLayer(statusStringOffset, + 0 - lengthDifference)) { + PCPP_LOG_ERROR("Cannot change layer size"); + return false; + } + } + + if (lengthDifference != 0) { + m_SipResponse->shiftFieldsOffset(m_SipResponse->getFirstField(), + lengthDifference); + m_SipResponse->m_FieldsOffset += lengthDifference; + } + + // copy status string + memcpy(m_SipResponse->m_Data + statusStringOffset, statusCodeString.c_str(), + statusCodeString.length()); + + // change status code + std::ostringstream statusCodeAsString; + statusCodeAsString << StatusCodeEnumToInt[newStatusCode]; + memcpy(m_SipResponse->m_Data + 8, statusCodeAsString.str().c_str(), 3); + + m_StatusCode = newStatusCode; + m_FirstLineEndOffset += lengthDifference; + + return true; +} + +void SipResponseFirstLine::setVersion(const std::string& newVersion) { + if (newVersion == "") + return; + + if (newVersion.length() != m_Version.length()) { + PCPP_LOG_ERROR("Expected version length is " + << m_Version.length() + << " characters in the format of SIP/x.y"); + return; + } + + char* verPos = (char*)m_SipResponse->m_Data; + memcpy(verPos, newVersion.c_str(), newVersion.length()); + m_Version = newVersion; +} + +SipResponseLayer::SipResponseStatusCode +SipResponseFirstLine::parseStatusCode(const char* data, size_t dataLen) { + // minimum data should be 12B long: "SIP/x.y XXX " + if (!data || dataLen < 12) { + return SipResponseLayer::SipStatusCodeUnknown; + } + + const char* statusCodeData = data + 8; + if (statusCodeData[3] != ' ') { + return SipResponseLayer::SipStatusCodeUnknown; + } + + auto codeAsEnum = + StatusCodeStringToEnumMap.find(std::string(statusCodeData, 3)); + if (codeAsEnum == StatusCodeStringToEnumMap.end()) { + return SipResponseLayer::SipStatusCodeUnknown; + } + return codeAsEnum->second; +} + +SipResponseFirstLine::SipResponseFirstLine(SipResponseLayer* sipResponse) + : m_SipResponse(sipResponse) { + m_Version = + parseVersion((char*)m_SipResponse->m_Data, m_SipResponse->getDataLen()); + if (m_Version == "") { + m_StatusCode = SipResponseLayer::SipStatusCodeUnknown; + } else { + m_StatusCode = parseStatusCode((char*)m_SipResponse->m_Data, + m_SipResponse->getDataLen()); + } + + char* endOfFirstLine; + if ((endOfFirstLine = (char*)memchr((char*)(m_SipResponse->m_Data), '\n', + m_SipResponse->m_DataLen)) != nullptr) { + m_FirstLineEndOffset = endOfFirstLine - (char*)m_SipResponse->m_Data + 1; + m_IsComplete = true; + } else { + m_FirstLineEndOffset = m_SipResponse->getDataLen(); + m_IsComplete = false; + } + + if (Logger::getInstance().isDebugEnabled(PacketLogModuleSipLayer)) { + int statusCode = (m_StatusCode == SipResponseLayer::SipStatusCodeUnknown + ? 0 + : StatusCodeEnumToInt[m_StatusCode]); + PCPP_LOG_DEBUG("Version='" << m_Version << "'; Status code=" << statusCode + << " '" << getStatusCodeString() << "'"); + } +} + +SipResponseFirstLine::SipResponseFirstLine( + SipResponseLayer* sipResponse, const std::string& version, + SipResponseLayer::SipResponseStatusCode statusCode, + std::string statusCodeString) { + if (statusCode == SipResponseLayer::SipStatusCodeUnknown) { + m_Exception.setMessage("Status code supplied was SipStatusCodeUnknown"); + throw m_Exception; + } + + if (version == "") { + m_Exception.setMessage("Version supplied was unknown"); + throw m_Exception; + } + + m_SipResponse = sipResponse; + + m_StatusCode = statusCode; + m_Version = version; + + std::ostringstream statusCodeAsString; + statusCodeAsString << StatusCodeEnumToInt[m_StatusCode]; + if (statusCodeString == "") + statusCodeString = StatusCodeEnumToString[m_StatusCode]; + std::string firstLine = m_Version + " " + statusCodeAsString.str() + " " + + statusCodeString + "\r\n"; + + m_FirstLineEndOffset = firstLine.length(); + + m_SipResponse->m_DataLen = firstLine.length(); + m_SipResponse->m_Data = new uint8_t[m_SipResponse->m_DataLen]; + memcpy(m_SipResponse->m_Data, firstLine.c_str(), m_SipResponse->m_DataLen); + + m_IsComplete = true; +} + +std::string SipResponseFirstLine::parseVersion(const char* data, + size_t dataLen) { + if (!data || dataLen < 8) // "SIP/x.y " + { + PCPP_LOG_DEBUG("SIP response length < 8, cannot identify version"); + return ""; + } + + if (data[0] != 'S' || data[1] != 'I' || data[2] != 'P' || data[3] != '/') { + PCPP_LOG_DEBUG("SIP response does not begin with 'SIP/'"); + return ""; + } + + char* nextSpace = (char*)memchr(data, ' ', dataLen); + if (nextSpace == nullptr) + return ""; + + return std::string(data, nextSpace - data); +} + +} // namespace pcpp diff --git a/Packet++/src/Sll2Layer.cpp b/Packet++/src/Sll2Layer.cpp index fe6e479957..4864dc5518 100644 --- a/Packet++/src/Sll2Layer.cpp +++ b/Packet++/src/Sll2Layer.cpp @@ -1,196 +1,183 @@ #define LOG_MODULE PacketLogModuleSll2Layer #include "Sll2Layer.h" -#include "Logger.h" +#include "ArpLayer.h" +#include "EndianPortable.h" #include "IPv4Layer.h" #include "IPv6Layer.h" +#include "Logger.h" +#include "MplsLayer.h" +#include "PPPoELayer.h" #include "PayloadLayer.h" -#include "ArpLayer.h" #include "VlanLayer.h" -#include "PPPoELayer.h" -#include "MplsLayer.h" #include -#include "EndianPortable.h" -namespace pcpp -{ -Sll2Layer::Sll2Layer(uint32_t interfaceIndex, uint16_t ARPHRDType, uint8_t packetType) -{ - const size_t headerLen = sizeof(sll2_header); - m_DataLen = headerLen; - m_Data = new uint8_t[headerLen]; - memset(m_Data, 0, headerLen); - setPacketType(packetType); - setArphrdType(ARPHRDType); - setInterfaceIndex(interfaceIndex); - m_Protocol = SLL2; -} - -bool Sll2Layer::setLinkLayerAddr(const uint8_t* addr, size_t addrLength) -{ - if (addr == nullptr || addrLength == 0 || addrLength > 8) - { - PCPP_LOG_ERROR("Address length is out of bounds, it must be between 1 and 8"); - return false; - } - - getSll2Header()->link_layer_addr_len = addrLength; - memcpy(getSll2Header()->link_layer_addr, addr, addrLength); - return true; +namespace pcpp { +Sll2Layer::Sll2Layer(uint32_t interfaceIndex, uint16_t ARPHRDType, + uint8_t packetType) { + const size_t headerLen = sizeof(sll2_header); + m_DataLen = headerLen; + m_Data = new uint8_t[headerLen]; + memset(m_Data, 0, headerLen); + setPacketType(packetType); + setArphrdType(ARPHRDType); + setInterfaceIndex(interfaceIndex); + m_Protocol = SLL2; } -MacAddress Sll2Layer::getLinkLayerAsMacAddress() { - const uint8_t* data = getLinkLayerAddr(); - uint8_t dataLen = getLinkLayerAddrLen(); - if (data == nullptr || dataLen == 0 || dataLen > 8) { - return MacAddress::Zero; - } - return MacAddress(data); -} - -bool Sll2Layer::setMacAddressAsLinkLayer(const MacAddress& macAddr) -{ - if (!macAddr.isValid()) - { - PCPP_LOG_ERROR("MAC address is not valid"); - return false; - } - - uint8_t macAddrAsArr[6]; - macAddr.copyTo(macAddrAsArr); - return setLinkLayerAddr(macAddrAsArr, 6); -} - -void Sll2Layer::parseNextLayer() -{ - if (m_DataLen <= sizeof(sll2_header)) - return; - - uint8_t* payload = m_Data + sizeof(sll2_header); - size_t payloadLen = m_DataLen - sizeof(sll2_header); - - sll2_header* hdr = getSll2Header(); - switch (be16toh(hdr->protocol_type)) - { - case PCPP_ETHERTYPE_IP: - m_NextLayer = IPv4Layer::isDataValid(payload, payloadLen) - ? static_cast(new IPv4Layer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case PCPP_ETHERTYPE_IPV6: - m_NextLayer = IPv6Layer::isDataValid(payload, payloadLen) - ? static_cast(new IPv6Layer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case PCPP_ETHERTYPE_ARP: - m_NextLayer = new ArpLayer(payload, payloadLen, this, m_Packet); - break; - case PCPP_ETHERTYPE_VLAN: - case PCPP_ETHERTYPE_IEEE_802_1AD: - m_NextLayer = new VlanLayer(payload, payloadLen, this, m_Packet); - break; - case PCPP_ETHERTYPE_PPPOES: - m_NextLayer = PPPoESessionLayer::isDataValid(payload, payloadLen) - ? static_cast(new PPPoESessionLayer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case PCPP_ETHERTYPE_PPPOED: - m_NextLayer = PPPoEDiscoveryLayer::isDataValid(payload, payloadLen) - ? static_cast(new PPPoEDiscoveryLayer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case PCPP_ETHERTYPE_MPLS: - m_NextLayer = new MplsLayer(payload, payloadLen, this, m_Packet); - break; - default: - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - } - -} - -void Sll2Layer::computeCalculateFields() -{ - if (m_NextLayer == nullptr) - return; - - sll2_header* hdr = getSll2Header(); - switch (m_NextLayer->getProtocol()) - { - case IPv4: - hdr->protocol_type = htobe16(PCPP_ETHERTYPE_IP); - break; - case IPv6: - hdr->protocol_type = htobe16(PCPP_ETHERTYPE_IPV6); - break; - case ARP: - hdr->protocol_type = htobe16(PCPP_ETHERTYPE_ARP); - break; - case VLAN: - hdr->protocol_type = htobe16(PCPP_ETHERTYPE_VLAN); - break; - default: - return; - } -} - -bool Sll2Layer::isDataValid(const uint8_t* data, size_t dataLen) -{ - return data && dataLen >= sizeof(sll2_header); -} - -std::string Sll2Layer::toString() const -{ - return "Linux cooked header v2"; -} - -uint16_t Sll2Layer::getProtocolType() const -{ - return be16toh(getSll2Header()->protocol_type); -} - -void Sll2Layer::setProtocolType(uint16_t protocolType) -{ - getSll2Header()->protocol_type = htobe16(protocolType); -} - -uint32_t Sll2Layer::getInterfaceIndex() const -{ - return be32toh(getSll2Header()->interface_index); -} - -void Sll2Layer::setInterfaceIndex(uint32_t interfaceIndex) -{ - getSll2Header()->interface_index = htobe32(interfaceIndex); -} - -uint16_t Sll2Layer::getArphrdType() const -{ - return be16toh(getSll2Header()->ARPHRD_type); -} - -void Sll2Layer::setArphrdType(uint16_t arphrdType) -{ - getSll2Header()->ARPHRD_type = htobe16(arphrdType); -} - -uint8_t Sll2Layer::getPacketType() const -{ - return getSll2Header()->packet_type; -} +bool Sll2Layer::setLinkLayerAddr(const uint8_t* addr, size_t addrLength) { + if (addr == nullptr || addrLength == 0 || addrLength > 8) { + PCPP_LOG_ERROR( + "Address length is out of bounds, it must be between 1 and 8"); + return false; + } -void Sll2Layer::setPacketType(uint8_t packetType) -{ - getSll2Header()->packet_type = packetType; + getSll2Header()->link_layer_addr_len = addrLength; + memcpy(getSll2Header()->link_layer_addr, addr, addrLength); + return true; +} + +MacAddress Sll2Layer::getLinkLayerAsMacAddress() { + const uint8_t* data = getLinkLayerAddr(); + uint8_t dataLen = getLinkLayerAddrLen(); + if (data == nullptr || dataLen == 0 || dataLen > 8) { + return MacAddress::Zero; + } + return MacAddress(data); +} + +bool Sll2Layer::setMacAddressAsLinkLayer(const MacAddress& macAddr) { + if (!macAddr.isValid()) { + PCPP_LOG_ERROR("MAC address is not valid"); + return false; + } + + uint8_t macAddrAsArr[6]; + macAddr.copyTo(macAddrAsArr); + return setLinkLayerAddr(macAddrAsArr, 6); +} + +void Sll2Layer::parseNextLayer() { + if (m_DataLen <= sizeof(sll2_header)) + return; + + uint8_t* payload = m_Data + sizeof(sll2_header); + size_t payloadLen = m_DataLen - sizeof(sll2_header); + + sll2_header* hdr = getSll2Header(); + switch (be16toh(hdr->protocol_type)) { + case PCPP_ETHERTYPE_IP: + m_NextLayer = IPv4Layer::isDataValid(payload, payloadLen) + ? static_cast( + new IPv4Layer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer( + payload, payloadLen, this, m_Packet)); + break; + case PCPP_ETHERTYPE_IPV6: + m_NextLayer = IPv6Layer::isDataValid(payload, payloadLen) + ? static_cast( + new IPv6Layer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer( + payload, payloadLen, this, m_Packet)); + break; + case PCPP_ETHERTYPE_ARP: + m_NextLayer = new ArpLayer(payload, payloadLen, this, m_Packet); + break; + case PCPP_ETHERTYPE_VLAN: + case PCPP_ETHERTYPE_IEEE_802_1AD: + m_NextLayer = new VlanLayer(payload, payloadLen, this, m_Packet); + break; + case PCPP_ETHERTYPE_PPPOES: + m_NextLayer = + PPPoESessionLayer::isDataValid(payload, payloadLen) + ? static_cast( + new PPPoESessionLayer(payload, payloadLen, this, m_Packet)) + : static_cast( + new PayloadLayer(payload, payloadLen, this, m_Packet)); + break; + case PCPP_ETHERTYPE_PPPOED: + m_NextLayer = + PPPoEDiscoveryLayer::isDataValid(payload, payloadLen) + ? static_cast( + new PPPoEDiscoveryLayer(payload, payloadLen, this, m_Packet)) + : static_cast( + new PayloadLayer(payload, payloadLen, this, m_Packet)); + break; + case PCPP_ETHERTYPE_MPLS: + m_NextLayer = new MplsLayer(payload, payloadLen, this, m_Packet); + break; + default: + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + } +} + +void Sll2Layer::computeCalculateFields() { + if (m_NextLayer == nullptr) + return; + + sll2_header* hdr = getSll2Header(); + switch (m_NextLayer->getProtocol()) { + case IPv4: + hdr->protocol_type = htobe16(PCPP_ETHERTYPE_IP); + break; + case IPv6: + hdr->protocol_type = htobe16(PCPP_ETHERTYPE_IPV6); + break; + case ARP: + hdr->protocol_type = htobe16(PCPP_ETHERTYPE_ARP); + break; + case VLAN: + hdr->protocol_type = htobe16(PCPP_ETHERTYPE_VLAN); + break; + default: + return; + } +} + +bool Sll2Layer::isDataValid(const uint8_t* data, size_t dataLen) { + return data && dataLen >= sizeof(sll2_header); +} + +std::string Sll2Layer::toString() const { return "Linux cooked header v2"; } + +uint16_t Sll2Layer::getProtocolType() const { + return be16toh(getSll2Header()->protocol_type); +} + +void Sll2Layer::setProtocolType(uint16_t protocolType) { + getSll2Header()->protocol_type = htobe16(protocolType); +} + +uint32_t Sll2Layer::getInterfaceIndex() const { + return be32toh(getSll2Header()->interface_index); +} + +void Sll2Layer::setInterfaceIndex(uint32_t interfaceIndex) { + getSll2Header()->interface_index = htobe32(interfaceIndex); +} + +uint16_t Sll2Layer::getArphrdType() const { + return be16toh(getSll2Header()->ARPHRD_type); +} + +void Sll2Layer::setArphrdType(uint16_t arphrdType) { + getSll2Header()->ARPHRD_type = htobe16(arphrdType); +} + +uint8_t Sll2Layer::getPacketType() const { + return getSll2Header()->packet_type; +} + +void Sll2Layer::setPacketType(uint8_t packetType) { + getSll2Header()->packet_type = packetType; } -uint8_t Sll2Layer::getLinkLayerAddrLen() const -{ - return getSll2Header()->link_layer_addr_len; +uint8_t Sll2Layer::getLinkLayerAddrLen() const { + return getSll2Header()->link_layer_addr_len; } -const uint8_t *Sll2Layer::getLinkLayerAddr() const -{ - return getSll2Header()->link_layer_addr; +const uint8_t* Sll2Layer::getLinkLayerAddr() const { + return getSll2Header()->link_layer_addr; } } // namespace pcpp diff --git a/Packet++/src/SllLayer.cpp b/Packet++/src/SllLayer.cpp index 21edcc3c4b..7fb1658c57 100644 --- a/Packet++/src/SllLayer.cpp +++ b/Packet++/src/SllLayer.cpp @@ -1,135 +1,132 @@ #define LOG_MODULE PacketLogModuleSllLayer #include "SllLayer.h" -#include "Logger.h" +#include "ArpLayer.h" +#include "EndianPortable.h" #include "IPv4Layer.h" #include "IPv6Layer.h" +#include "Logger.h" +#include "MplsLayer.h" +#include "PPPoELayer.h" #include "PayloadLayer.h" -#include "ArpLayer.h" #include "VlanLayer.h" -#include "PPPoELayer.h" -#include "MplsLayer.h" #include -#include "EndianPortable.h" -namespace pcpp -{ - -SllLayer::SllLayer(uint16_t packetType, uint16_t ARPHRDType) -{ - const size_t headerLen = sizeof(sll_header); - m_DataLen = headerLen; - m_Data = new uint8_t[headerLen]; - memset(m_Data, 0, headerLen); - sll_header* sllHdr = (sll_header*)m_Data; - sllHdr->packet_type = htobe16(packetType); - sllHdr->ARPHRD_type = htobe16(ARPHRDType); - m_Protocol = SLL; +namespace pcpp { + +SllLayer::SllLayer(uint16_t packetType, uint16_t ARPHRDType) { + const size_t headerLen = sizeof(sll_header); + m_DataLen = headerLen; + m_Data = new uint8_t[headerLen]; + memset(m_Data, 0, headerLen); + sll_header* sllHdr = (sll_header*)m_Data; + sllHdr->packet_type = htobe16(packetType); + sllHdr->ARPHRD_type = htobe16(ARPHRDType); + m_Protocol = SLL; } -bool SllLayer::setLinkLayerAddr(uint8_t* addr, size_t addrLength) -{ - if (addr == nullptr || addrLength == 0 || addrLength > 8) - { - PCPP_LOG_ERROR("Address length is out of bounds, it must be between 1 and 8"); - return false; - } +bool SllLayer::setLinkLayerAddr(uint8_t* addr, size_t addrLength) { + if (addr == nullptr || addrLength == 0 || addrLength > 8) { + PCPP_LOG_ERROR( + "Address length is out of bounds, it must be between 1 and 8"); + return false; + } - sll_header* sllHdr = getSllHeader(); - memcpy(sllHdr->link_layer_addr, addr, addrLength); - sllHdr->link_layer_addr_len = htobe16(addrLength); + sll_header* sllHdr = getSllHeader(); + memcpy(sllHdr->link_layer_addr, addr, addrLength); + sllHdr->link_layer_addr_len = htobe16(addrLength); - return true; + return true; } -bool SllLayer::setMacAddressAsLinkLayer(MacAddress const& macAddr) -{ - if (!macAddr.isValid()) - { - PCPP_LOG_ERROR("MAC address is not valid"); - return false; - } +bool SllLayer::setMacAddressAsLinkLayer(MacAddress const& macAddr) { + if (!macAddr.isValid()) { + PCPP_LOG_ERROR("MAC address is not valid"); + return false; + } - uint8_t macAddrAsArr[6]; - macAddr.copyTo(macAddrAsArr); - return setLinkLayerAddr(macAddrAsArr, 6); + uint8_t macAddrAsArr[6]; + macAddr.copyTo(macAddrAsArr); + return setLinkLayerAddr(macAddrAsArr, 6); } -void SllLayer::parseNextLayer() -{ - if (m_DataLen <= sizeof(sll_header)) - return; - - uint8_t* payload = m_Data + sizeof(sll_header); - size_t payloadLen = m_DataLen - sizeof(sll_header); - - sll_header* hdr = getSllHeader(); - switch (be16toh(hdr->protocol_type)) - { - case PCPP_ETHERTYPE_IP: - m_NextLayer = IPv4Layer::isDataValid(payload, payloadLen) - ? static_cast(new IPv4Layer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case PCPP_ETHERTYPE_IPV6: - m_NextLayer = IPv6Layer::isDataValid(payload, payloadLen) - ? static_cast(new IPv6Layer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case PCPP_ETHERTYPE_ARP: - m_NextLayer = new ArpLayer(payload, payloadLen, this, m_Packet); - break; - case PCPP_ETHERTYPE_VLAN: - case PCPP_ETHERTYPE_IEEE_802_1AD: - m_NextLayer = new VlanLayer(payload, payloadLen, this, m_Packet); - break; - case PCPP_ETHERTYPE_PPPOES: - m_NextLayer = PPPoESessionLayer::isDataValid(payload, payloadLen) - ? static_cast(new PPPoESessionLayer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case PCPP_ETHERTYPE_PPPOED: - m_NextLayer = PPPoEDiscoveryLayer::isDataValid(payload, payloadLen) - ? static_cast(new PPPoEDiscoveryLayer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case PCPP_ETHERTYPE_MPLS: - m_NextLayer = new MplsLayer(payload, payloadLen, this, m_Packet); - break; - default: - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - } - +void SllLayer::parseNextLayer() { + if (m_DataLen <= sizeof(sll_header)) + return; + + uint8_t* payload = m_Data + sizeof(sll_header); + size_t payloadLen = m_DataLen - sizeof(sll_header); + + sll_header* hdr = getSllHeader(); + switch (be16toh(hdr->protocol_type)) { + case PCPP_ETHERTYPE_IP: + m_NextLayer = IPv4Layer::isDataValid(payload, payloadLen) + ? static_cast( + new IPv4Layer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer( + payload, payloadLen, this, m_Packet)); + break; + case PCPP_ETHERTYPE_IPV6: + m_NextLayer = IPv6Layer::isDataValid(payload, payloadLen) + ? static_cast( + new IPv6Layer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer( + payload, payloadLen, this, m_Packet)); + break; + case PCPP_ETHERTYPE_ARP: + m_NextLayer = new ArpLayer(payload, payloadLen, this, m_Packet); + break; + case PCPP_ETHERTYPE_VLAN: + case PCPP_ETHERTYPE_IEEE_802_1AD: + m_NextLayer = new VlanLayer(payload, payloadLen, this, m_Packet); + break; + case PCPP_ETHERTYPE_PPPOES: + m_NextLayer = + PPPoESessionLayer::isDataValid(payload, payloadLen) + ? static_cast( + new PPPoESessionLayer(payload, payloadLen, this, m_Packet)) + : static_cast( + new PayloadLayer(payload, payloadLen, this, m_Packet)); + break; + case PCPP_ETHERTYPE_PPPOED: + m_NextLayer = + PPPoEDiscoveryLayer::isDataValid(payload, payloadLen) + ? static_cast( + new PPPoEDiscoveryLayer(payload, payloadLen, this, m_Packet)) + : static_cast( + new PayloadLayer(payload, payloadLen, this, m_Packet)); + break; + case PCPP_ETHERTYPE_MPLS: + m_NextLayer = new MplsLayer(payload, payloadLen, this, m_Packet); + break; + default: + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + } } -void SllLayer::computeCalculateFields() -{ - if (m_NextLayer == nullptr) - return; - - sll_header* hdr = getSllHeader(); - switch (m_NextLayer->getProtocol()) - { - case IPv4: - hdr->protocol_type = htobe16(PCPP_ETHERTYPE_IP); - break; - case IPv6: - hdr->protocol_type = htobe16(PCPP_ETHERTYPE_IPV6); - break; - case ARP: - hdr->protocol_type = htobe16(PCPP_ETHERTYPE_ARP); - break; - case VLAN: - hdr->protocol_type = htobe16(PCPP_ETHERTYPE_VLAN); - break; - default: - return; - } +void SllLayer::computeCalculateFields() { + if (m_NextLayer == nullptr) + return; + + sll_header* hdr = getSllHeader(); + switch (m_NextLayer->getProtocol()) { + case IPv4: + hdr->protocol_type = htobe16(PCPP_ETHERTYPE_IP); + break; + case IPv6: + hdr->protocol_type = htobe16(PCPP_ETHERTYPE_IPV6); + break; + case ARP: + hdr->protocol_type = htobe16(PCPP_ETHERTYPE_ARP); + break; + case VLAN: + hdr->protocol_type = htobe16(PCPP_ETHERTYPE_VLAN); + break; + default: + return; + } } -std::string SllLayer::toString() const -{ - return "Linux cooked header"; -} +std::string SllLayer::toString() const { return "Linux cooked header"; } } // namespace pcpp diff --git a/Packet++/src/SomeIpLayer.cpp b/Packet++/src/SomeIpLayer.cpp index f97c006a44..a020b44f80 100644 --- a/Packet++/src/SomeIpLayer.cpp +++ b/Packet++/src/SomeIpLayer.cpp @@ -1,366 +1,317 @@ #define LOG_MODULE PacketLogModuleSomeIpLayer #include "SomeIpLayer.h" -#include "SomeIpSdLayer.h" +#include "EndianPortable.h" #include "Packet.h" #include "PayloadLayer.h" -#include "EndianPortable.h" +#include "SomeIpSdLayer.h" #include #include #include -namespace pcpp -{ +namespace pcpp { // SomeIpLayer -void splitUint32Id(uint32_t uint32Id, uint16_t &uint16IdUpper, uint16_t &uint16IdLower) -{ - uint16IdLower = (uint32Id & 0x0000ffff); - uint16IdUpper = (uint32Id & 0xffff0000) >> 16; +void splitUint32Id(uint32_t uint32Id, uint16_t& uint16IdUpper, + uint16_t& uint16IdLower) { + uint16IdLower = (uint32Id & 0x0000ffff); + uint16IdUpper = (uint32Id & 0xffff0000) >> 16; } std::unordered_set SomeIpLayer::m_SomeIpPorts{}; -SomeIpLayer::SomeIpLayer(uint16_t serviceID, uint16_t methodID, uint16_t clientID, uint16_t sessionID, - uint8_t interfaceVersion, MsgType type, uint8_t returnCode, const uint8_t *const data, - size_t dataLen) -{ - const size_t headerLen = sizeof(someiphdr); - m_DataLen = headerLen + dataLen; - m_Data = new uint8_t[m_DataLen]; - m_Protocol = SomeIP; - memset(m_Data, 0, headerLen); - memcpy(m_Data + headerLen, data, dataLen); - - setServiceID(serviceID); - setMethodID(methodID); - setPayloadLength((uint32_t)dataLen); - setClientID(clientID); - setSessionID(sessionID); - setProtocolVersion(0x01); - setInterfaceVersion(interfaceVersion); - setMessageType(type); - setReturnCode(returnCode); -} +SomeIpLayer::SomeIpLayer(uint16_t serviceID, uint16_t methodID, + uint16_t clientID, uint16_t sessionID, + uint8_t interfaceVersion, MsgType type, + uint8_t returnCode, const uint8_t* const data, + size_t dataLen) { + const size_t headerLen = sizeof(someiphdr); + m_DataLen = headerLen + dataLen; + m_Data = new uint8_t[m_DataLen]; + m_Protocol = SomeIP; + memset(m_Data, 0, headerLen); + memcpy(m_Data + headerLen, data, dataLen); -Layer* SomeIpLayer::parseSomeIpLayer(uint8_t *data, size_t dataLen, Layer* prevLayer, Packet* packet) -{ - /* Ideas taken from wireshark some ip dissector */ - const size_t headerLen = sizeof(someiphdr); - if (dataLen < headerLen) - return new PayloadLayer(data, dataLen, prevLayer, packet); + setServiceID(serviceID); + setMethodID(methodID); + setPayloadLength((uint32_t)dataLen); + setClientID(clientID); + setSessionID(sessionID); + setProtocolVersion(0x01); + setInterfaceVersion(interfaceVersion); + setMessageType(type); + setReturnCode(returnCode); +} - uint32_t lengthBE = 0; - memcpy(&lengthBE, data + sizeof(uint32_t), sizeof(uint32_t)); // length field in SOME/IP header - uint32_t length = be32toh(lengthBE); - if ((length < 8) || (length > dataLen - 8)) - return new PayloadLayer(data, dataLen, prevLayer, packet); +Layer* SomeIpLayer::parseSomeIpLayer(uint8_t* data, size_t dataLen, + Layer* prevLayer, Packet* packet) { + /* Ideas taken from wireshark some ip dissector */ + const size_t headerLen = sizeof(someiphdr); + if (dataLen < headerLen) + return new PayloadLayer(data, dataLen, prevLayer, packet); - if (data[12] != SOMEIP_PROTOCOL_VERSION) - return new PayloadLayer(data, dataLen, prevLayer, packet); + uint32_t lengthBE = 0; + memcpy(&lengthBE, data + sizeof(uint32_t), + sizeof(uint32_t)); // length field in SOME/IP header + uint32_t length = be32toh(lengthBE); + if ((length < 8) || (length > dataLen - 8)) + return new PayloadLayer(data, dataLen, prevLayer, packet); - someiphdr *hdr = (someiphdr *)data; + if (data[12] != SOMEIP_PROTOCOL_VERSION) + return new PayloadLayer(data, dataLen, prevLayer, packet); - switch (static_cast(hdr->msgType & ~(uint8_t)MsgType::TP_REQUEST)) - { - case MsgType::REQUEST: - case MsgType::REQUEST_ACK: - case MsgType::REQUEST_NO_RETURN: - case MsgType::REQUEST_NO_RETURN_ACK: - case MsgType::NOTIFICATION: - case MsgType::NOTIFICATION_ACK: - case MsgType::RESPONSE: - case MsgType::RESPONSE_ACK: - case MsgType::ERRORS: - case MsgType::ERROR_ACK: - break; - default: - return new PayloadLayer(data, dataLen, prevLayer, packet); - } + someiphdr* hdr = (someiphdr*)data; - if (be16toh(hdr->serviceID) == 0xFFFF && be16toh(hdr->methodID) == 0x8100 && SomeIpSdLayer::isDataValid(data, dataLen)) - { - return new SomeIpSdLayer(data, dataLen, prevLayer, packet); - } - else if ((hdr->msgType & (uint8_t)SomeIpLayer::MsgType::TP_REQUEST) != 0) - { - return new SomeIpTpLayer(data, dataLen, prevLayer, packet); - } - else - { - return new SomeIpLayer(data, dataLen, prevLayer, packet); - } -} + switch (static_cast(hdr->msgType & ~(uint8_t)MsgType::TP_REQUEST)) { + case MsgType::REQUEST: + case MsgType::REQUEST_ACK: + case MsgType::REQUEST_NO_RETURN: + case MsgType::REQUEST_NO_RETURN_ACK: + case MsgType::NOTIFICATION: + case MsgType::NOTIFICATION_ACK: + case MsgType::RESPONSE: + case MsgType::RESPONSE_ACK: + case MsgType::ERRORS: + case MsgType::ERROR_ACK: + break; + default: + return new PayloadLayer(data, dataLen, prevLayer, packet); + } -bool SomeIpLayer::isSomeIpPort(uint16_t port) -{ - return SomeIpSdLayer::isSomeIpSdPort(port) || - std::any_of(m_SomeIpPorts.begin(), m_SomeIpPorts.end(), - [&](const uint16_t &someIpPort) { return someIpPort == port; }); + if (be16toh(hdr->serviceID) == 0xFFFF && be16toh(hdr->methodID) == 0x8100 && + SomeIpSdLayer::isDataValid(data, dataLen)) { + return new SomeIpSdLayer(data, dataLen, prevLayer, packet); + } else if ((hdr->msgType & (uint8_t)SomeIpLayer::MsgType::TP_REQUEST) != 0) { + return new SomeIpTpLayer(data, dataLen, prevLayer, packet); + } else { + return new SomeIpLayer(data, dataLen, prevLayer, packet); + } } -void SomeIpLayer::addSomeIpPort(uint16_t port) -{ - m_SomeIpPorts.insert(port); +bool SomeIpLayer::isSomeIpPort(uint16_t port) { + return SomeIpSdLayer::isSomeIpSdPort(port) || + std::any_of( + m_SomeIpPorts.begin(), m_SomeIpPorts.end(), + [&](const uint16_t& someIpPort) { return someIpPort == port; }); } -void SomeIpLayer::removeSomeIpPort(uint16_t port) -{ - m_SomeIpPorts.erase(port); -} +void SomeIpLayer::addSomeIpPort(uint16_t port) { m_SomeIpPorts.insert(port); } -void SomeIpLayer::removeAllSomeIpPorts() -{ - m_SomeIpPorts.clear(); -} +void SomeIpLayer::removeSomeIpPort(uint16_t port) { m_SomeIpPorts.erase(port); } + +void SomeIpLayer::removeAllSomeIpPorts() { m_SomeIpPorts.clear(); } -uint32_t SomeIpLayer::getMessageID() const -{ - someiphdr *hdr = getSomeIpHeader(); +uint32_t SomeIpLayer::getMessageID() const { + someiphdr* hdr = getSomeIpHeader(); - return ((uint32_t)be16toh(hdr->serviceID) << 16) + be16toh(hdr->methodID); + return ((uint32_t)be16toh(hdr->serviceID) << 16) + be16toh(hdr->methodID); } -void SomeIpLayer::setMessageID(uint32_t messageID) -{ - uint16_t methodID; - uint16_t serviceID; +void SomeIpLayer::setMessageID(uint32_t messageID) { + uint16_t methodID; + uint16_t serviceID; - splitUint32Id(messageID, serviceID, methodID); + splitUint32Id(messageID, serviceID, methodID); - someiphdr *hdr = getSomeIpHeader(); - hdr->serviceID = htobe16(serviceID); - hdr->methodID = htobe16(methodID); + someiphdr* hdr = getSomeIpHeader(); + hdr->serviceID = htobe16(serviceID); + hdr->methodID = htobe16(methodID); } -uint16_t SomeIpLayer::getServiceID() const -{ - return be16toh(getSomeIpHeader()->serviceID); +uint16_t SomeIpLayer::getServiceID() const { + return be16toh(getSomeIpHeader()->serviceID); } -void SomeIpLayer::setServiceID(uint16_t serviceID) -{ - getSomeIpHeader()->serviceID = htobe16(serviceID); +void SomeIpLayer::setServiceID(uint16_t serviceID) { + getSomeIpHeader()->serviceID = htobe16(serviceID); } -uint16_t SomeIpLayer::getMethodID() const -{ - return be16toh(getSomeIpHeader()->methodID); +uint16_t SomeIpLayer::getMethodID() const { + return be16toh(getSomeIpHeader()->methodID); } -void SomeIpLayer::setMethodID(uint16_t methodID) -{ - getSomeIpHeader()->methodID = htobe16(methodID); +void SomeIpLayer::setMethodID(uint16_t methodID) { + getSomeIpHeader()->methodID = htobe16(methodID); } -uint32_t SomeIpLayer::getLengthField() const -{ - return be32toh(getSomeIpHeader()->length); +uint32_t SomeIpLayer::getLengthField() const { + return be32toh(getSomeIpHeader()->length); } -uint32_t SomeIpLayer::getRequestID() const -{ - someiphdr *hdr = getSomeIpHeader(); +uint32_t SomeIpLayer::getRequestID() const { + someiphdr* hdr = getSomeIpHeader(); - return ((uint32_t)be16toh(hdr->clientID) << 16) + be16toh(hdr->sessionID); + return ((uint32_t)be16toh(hdr->clientID) << 16) + be16toh(hdr->sessionID); } -void SomeIpLayer::setRequestID(uint32_t requestID) -{ - uint16_t clientID; - uint16_t sessionID; +void SomeIpLayer::setRequestID(uint32_t requestID) { + uint16_t clientID; + uint16_t sessionID; - splitUint32Id(requestID, clientID, sessionID); + splitUint32Id(requestID, clientID, sessionID); - someiphdr *hdr = getSomeIpHeader(); - hdr->clientID = htobe16(clientID); - hdr->sessionID = htobe16(sessionID); + someiphdr* hdr = getSomeIpHeader(); + hdr->clientID = htobe16(clientID); + hdr->sessionID = htobe16(sessionID); } -uint16_t SomeIpLayer::getClientID() const -{ - return be16toh(getSomeIpHeader()->clientID); +uint16_t SomeIpLayer::getClientID() const { + return be16toh(getSomeIpHeader()->clientID); } -void SomeIpLayer::setClientID(uint16_t clientID) -{ - getSomeIpHeader()->clientID = htobe16(clientID); +void SomeIpLayer::setClientID(uint16_t clientID) { + getSomeIpHeader()->clientID = htobe16(clientID); } -uint16_t SomeIpLayer::getSessionID() const -{ - return be16toh(getSomeIpHeader()->sessionID); +uint16_t SomeIpLayer::getSessionID() const { + return be16toh(getSomeIpHeader()->sessionID); } -void SomeIpLayer::setSessionID(uint16_t sessionID) -{ - getSomeIpHeader()->sessionID = htobe16(sessionID); +void SomeIpLayer::setSessionID(uint16_t sessionID) { + getSomeIpHeader()->sessionID = htobe16(sessionID); } -uint8_t SomeIpLayer::getProtocolVersion() const -{ - return getSomeIpHeader()->protocolVersion; +uint8_t SomeIpLayer::getProtocolVersion() const { + return getSomeIpHeader()->protocolVersion; } -void SomeIpLayer::setProtocolVersion(uint8_t version) -{ - getSomeIpHeader()->protocolVersion = version; +void SomeIpLayer::setProtocolVersion(uint8_t version) { + getSomeIpHeader()->protocolVersion = version; } -uint8_t SomeIpLayer::getInterfaceVersion() const -{ - return getSomeIpHeader()->interfaceVersion; +uint8_t SomeIpLayer::getInterfaceVersion() const { + return getSomeIpHeader()->interfaceVersion; } -void SomeIpLayer::setInterfaceVersion(uint8_t version) -{ - getSomeIpHeader()->interfaceVersion = version; +void SomeIpLayer::setInterfaceVersion(uint8_t version) { + getSomeIpHeader()->interfaceVersion = version; } -SomeIpLayer::MsgType SomeIpLayer::getMessageType() const -{ - return static_cast(getSomeIpHeader()->msgType); +SomeIpLayer::MsgType SomeIpLayer::getMessageType() const { + return static_cast(getSomeIpHeader()->msgType); } -uint8_t SomeIpLayer::getMessageTypeAsInt() const -{ - return getSomeIpHeader()->msgType; +uint8_t SomeIpLayer::getMessageTypeAsInt() const { + return getSomeIpHeader()->msgType; } -void SomeIpLayer::setMessageType(MsgType type) -{ - setMessageType(static_cast(type)); +void SomeIpLayer::setMessageType(MsgType type) { + setMessageType(static_cast(type)); } -void SomeIpLayer::setMessageType(uint8_t type) -{ - getSomeIpHeader()->msgType = type; +void SomeIpLayer::setMessageType(uint8_t type) { + getSomeIpHeader()->msgType = type; } -uint8_t SomeIpLayer::getReturnCode() const -{ - return getSomeIpHeader()->returnCode; +uint8_t SomeIpLayer::getReturnCode() const { + return getSomeIpHeader()->returnCode; } -void SomeIpLayer::setReturnCode(uint8_t returnCode) -{ - getSomeIpHeader()->returnCode = returnCode; +void SomeIpLayer::setReturnCode(uint8_t returnCode) { + getSomeIpHeader()->returnCode = returnCode; } -void SomeIpLayer::setPayloadLength(uint32_t payloadLength) -{ - someiphdr *hdr = getSomeIpHeader(); - hdr->length = htobe32(sizeof(someiphdr) - sizeof(hdr->serviceID) - sizeof(hdr->methodID) - sizeof(hdr->length) + - payloadLength); +void SomeIpLayer::setPayloadLength(uint32_t payloadLength) { + someiphdr* hdr = getSomeIpHeader(); + hdr->length = + htobe32(sizeof(someiphdr) - sizeof(hdr->serviceID) - + sizeof(hdr->methodID) - sizeof(hdr->length) + payloadLength); } -void SomeIpLayer::parseNextLayer() -{ - size_t headerLen = getHeaderLen(); - if (m_DataLen <= headerLen) - return; +void SomeIpLayer::parseNextLayer() { + size_t headerLen = getHeaderLen(); + if (m_DataLen <= headerLen) + return; - uint8_t *payload = m_Data + headerLen; - size_t payloadLen = m_DataLen - headerLen; + uint8_t* payload = m_Data + headerLen; + size_t payloadLen = m_DataLen - headerLen; - m_NextLayer = parseSomeIpLayer(payload, payloadLen, this, m_Packet); + m_NextLayer = parseSomeIpLayer(payload, payloadLen, this, m_Packet); } -std::string SomeIpLayer::toString() const -{ - std::stringstream dataStream; +std::string SomeIpLayer::toString() const { + std::stringstream dataStream; - dataStream << "SOME/IP Layer" - << std::hex - << ", Service ID: 0x" << getServiceID() - << ", Method ID: 0x" << getMethodID() - << std::dec - << ", Length: " << getLengthField(); + dataStream << "SOME/IP Layer" << std::hex << ", Service ID: 0x" + << getServiceID() << ", Method ID: 0x" << getMethodID() << std::dec + << ", Length: " << getLengthField(); - return dataStream.str(); + return dataStream.str(); } // SomeIpTpLayer -SomeIpTpLayer::SomeIpTpLayer(uint16_t serviceID, uint16_t methodID, uint16_t clientID, uint16_t sessionID, - uint8_t interfaceVersion, MsgType type, uint8_t returnCode, uint32_t offset, - bool moreSegmentsFlag, const uint8_t *const data, size_t dataLen) -{ - const size_t headerLen = sizeof(someiptphdr); +SomeIpTpLayer::SomeIpTpLayer(uint16_t serviceID, uint16_t methodID, + uint16_t clientID, uint16_t sessionID, + uint8_t interfaceVersion, MsgType type, + uint8_t returnCode, uint32_t offset, + bool moreSegmentsFlag, const uint8_t* const data, + size_t dataLen) { + const size_t headerLen = sizeof(someiptphdr); - m_DataLen = headerLen + dataLen; - m_Data = new uint8_t[m_DataLen]; - m_Protocol = SomeIP; - memset(m_Data, 0, headerLen); - memcpy(m_Data + headerLen, data, dataLen); + m_DataLen = headerLen + dataLen; + m_Data = new uint8_t[m_DataLen]; + m_Protocol = SomeIP; + memset(m_Data, 0, headerLen); + memcpy(m_Data + headerLen, data, dataLen); - setServiceID(serviceID); - setMethodID(methodID); - setPayloadLength((uint32_t)(dataLen + sizeof(uint32_t))); - setClientID(clientID); - setSessionID(sessionID); - setProtocolVersion(0x01); - setInterfaceVersion(interfaceVersion); - setMessageType(setTpFlag((uint8_t)type)); - setReturnCode(returnCode); - setOffset(offset); - setMoreSegmentsFlag(moreSegmentsFlag); + setServiceID(serviceID); + setMethodID(methodID); + setPayloadLength((uint32_t)(dataLen + sizeof(uint32_t))); + setClientID(clientID); + setSessionID(sessionID); + setProtocolVersion(0x01); + setInterfaceVersion(interfaceVersion); + setMessageType(setTpFlag((uint8_t)type)); + setReturnCode(returnCode); + setOffset(offset); + setMoreSegmentsFlag(moreSegmentsFlag); } -uint32_t SomeIpTpLayer::getOffset() const -{ - return (be32toh(getSomeIpTpHeader()->offsetAndFlag) & SOMEIP_TP_OFFSET_MASK) >> 4; +uint32_t SomeIpTpLayer::getOffset() const { + return (be32toh(getSomeIpTpHeader()->offsetAndFlag) & + SOMEIP_TP_OFFSET_MASK) >> + 4; } -void SomeIpTpLayer::setOffset(uint32_t offset) -{ - uint32_t val = (offset << 4) | (be32toh(getSomeIpTpHeader()->offsetAndFlag) & ~SOMEIP_TP_OFFSET_MASK); - getSomeIpTpHeader()->offsetAndFlag = htobe32(val); +void SomeIpTpLayer::setOffset(uint32_t offset) { + uint32_t val = (offset << 4) | (be32toh(getSomeIpTpHeader()->offsetAndFlag) & + ~SOMEIP_TP_OFFSET_MASK); + getSomeIpTpHeader()->offsetAndFlag = htobe32(val); } -bool SomeIpTpLayer::getMoreSegmentsFlag() const -{ - return be32toh(getSomeIpTpHeader()->offsetAndFlag) & SOMEIP_TP_MORE_FLAG_MASK; +bool SomeIpTpLayer::getMoreSegmentsFlag() const { + return be32toh(getSomeIpTpHeader()->offsetAndFlag) & SOMEIP_TP_MORE_FLAG_MASK; } -void SomeIpTpLayer::setMoreSegmentsFlag(bool flag) -{ - uint32_t val = be32toh(getSomeIpTpHeader()->offsetAndFlag); +void SomeIpTpLayer::setMoreSegmentsFlag(bool flag) { + uint32_t val = be32toh(getSomeIpTpHeader()->offsetAndFlag); - if (flag) - { - val = val | SOMEIP_TP_MORE_FLAG_MASK; - } - else - { - val = val & ~SOMEIP_TP_MORE_FLAG_MASK; - } + if (flag) { + val = val | SOMEIP_TP_MORE_FLAG_MASK; + } else { + val = val & ~SOMEIP_TP_MORE_FLAG_MASK; + } - getSomeIpTpHeader()->offsetAndFlag = htobe32(val); + getSomeIpTpHeader()->offsetAndFlag = htobe32(val); } -void SomeIpTpLayer::computeCalculateFields() -{ - setMessageType(setTpFlag(getMessageTypeAsInt())); +void SomeIpTpLayer::computeCalculateFields() { + setMessageType(setTpFlag(getMessageTypeAsInt())); } -std::string SomeIpTpLayer::toString() const -{ - std::stringstream dataStream; +std::string SomeIpTpLayer::toString() const { + std::stringstream dataStream; - dataStream << "SOME/IP-TP Layer" - << std::hex - << ", Service ID: 0x" << getServiceID() - << ", Method ID: 0x" << getMethodID() - << std::dec - << ", Length: " << getLengthField(); + dataStream << "SOME/IP-TP Layer" << std::hex << ", Service ID: 0x" + << getServiceID() << ", Method ID: 0x" << getMethodID() << std::dec + << ", Length: " << getLengthField(); - return dataStream.str(); + return dataStream.str(); } -uint8_t SomeIpTpLayer::setTpFlag(uint8_t messageType) -{ - return messageType | (uint8_t)SomeIpLayer::MsgType::TP_REQUEST; +uint8_t SomeIpTpLayer::setTpFlag(uint8_t messageType) { + return messageType | (uint8_t)SomeIpLayer::MsgType::TP_REQUEST; } } // namespace pcpp diff --git a/Packet++/src/SomeIpSdLayer.cpp b/Packet++/src/SomeIpSdLayer.cpp index f75abe199b..d988a50746 100644 --- a/Packet++/src/SomeIpSdLayer.cpp +++ b/Packet++/src/SomeIpSdLayer.cpp @@ -6,815 +6,754 @@ #include #include -namespace pcpp -{ +namespace pcpp { /* * SomeIpSdOption */ -SomeIpSdOption::~SomeIpSdOption() -{ - if (m_ShadowData != nullptr) - delete[] m_ShadowData; +SomeIpSdOption::~SomeIpSdOption() { + if (m_ShadowData != nullptr) + delete[] m_ShadowData; } SomeIpSdOption::OptionType SomeIpSdOption::getType() const { - return static_cast(getSomeIpSdOptionHeader()->type); + return static_cast(getSomeIpSdOptionHeader()->type); } -uint8_t* SomeIpSdOption::getDataPtr() const -{ - if (m_DataContainer != nullptr) - return m_DataContainer->getDataPtr(m_Offset); +uint8_t* SomeIpSdOption::getDataPtr() const { + if (m_DataContainer != nullptr) + return m_DataContainer->getDataPtr(m_Offset); - return m_ShadowData; + return m_ShadowData; } -SomeIpSdOption::someipsdhdroptionsbase *SomeIpSdOption::getSomeIpSdOptionHeader() const -{ - return (someipsdhdroptionsbase *)getDataPtr(); +SomeIpSdOption::someipsdhdroptionsbase* +SomeIpSdOption::getSomeIpSdOptionHeader() const { + return (someipsdhdroptionsbase*)getDataPtr(); } -void SomeIpSdOption::initStdFields(OptionType type) -{ - someipsdhdroptionsbase *optionHdr = getSomeIpSdOptionHeader(); +void SomeIpSdOption::initStdFields(OptionType type) { + someipsdhdroptionsbase* optionHdr = getSomeIpSdOptionHeader(); - optionHdr->type = static_cast(type); - /* Length field is excluding length field itself and uint8_t type field */ - optionHdr->length = htobe16((uint16_t)(m_DataLen - sizeof(optionHdr->length) - sizeof(optionHdr->type))); + optionHdr->type = static_cast(type); + /* Length field is excluding length field itself and uint8_t type field */ + optionHdr->length = htobe16((uint16_t)(m_DataLen - sizeof(optionHdr->length) - + sizeof(optionHdr->type))); } /* * SomeIpSdIPv4Option */ -SomeIpSdIPv4Option::SomeIpSdIPv4Option(IPv4OptionType type, IPv4Address ipAddress, uint16_t port, - SomeIpSdProtocolType l4Protocol) -{ - m_DataLen = sizeof(someipsdhdroptionsipv4); - m_ShadowData = new uint8_t[m_DataLen]; - memset(m_ShadowData, 0, m_DataLen); +SomeIpSdIPv4Option::SomeIpSdIPv4Option(IPv4OptionType type, + IPv4Address ipAddress, uint16_t port, + SomeIpSdProtocolType l4Protocol) { + m_DataLen = sizeof(someipsdhdroptionsipv4); + m_ShadowData = new uint8_t[m_DataLen]; + memset(m_ShadowData, 0, m_DataLen); - switch(type) - { - case IPv4OptionType::IPv4Endpoint: - initStdFields(OptionType::IPv4Endpoint); - break; - case IPv4OptionType::IPv4Multicast: - initStdFields(OptionType::IPv4Multicast); - break; - case IPv4OptionType::IPv4SdEndpoint: - initStdFields(OptionType::IPv4SdEndpoint); - break; - } + switch (type) { + case IPv4OptionType::IPv4Endpoint: + initStdFields(OptionType::IPv4Endpoint); + break; + case IPv4OptionType::IPv4Multicast: + initStdFields(OptionType::IPv4Multicast); + break; + case IPv4OptionType::IPv4SdEndpoint: + initStdFields(OptionType::IPv4SdEndpoint); + break; + } - someipsdhdroptionsipv4 *hdr = (someipsdhdroptionsipv4 *)getDataPtr(); - hdr->ipv4Address = ipAddress.toInt(); - hdr->portNumber = htobe16(port); - hdr->l4Protocol = l4Protocol; + someipsdhdroptionsipv4* hdr = (someipsdhdroptionsipv4*)getDataPtr(); + hdr->ipv4Address = ipAddress.toInt(); + hdr->portNumber = htobe16(port); + hdr->l4Protocol = l4Protocol; } -SomeIpSdIPv4Option::SomeIpSdIPv4Option(const IDataContainer *dataContainer, size_t offset) - : SomeIpSdOption(dataContainer, offset) -{ - m_DataLen = sizeof(someipsdhdroptionsipv4); +SomeIpSdIPv4Option::SomeIpSdIPv4Option(const IDataContainer* dataContainer, + size_t offset) + : SomeIpSdOption(dataContainer, offset) { + m_DataLen = sizeof(someipsdhdroptionsipv4); } -IPv4Address SomeIpSdIPv4Option::getIpAddress() const -{ - someipsdhdroptionsipv4 *hdr = (someipsdhdroptionsipv4 *)getDataPtr(); - IPv4Address ipAddr(hdr->ipv4Address); +IPv4Address SomeIpSdIPv4Option::getIpAddress() const { + someipsdhdroptionsipv4* hdr = (someipsdhdroptionsipv4*)getDataPtr(); + IPv4Address ipAddr(hdr->ipv4Address); - return ipAddr; + return ipAddr; } -uint16_t SomeIpSdIPv4Option::getPort() const -{ - someipsdhdroptionsipv4 *hdr = (someipsdhdroptionsipv4 *)getDataPtr(); - return be16toh(hdr->portNumber); +uint16_t SomeIpSdIPv4Option::getPort() const { + someipsdhdroptionsipv4* hdr = (someipsdhdroptionsipv4*)getDataPtr(); + return be16toh(hdr->portNumber); } -SomeIpSdProtocolType SomeIpSdIPv4Option::getProtocol() const -{ - someipsdhdroptionsipv4 *hdr = (someipsdhdroptionsipv4 *)getDataPtr(); - return hdr->l4Protocol; +SomeIpSdProtocolType SomeIpSdIPv4Option::getProtocol() const { + someipsdhdroptionsipv4* hdr = (someipsdhdroptionsipv4*)getDataPtr(); + return hdr->l4Protocol; } /* * SomeIpSdIPv6Option */ -SomeIpSdIPv6Option::SomeIpSdIPv6Option(IPv6OptionType type, IPv6Address ipAddress, uint16_t port, - SomeIpSdProtocolType l4Protocol) -{ - m_DataLen = sizeof(someipsdhdroptionsipv6); - m_ShadowData = new uint8_t[m_DataLen]; - memset(m_ShadowData, 0, m_DataLen); +SomeIpSdIPv6Option::SomeIpSdIPv6Option(IPv6OptionType type, + IPv6Address ipAddress, uint16_t port, + SomeIpSdProtocolType l4Protocol) { + m_DataLen = sizeof(someipsdhdroptionsipv6); + m_ShadowData = new uint8_t[m_DataLen]; + memset(m_ShadowData, 0, m_DataLen); - switch(type) - { - case IPv6OptionType::IPv6Endpoint: - initStdFields(OptionType::IPv6Endpoint); - break; - case IPv6OptionType::IPv6Multicast: - initStdFields(OptionType::IPv6Multicast); - break; - case IPv6OptionType::IPv6SdEndpoint: - initStdFields(OptionType::IPv6SdEndpoint); - break; - } + switch (type) { + case IPv6OptionType::IPv6Endpoint: + initStdFields(OptionType::IPv6Endpoint); + break; + case IPv6OptionType::IPv6Multicast: + initStdFields(OptionType::IPv6Multicast); + break; + case IPv6OptionType::IPv6SdEndpoint: + initStdFields(OptionType::IPv6SdEndpoint); + break; + } - someipsdhdroptionsipv6 *hdr = (someipsdhdroptionsipv6 *)getDataPtr(); - std::memcpy(hdr->ipv6Address, ipAddress.toBytes(), 16); - hdr->portNumber = htobe16(port); - hdr->l4Protocol = l4Protocol; + someipsdhdroptionsipv6* hdr = (someipsdhdroptionsipv6*)getDataPtr(); + std::memcpy(hdr->ipv6Address, ipAddress.toBytes(), 16); + hdr->portNumber = htobe16(port); + hdr->l4Protocol = l4Protocol; } -SomeIpSdIPv6Option::SomeIpSdIPv6Option(const IDataContainer *dataContainer, size_t offset) - : SomeIpSdOption(dataContainer, offset) -{ - m_DataLen = sizeof(someipsdhdroptionsipv6); +SomeIpSdIPv6Option::SomeIpSdIPv6Option(const IDataContainer* dataContainer, + size_t offset) + : SomeIpSdOption(dataContainer, offset) { + m_DataLen = sizeof(someipsdhdroptionsipv6); } -IPv6Address SomeIpSdIPv6Option::getIpAddress() const -{ - someipsdhdroptionsipv6 *hdr = (someipsdhdroptionsipv6 *)getDataPtr(); - IPv6Address ipAddr(hdr->ipv6Address); +IPv6Address SomeIpSdIPv6Option::getIpAddress() const { + someipsdhdroptionsipv6* hdr = (someipsdhdroptionsipv6*)getDataPtr(); + IPv6Address ipAddr(hdr->ipv6Address); - return ipAddr; + return ipAddr; } -uint16_t SomeIpSdIPv6Option::getPort() const -{ - someipsdhdroptionsipv6 *hdr = (someipsdhdroptionsipv6 *)getDataPtr(); - return be16toh(hdr->portNumber); +uint16_t SomeIpSdIPv6Option::getPort() const { + someipsdhdroptionsipv6* hdr = (someipsdhdroptionsipv6*)getDataPtr(); + return be16toh(hdr->portNumber); } -SomeIpSdProtocolType SomeIpSdIPv6Option::getProtocol() const -{ - someipsdhdroptionsipv6 *hdr = (someipsdhdroptionsipv6 *)getDataPtr(); - return hdr->l4Protocol; +SomeIpSdProtocolType SomeIpSdIPv6Option::getProtocol() const { + someipsdhdroptionsipv6* hdr = (someipsdhdroptionsipv6*)getDataPtr(); + return hdr->l4Protocol; } /* * SomeIpSdConfigurationOption */ -SomeIpSdConfigurationOption::SomeIpSdConfigurationOption(const std::string &configurationString) -{ - m_DataLen = configurationString.length() + sizeof(someipsdhdroptionsbase); - m_ShadowData = new uint8_t[m_DataLen]; - memset(m_ShadowData, 0, m_DataLen); +SomeIpSdConfigurationOption::SomeIpSdConfigurationOption( + const std::string& configurationString) { + m_DataLen = configurationString.length() + sizeof(someipsdhdroptionsbase); + m_ShadowData = new uint8_t[m_DataLen]; + memset(m_ShadowData, 0, m_DataLen); - initStdFields(OptionType::ConfigurationString); - std::memcpy(getDataPtr() + sizeof(someipsdhdroptionsbase), configurationString.c_str(), - configurationString.length()); + initStdFields(OptionType::ConfigurationString); + std::memcpy(getDataPtr() + sizeof(someipsdhdroptionsbase), + configurationString.c_str(), configurationString.length()); } -SomeIpSdConfigurationOption::SomeIpSdConfigurationOption(const IDataContainer *dataContainer, size_t offset) - : SomeIpSdOption(dataContainer, offset) -{ - m_DataLen = sizeof(someipsdhdroptionsbase) - 1 + be16toh(getSomeIpSdOptionHeader()->length); +SomeIpSdConfigurationOption::SomeIpSdConfigurationOption( + const IDataContainer* dataContainer, size_t offset) + : SomeIpSdOption(dataContainer, offset) { + m_DataLen = sizeof(someipsdhdroptionsbase) - 1 + + be16toh(getSomeIpSdOptionHeader()->length); } -std::string SomeIpSdConfigurationOption::getConfigurationString() const -{ - return std::string((char *)getDataPtr() + sizeof(someipsdhdroptionsbase), - be16toh(getSomeIpSdOptionHeader()->length) - 1); +std::string SomeIpSdConfigurationOption::getConfigurationString() const { + return std::string((char*)getDataPtr() + sizeof(someipsdhdroptionsbase), + be16toh(getSomeIpSdOptionHeader()->length) - 1); } /* * SomeIpSdLoadBalancingOption */ -SomeIpSdLoadBalancingOption::SomeIpSdLoadBalancingOption(uint16_t priority, uint16_t weight) -{ - m_DataLen = sizeof(someipsdhdroptionsload); - m_ShadowData = new uint8_t[m_DataLen]; - memset(m_ShadowData, 0, m_DataLen); +SomeIpSdLoadBalancingOption::SomeIpSdLoadBalancingOption(uint16_t priority, + uint16_t weight) { + m_DataLen = sizeof(someipsdhdroptionsload); + m_ShadowData = new uint8_t[m_DataLen]; + memset(m_ShadowData, 0, m_DataLen); - initStdFields(OptionType::LoadBalancing); + initStdFields(OptionType::LoadBalancing); - someipsdhdroptionsload *hdr = (someipsdhdroptionsload *)getDataPtr(); - hdr->priority = htobe16(priority); - hdr->weight = htobe16(weight); + someipsdhdroptionsload* hdr = (someipsdhdroptionsload*)getDataPtr(); + hdr->priority = htobe16(priority); + hdr->weight = htobe16(weight); } -SomeIpSdLoadBalancingOption::SomeIpSdLoadBalancingOption(const IDataContainer *dataContainer, size_t offset) - : SomeIpSdOption(dataContainer, offset) -{ - m_DataLen = sizeof(someipsdhdroptionsload); +SomeIpSdLoadBalancingOption::SomeIpSdLoadBalancingOption( + const IDataContainer* dataContainer, size_t offset) + : SomeIpSdOption(dataContainer, offset) { + m_DataLen = sizeof(someipsdhdroptionsload); } -uint16_t SomeIpSdLoadBalancingOption::getPriority() const -{ - someipsdhdroptionsload *hdr = (someipsdhdroptionsload *)getDataPtr(); - return be16toh(hdr->priority); +uint16_t SomeIpSdLoadBalancingOption::getPriority() const { + someipsdhdroptionsload* hdr = (someipsdhdroptionsload*)getDataPtr(); + return be16toh(hdr->priority); } -uint16_t SomeIpSdLoadBalancingOption::getWeight() const -{ - someipsdhdroptionsload *hdr = (someipsdhdroptionsload *)getDataPtr(); - return be16toh(hdr->weight); +uint16_t SomeIpSdLoadBalancingOption::getWeight() const { + someipsdhdroptionsload* hdr = (someipsdhdroptionsload*)getDataPtr(); + return be16toh(hdr->weight); } /* * SomeIpSdEntry */ -SomeIpSdEntry::SomeIpSdEntry(EntryType type, uint16_t serviceID, uint16_t instanceID, uint8_t majorVersion, - uint32_t TTL, uint32_t minorVersion) -{ - initStdFields(type, serviceID, instanceID, majorVersion, TTL); - setMinorVersion(minorVersion); -} - -SomeIpSdEntry::SomeIpSdEntry(EntryType type, uint16_t serviceID, uint16_t instanceID, uint8_t majorVersion, - uint32_t TTL, uint8_t counter, uint16_t eventGroupID) -{ - initStdFields(type, serviceID, instanceID, majorVersion, TTL); - setCounter(counter); - setEventgroupId(eventGroupID); -} - -SomeIpSdEntry::SomeIpSdEntry(const SomeIpSdLayer *pSomeIpSdLayer, size_t offset) - : m_Layer(pSomeIpSdLayer), m_Offset(offset), m_ShadowData(nullptr) -{ - EntryType entryType; - - someipsdhdrentry *hdr = getSomeIpSdEntryHeader(); - TypeInternal internalType = static_cast(hdr->type); - auto ttl = getTtl(); +SomeIpSdEntry::SomeIpSdEntry(EntryType type, uint16_t serviceID, + uint16_t instanceID, uint8_t majorVersion, + uint32_t TTL, uint32_t minorVersion) { + initStdFields(type, serviceID, instanceID, majorVersion, TTL); + setMinorVersion(minorVersion); +} + +SomeIpSdEntry::SomeIpSdEntry(EntryType type, uint16_t serviceID, + uint16_t instanceID, uint8_t majorVersion, + uint32_t TTL, uint8_t counter, + uint16_t eventGroupID) { + initStdFields(type, serviceID, instanceID, majorVersion, TTL); + setCounter(counter); + setEventgroupId(eventGroupID); +} + +SomeIpSdEntry::SomeIpSdEntry(const SomeIpSdLayer* pSomeIpSdLayer, size_t offset) + : m_Layer(pSomeIpSdLayer), m_Offset(offset), m_ShadowData(nullptr) { + EntryType entryType; - switch(internalType) - { - case SomeIpSdEntry::TypeInternal::FindService_Internal: - entryType = SomeIpSdEntry::EntryType::FindService; - break; - case SomeIpSdEntry::TypeInternal::OfferService_Internal: - if (ttl == 0) - { - entryType = EntryType::StopOfferService; - } - else - { - entryType = EntryType::OfferService; - } - break; - case SomeIpSdEntry::TypeInternal::SubscribeEventgroup_Internal: - if (ttl == 0) - { - entryType = EntryType::StopSubscribeEventgroup; - } - else - { - entryType = EntryType::SubscribeEventgroup; - } - break; - case SomeIpSdEntry::TypeInternal::SubscribeEventgroupAck_Internal: - if (ttl == 0) - { - entryType = EntryType::SubscribeEventgroupNack; - } - else - { - entryType = EntryType::SubscribeEventgroupAck; - } - break; - default: - entryType = EntryType::UnknownEntryType; - break; - } + someipsdhdrentry* hdr = getSomeIpSdEntryHeader(); + TypeInternal internalType = static_cast(hdr->type); + auto ttl = getTtl(); - m_EntryType = entryType; + switch (internalType) { + case SomeIpSdEntry::TypeInternal::FindService_Internal: + entryType = SomeIpSdEntry::EntryType::FindService; + break; + case SomeIpSdEntry::TypeInternal::OfferService_Internal: + if (ttl == 0) { + entryType = EntryType::StopOfferService; + } else { + entryType = EntryType::OfferService; + } + break; + case SomeIpSdEntry::TypeInternal::SubscribeEventgroup_Internal: + if (ttl == 0) { + entryType = EntryType::StopSubscribeEventgroup; + } else { + entryType = EntryType::SubscribeEventgroup; + } + break; + case SomeIpSdEntry::TypeInternal::SubscribeEventgroupAck_Internal: + if (ttl == 0) { + entryType = EntryType::SubscribeEventgroupNack; + } else { + entryType = EntryType::SubscribeEventgroupAck; + } + break; + default: + entryType = EntryType::UnknownEntryType; + break; + } + + m_EntryType = entryType; +} + +SomeIpSdEntry::~SomeIpSdEntry() { + if (m_ShadowData != nullptr) + delete[] m_ShadowData; } -SomeIpSdEntry::~SomeIpSdEntry() -{ - if (m_ShadowData != nullptr) - delete[] m_ShadowData; +uint8_t* SomeIpSdEntry::getDataPtr() const { + if (m_Layer != nullptr) + return m_Layer->getDataPtr(m_Offset); + + return m_ShadowData; } -uint8_t *SomeIpSdEntry::getDataPtr() const -{ - if (m_Layer != nullptr) - return m_Layer->getDataPtr(m_Offset); +SomeIpSdEntry::someipsdhdrentry* SomeIpSdEntry::getSomeIpSdEntryHeader() const { + return (someipsdhdrentry*)getDataPtr(); +} - return m_ShadowData; +uint32_t SomeIpSdEntry::getNumOptions() const { + auto* hdr = getSomeIpSdEntryHeader(); + return hdr->nrOpt1 + hdr->nrOpt2; } -SomeIpSdEntry::someipsdhdrentry *SomeIpSdEntry::getSomeIpSdEntryHeader() const -{ - return (someipsdhdrentry *)getDataPtr(); +uint16_t SomeIpSdEntry::getServiceId() const { + return be16toh(getSomeIpSdEntryHeader()->serviceID); } -uint32_t SomeIpSdEntry::getNumOptions() const -{ - auto *hdr = getSomeIpSdEntryHeader(); - return hdr->nrOpt1 + hdr->nrOpt2; +void SomeIpSdEntry::setServiceId(uint16_t serviceId) { + getSomeIpSdEntryHeader()->serviceID = htobe16(serviceId); } -uint16_t SomeIpSdEntry::getServiceId() const -{ - return be16toh(getSomeIpSdEntryHeader()->serviceID); +uint16_t SomeIpSdEntry::getInstanceId() const { + return be16toh(getSomeIpSdEntryHeader()->instanceID); } -void SomeIpSdEntry::setServiceId(uint16_t serviceId) -{ - getSomeIpSdEntryHeader()->serviceID = htobe16(serviceId); -} - -uint16_t SomeIpSdEntry::getInstanceId() const -{ - return be16toh(getSomeIpSdEntryHeader()->instanceID); -} - -void SomeIpSdEntry::setInstanceId(uint16_t instanceId) -{ - getSomeIpSdEntryHeader()->instanceID = htobe16(instanceId); -} - -uint8_t SomeIpSdEntry::getMajorVersion() const -{ - return (be32toh(getSomeIpSdEntryHeader()->majorVersion_ttl) & ~SOMEIPSD_HDR_ENTRY_MASK_TTL) >> 24; -} - -void SomeIpSdEntry::setMajorVersion(uint8_t majorVersion) -{ - someipsdhdrentry *hdr = getSomeIpSdEntryHeader(); - uint32_t val = (majorVersion << 24) | (be32toh(hdr->majorVersion_ttl) & SOMEIPSD_HDR_ENTRY_MASK_TTL); - hdr->majorVersion_ttl = htobe32(val); -} - -uint32_t SomeIpSdEntry::getTtl() const -{ - return be32toh(getSomeIpSdEntryHeader()->majorVersion_ttl) & SOMEIPSD_HDR_ENTRY_MASK_TTL; -} - -void SomeIpSdEntry::setTtl(uint32_t ttl) -{ - someipsdhdrentry *hdr = getSomeIpSdEntryHeader(); - uint32_t val = (ttl & SOMEIPSD_HDR_ENTRY_MASK_TTL) | (be32toh(hdr->majorVersion_ttl) & ~SOMEIPSD_HDR_ENTRY_MASK_TTL); - hdr->majorVersion_ttl = htobe32(val); +void SomeIpSdEntry::setInstanceId(uint16_t instanceId) { + getSomeIpSdEntryHeader()->instanceID = htobe16(instanceId); } -uint32_t SomeIpSdEntry::getMinorVersion() const -{ - return be32toh(getSomeIpSdEntryHeader()->data); +uint8_t SomeIpSdEntry::getMajorVersion() const { + return (be32toh(getSomeIpSdEntryHeader()->majorVersion_ttl) & + ~SOMEIPSD_HDR_ENTRY_MASK_TTL) >> + 24; } -void SomeIpSdEntry::setMinorVersion(uint32_t minorVersion) -{ - getSomeIpSdEntryHeader()->data = htobe32(minorVersion); +void SomeIpSdEntry::setMajorVersion(uint8_t majorVersion) { + someipsdhdrentry* hdr = getSomeIpSdEntryHeader(); + uint32_t val = (majorVersion << 24) | + (be32toh(hdr->majorVersion_ttl) & SOMEIPSD_HDR_ENTRY_MASK_TTL); + hdr->majorVersion_ttl = htobe32(val); } -uint8_t SomeIpSdEntry::getCounter() const -{ - return (uint8_t)((be32toh(getSomeIpSdEntryHeader()->data) >> 16) & 0x0F); +uint32_t SomeIpSdEntry::getTtl() const { + return be32toh(getSomeIpSdEntryHeader()->majorVersion_ttl) & + SOMEIPSD_HDR_ENTRY_MASK_TTL; } -void SomeIpSdEntry::setCounter(uint8_t counter) -{ - someipsdhdrentry *hdr = getSomeIpSdEntryHeader(); - hdr->data = htobe32((be32toh(hdr->data) & 0xFFF0FFFF) | ((counter & 0x0F) << 16)); +void SomeIpSdEntry::setTtl(uint32_t ttl) { + someipsdhdrentry* hdr = getSomeIpSdEntryHeader(); + uint32_t val = + (ttl & SOMEIPSD_HDR_ENTRY_MASK_TTL) | + (be32toh(hdr->majorVersion_ttl) & ~SOMEIPSD_HDR_ENTRY_MASK_TTL); + hdr->majorVersion_ttl = htobe32(val); } -uint16_t SomeIpSdEntry::getEventgroupId() const -{ - return (uint16_t)(be32toh(getSomeIpSdEntryHeader()->data) & 0x0000FFFF); +uint32_t SomeIpSdEntry::getMinorVersion() const { + return be32toh(getSomeIpSdEntryHeader()->data); } -void SomeIpSdEntry::setEventgroupId(uint16_t eventgroupID) -{ - someipsdhdrentry *hdr = getSomeIpSdEntryHeader(); - hdr->data = htobe32((be32toh(hdr->data) & 0xFFFF0000) | eventgroupID); +void SomeIpSdEntry::setMinorVersion(uint32_t minorVersion) { + getSomeIpSdEntryHeader()->data = htobe32(minorVersion); } -void SomeIpSdEntry::initStdFields(EntryType type, uint16_t serviceID, uint16_t instanceID, uint8_t majorVersion, - uint32_t TTL) -{ - m_EntryType = type; - m_Layer = nullptr; - m_Offset = 0; +uint8_t SomeIpSdEntry::getCounter() const { + return (uint8_t)((be32toh(getSomeIpSdEntryHeader()->data) >> 16) & 0x0F); +} - size_t dataLen = sizeof(someipsdhdrentry); - m_ShadowData = new uint8_t[dataLen]; - memset(m_ShadowData, 0, dataLen); +void SomeIpSdEntry::setCounter(uint8_t counter) { + someipsdhdrentry* hdr = getSomeIpSdEntryHeader(); + hdr->data = + htobe32((be32toh(hdr->data) & 0xFFF0FFFF) | ((counter & 0x0F) << 16)); +} - someipsdhdrentry *hdr = getSomeIpSdEntryHeader(); - setServiceId(serviceID); - setInstanceId(instanceID); - setMajorVersion(majorVersion); - setTtl(TTL); +uint16_t SomeIpSdEntry::getEventgroupId() const { + return (uint16_t)(be32toh(getSomeIpSdEntryHeader()->data) & 0x0000FFFF); +} - switch (type) - { - case EntryType::FindService: - { - hdr->type = static_cast(TypeInternal::FindService_Internal); - break; - } - case EntryType::OfferService: - case EntryType::StopOfferService: - { - hdr->type = static_cast(TypeInternal::OfferService_Internal); - break; - } - case EntryType::SubscribeEventgroup: - case EntryType::StopSubscribeEventgroup: - { - hdr->type = static_cast(TypeInternal::SubscribeEventgroup_Internal); - break; - } - case EntryType::SubscribeEventgroupAck: - case EntryType::SubscribeEventgroupNack: - { - hdr->type = static_cast(TypeInternal::SubscribeEventgroupAck_Internal); - break; - } - default: - break; - } +void SomeIpSdEntry::setEventgroupId(uint16_t eventgroupID) { + someipsdhdrentry* hdr = getSomeIpSdEntryHeader(); + hdr->data = htobe32((be32toh(hdr->data) & 0xFFFF0000) | eventgroupID); +} + +void SomeIpSdEntry::initStdFields(EntryType type, uint16_t serviceID, + uint16_t instanceID, uint8_t majorVersion, + uint32_t TTL) { + m_EntryType = type; + m_Layer = nullptr; + m_Offset = 0; + + size_t dataLen = sizeof(someipsdhdrentry); + m_ShadowData = new uint8_t[dataLen]; + memset(m_ShadowData, 0, dataLen); + + someipsdhdrentry* hdr = getSomeIpSdEntryHeader(); + setServiceId(serviceID); + setInstanceId(instanceID); + setMajorVersion(majorVersion); + setTtl(TTL); + + switch (type) { + case EntryType::FindService: { + hdr->type = static_cast(TypeInternal::FindService_Internal); + break; + } + case EntryType::OfferService: + case EntryType::StopOfferService: { + hdr->type = static_cast(TypeInternal::OfferService_Internal); + break; + } + case EntryType::SubscribeEventgroup: + case EntryType::StopSubscribeEventgroup: { + hdr->type = + static_cast(TypeInternal::SubscribeEventgroup_Internal); + break; + } + case EntryType::SubscribeEventgroupAck: + case EntryType::SubscribeEventgroupNack: { + hdr->type = + static_cast(TypeInternal::SubscribeEventgroupAck_Internal); + break; + } + default: + break; + } } /* * SomeIpSdLayer */ -SomeIpSdLayer::SomeIpSdLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) - : SomeIpLayer(data, dataLen, prevLayer, packet) -{ - countOptions(m_NumOptions, data); +SomeIpSdLayer::SomeIpSdLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet) + : SomeIpLayer(data, dataLen, prevLayer, packet) { + countOptions(m_NumOptions, data); } -SomeIpSdLayer::SomeIpSdLayer(uint16_t serviceID, uint16_t methodID, uint16_t clientID, uint16_t sessionID, - uint8_t interfaceVersion, MsgType type, uint8_t returnCode, uint8_t flags) -{ - m_Protocol = SomeIP; - m_DataLen = sizeof(someipsdhdr) + 2 * sizeof(uint32_t); - m_Data = new uint8_t[m_DataLen]; - memset(m_Data, 0, m_DataLen); +SomeIpSdLayer::SomeIpSdLayer(uint16_t serviceID, uint16_t methodID, + uint16_t clientID, uint16_t sessionID, + uint8_t interfaceVersion, MsgType type, + uint8_t returnCode, uint8_t flags) { + m_Protocol = SomeIP; + m_DataLen = sizeof(someipsdhdr) + 2 * sizeof(uint32_t); + m_Data = new uint8_t[m_DataLen]; + memset(m_Data, 0, m_DataLen); - m_NumOptions = 0; + m_NumOptions = 0; - setServiceID(serviceID); - setMethodID(methodID); - setPayloadLength(sizeof(uint32_t) * 3); // Flags+Reserved, Length Entries, Length Options - setClientID(clientID); - setSessionID(sessionID); - setProtocolVersion(0x01); - setInterfaceVersion(interfaceVersion); - setMessageType(type); - setReturnCode(returnCode); - setFlags(flags); + setServiceID(serviceID); + setMethodID(methodID); + setPayloadLength(sizeof(uint32_t) * + 3); // Flags+Reserved, Length Entries, Length Options + setClientID(clientID); + setSessionID(sessionID); + setProtocolVersion(0x01); + setInterfaceVersion(interfaceVersion); + setMessageType(type); + setReturnCode(returnCode); + setFlags(flags); } -uint8_t SomeIpSdLayer::getFlags() const -{ - someipsdhdr *hdr = (someipsdhdr *)m_Data; - return hdr->flags; +uint8_t SomeIpSdLayer::getFlags() const { + someipsdhdr* hdr = (someipsdhdr*)m_Data; + return hdr->flags; } -void SomeIpSdLayer::setFlags(uint8_t flags) -{ - someipsdhdr *hdr = (someipsdhdr *)m_Data; - hdr->flags = flags; +void SomeIpSdLayer::setFlags(uint8_t flags) { + someipsdhdr* hdr = (someipsdhdr*)m_Data; + hdr->flags = flags; } -uint32_t SomeIpSdLayer::getNumEntries() const -{ - return (uint32_t)(getLenEntries() / sizeof(SomeIpSdEntry::someipsdhdrentry)); +uint32_t SomeIpSdLayer::getNumEntries() const { + return (uint32_t)(getLenEntries() / sizeof(SomeIpSdEntry::someipsdhdrentry)); } -uint32_t SomeIpSdLayer::getNumOptions() const -{ - return m_NumOptions; -} +uint32_t SomeIpSdLayer::getNumOptions() const { return m_NumOptions; } -const SomeIpSdLayer::EntriesVec SomeIpSdLayer::getEntries() const -{ - size_t remainingLen = getLenEntries(); - size_t offset = sizeof(someipsdhdr) + sizeof(uint32_t); +const SomeIpSdLayer::EntriesVec SomeIpSdLayer::getEntries() const { + size_t remainingLen = getLenEntries(); + size_t offset = sizeof(someipsdhdr) + sizeof(uint32_t); - EntriesVec vecEntries; - EntryPtr entry; + EntriesVec vecEntries; + EntryPtr entry; - while (remainingLen > 0) - { - entry = new SomeIpSdEntry(this, offset); + while (remainingLen > 0) { + entry = new SomeIpSdEntry(this, offset); - size_t entryLen = entry->getLength(); - remainingLen -= entryLen; - offset += entryLen; + size_t entryLen = entry->getLength(); + remainingLen -= entryLen; + offset += entryLen; - vecEntries.push_back(entry); - } + vecEntries.push_back(entry); + } - return vecEntries; + return vecEntries; }; -const SomeIpSdLayer::OptionsVec SomeIpSdLayer::getOptions() const -{ - OptionsVec vecOptions; - OptionPtr option; +const SomeIpSdLayer::OptionsVec SomeIpSdLayer::getOptions() const { + OptionsVec vecOptions; + OptionPtr option; - size_t remainingLen = getLenOptions(); - size_t offset = sizeof(someipsdhdr) + sizeof(uint32_t) + getLenEntries() + sizeof(uint32_t); + size_t remainingLen = getLenOptions(); + size_t offset = sizeof(someipsdhdr) + sizeof(uint32_t) + getLenEntries() + + sizeof(uint32_t); - while (remainingLen > 0) - { - SomeIpSdOption::someipsdhdroptionsbase *hdr = (SomeIpSdOption::someipsdhdroptionsbase *)(m_Data + offset); - SomeIpSdOption::OptionType optionType = static_cast(hdr->type); + while (remainingLen > 0) { + SomeIpSdOption::someipsdhdroptionsbase* hdr = + (SomeIpSdOption::someipsdhdroptionsbase*)(m_Data + offset); + SomeIpSdOption::OptionType optionType = + static_cast(hdr->type); - option = parseOption(optionType, offset); + option = parseOption(optionType, offset); - if (option != nullptr) - { - vecOptions.push_back(std::move(option)); - } + if (option != nullptr) { + vecOptions.push_back(std::move(option)); + } - size_t optionLen = be16toh(hdr->length) + 3; - remainingLen -= optionLen; - offset += optionLen; - } + size_t optionLen = be16toh(hdr->length) + 3; + remainingLen -= optionLen; + offset += optionLen; + } - return vecOptions; + return vecOptions; } -const SomeIpSdLayer::OptionsVec SomeIpSdLayer::getOptionsFromEntry(uint32_t index) const -{ - OptionsVec vecOptions; - OptionPtr option; +const SomeIpSdLayer::OptionsVec +SomeIpSdLayer::getOptionsFromEntry(uint32_t index) const { + OptionsVec vecOptions; + OptionPtr option; - if (index >= getNumEntries()) - return vecOptions; + if (index >= getNumEntries()) + return vecOptions; - size_t remainingLen = getLenOptions(); - size_t offset = sizeof(someipsdhdr) + sizeof(uint32_t) + getLenEntries() + sizeof(uint32_t); + size_t remainingLen = getLenOptions(); + size_t offset = sizeof(someipsdhdr) + sizeof(uint32_t) + getLenEntries() + + sizeof(uint32_t); - size_t offsetToEntry = sizeof(someipsdhdr) + sizeof(uint32_t) + index * sizeof(SomeIpSdEntry::someipsdhdrentry); - SomeIpSdEntry::someipsdhdrentry *hdrEntry = (SomeIpSdEntry::someipsdhdrentry *)(m_Data + offsetToEntry); - uint8_t startIdxRun1 = hdrEntry->indexFirstOption; - uint8_t lenRun1 = hdrEntry->nrOpt1; - uint8_t startIdxRun2 = hdrEntry->indexSecondOption; - uint8_t lenRun2 = hdrEntry->nrOpt2; + size_t offsetToEntry = sizeof(someipsdhdr) + sizeof(uint32_t) + + index * sizeof(SomeIpSdEntry::someipsdhdrentry); + SomeIpSdEntry::someipsdhdrentry* hdrEntry = + (SomeIpSdEntry::someipsdhdrentry*)(m_Data + offsetToEntry); + uint8_t startIdxRun1 = hdrEntry->indexFirstOption; + uint8_t lenRun1 = hdrEntry->nrOpt1; + uint8_t startIdxRun2 = hdrEntry->indexSecondOption; + uint8_t lenRun2 = hdrEntry->nrOpt2; - int idx = 0; + int idx = 0; - while (remainingLen > 0) - { - SomeIpSdOption::someipsdhdroptionsbase *hdrOption = (SomeIpSdOption::someipsdhdroptionsbase *)(m_Data + offset); + while (remainingLen > 0) { + SomeIpSdOption::someipsdhdroptionsbase* hdrOption = + (SomeIpSdOption::someipsdhdroptionsbase*)(m_Data + offset); - if (((idx >= startIdxRun1) && (idx < (startIdxRun1 + lenRun1))) || - ((idx >= startIdxRun2) && (idx < (startIdxRun2 + lenRun2)))) - { - SomeIpSdOption::OptionType optionType = static_cast(hdrOption->type); + if (((idx >= startIdxRun1) && (idx < (startIdxRun1 + lenRun1))) || + ((idx >= startIdxRun2) && (idx < (startIdxRun2 + lenRun2)))) { + SomeIpSdOption::OptionType optionType = + static_cast(hdrOption->type); - option = parseOption(optionType, offset); + option = parseOption(optionType, offset); - if (option != nullptr) - { - vecOptions.push_back(std::move(option)); - } - } + if (option != nullptr) { + vecOptions.push_back(std::move(option)); + } + } - size_t optionLen = be16toh(hdrOption->length) + 3; - remainingLen -= optionLen; - offset += optionLen; - ++idx; - } + size_t optionLen = be16toh(hdrOption->length) + 3; + remainingLen -= optionLen; + offset += optionLen; + ++idx; + } - return vecOptions; + return vecOptions; } -bool SomeIpSdLayer::addOptionTo(uint32_t indexEntry, const SomeIpSdOption &option) -{ - if (indexEntry >= getNumEntries()) - { - return false; - } +bool SomeIpSdLayer::addOptionTo(uint32_t indexEntry, + const SomeIpSdOption& option) { + if (indexEntry >= getNumEntries()) { + return false; + } - uint32_t indexOption = findOption(option); - bool success = addOptionIndex(indexEntry, indexOption); + uint32_t indexOption = findOption(option); + bool success = addOptionIndex(indexEntry, indexOption); - if (!success) - { - return false; - } + if (!success) { + return false; + } - if (indexOption == m_NumOptions) - { - addOption(option); - } + if (indexOption == m_NumOptions) { + addOption(option); + } - return true; + return true; } -std::string SomeIpSdLayer::toString() const -{ - std::stringstream dataStream; +std::string SomeIpSdLayer::toString() const { + std::stringstream dataStream; - dataStream << "SOME/IP-SD Layer, " << getNumEntries() << " entries, " << getNumOptions() << " options"; + dataStream << "SOME/IP-SD Layer, " << getNumEntries() << " entries, " + << getNumOptions() << " options"; - return dataStream.str(); + return dataStream.str(); } -uint32_t SomeIpSdLayer::addEntry(const SomeIpSdEntry &entry) -{ - size_t lenEntries = getLenEntries(); - int offsetToAddAt = sizeof(someipsdhdr) + sizeof(uint32_t) + lenEntries; +uint32_t SomeIpSdLayer::addEntry(const SomeIpSdEntry& entry) { + size_t lenEntries = getLenEntries(); + int offsetToAddAt = sizeof(someipsdhdr) + sizeof(uint32_t) + lenEntries; - extendLayer(offsetToAddAt, entry.getLength()); + extendLayer(offsetToAddAt, entry.getLength()); - setLenEntries(lenEntries + entry.getLength()); + setLenEntries(lenEntries + entry.getLength()); - memcpy(m_Data + offsetToAddAt, entry.getDataPtr(), entry.getLength()); + memcpy(m_Data + offsetToAddAt, entry.getDataPtr(), entry.getLength()); - auto hdr = getSomeIpHeader(); - hdr->length = htobe32(be32toh(hdr->length) + (uint32_t)entry.getLength()); + auto hdr = getSomeIpHeader(); + hdr->length = htobe32(be32toh(hdr->length) + (uint32_t)entry.getLength()); - return getNumEntries() - 1; + return getNumEntries() - 1; } -bool SomeIpSdLayer::isDataValid(const uint8_t* data, size_t dataLen) -{ - uint32_t count; - if (!data || - dataLen < sizeof(someipsdhdr) + sizeof(uint32_t) || - dataLen < sizeof(someipsdhdr) + sizeof(uint32_t) + getLenEntries(data) + sizeof(uint32_t) || - dataLen < sizeof(someipsdhdr) + sizeof(uint32_t) + getLenEntries(data) + sizeof(uint32_t) + getLenOptions(data) || - !countOptions(count, data)) - { - return false; - } +bool SomeIpSdLayer::isDataValid(const uint8_t* data, size_t dataLen) { + uint32_t count; + if (!data || dataLen < sizeof(someipsdhdr) + sizeof(uint32_t) || + dataLen < sizeof(someipsdhdr) + sizeof(uint32_t) + getLenEntries(data) + + sizeof(uint32_t) || + dataLen < sizeof(someipsdhdr) + sizeof(uint32_t) + getLenEntries(data) + + sizeof(uint32_t) + getLenOptions(data) || + !countOptions(count, data)) { + return false; + } - return true; + return true; } -bool SomeIpSdLayer::countOptions(uint32_t& count, const uint8_t* data) -{ - size_t offsetOption = sizeof(someipsdhdr) + sizeof(uint32_t) + getLenEntries(data) + sizeof(uint32_t); - size_t lenOptions = getLenOptions(data); - uint32_t len = 0; +bool SomeIpSdLayer::countOptions(uint32_t& count, const uint8_t* data) { + size_t offsetOption = sizeof(someipsdhdr) + sizeof(uint32_t) + + getLenEntries(data) + sizeof(uint32_t); + size_t lenOptions = getLenOptions(data); + uint32_t len = 0; - count = 0; - while (len < lenOptions) - { - if (len + sizeof(uint16_t) + 3 * sizeof(uint8_t) > lenOptions) - return false; + count = 0; + while (len < lenOptions) { + if (len + sizeof(uint16_t) + 3 * sizeof(uint8_t) > lenOptions) + return false; - uint32_t lenOption = be16toh(*((uint16_t *)(data + offsetOption + len))) + 3 * sizeof(uint8_t); - len += lenOption; - if (len > lenOptions) // the last one must be equal to lenOptions - return false; + uint32_t lenOption = be16toh(*((uint16_t*)(data + offsetOption + len))) + + 3 * sizeof(uint8_t); + len += lenOption; + if (len > lenOptions) // the last one must be equal to lenOptions + return false; - ++(count); - } - return true; + ++(count); + } + return true; } -uint32_t SomeIpSdLayer::findOption(const SomeIpSdOption &option) -{ - size_t offsetOption = sizeof(someipsdhdr) + sizeof(uint32_t) + getLenEntries() + sizeof(uint32_t); +uint32_t SomeIpSdLayer::findOption(const SomeIpSdOption& option) { + size_t offsetOption = sizeof(someipsdhdr) + sizeof(uint32_t) + + getLenEntries() + sizeof(uint32_t); - uint32_t i = 0; - while (i < m_NumOptions) - { - uint32_t lenOption = be16toh(*((uint16_t *)(m_Data + offsetOption))) + 3 * sizeof(uint8_t); + uint32_t i = 0; + while (i < m_NumOptions) { + uint32_t lenOption = + be16toh(*((uint16_t*)(m_Data + offsetOption))) + 3 * sizeof(uint8_t); - if (option.getLength() == lenOption) - { - if (memcmp(m_Data + offsetOption, option.getDataPtr(), option.getLength()) == 0) - { - return i; - } - } - - offsetOption += lenOption; - ++i; - } - return i; -} + if (option.getLength() == lenOption) { + if (memcmp(m_Data + offsetOption, option.getDataPtr(), + option.getLength()) == 0) { + return i; + } + } + + offsetOption += lenOption; + ++i; + } + return i; +} -void SomeIpSdLayer::addOption(const SomeIpSdOption &option) -{ - int offsetToAddAt = (int)getHeaderLen(); +void SomeIpSdLayer::addOption(const SomeIpSdOption& option) { + int offsetToAddAt = (int)getHeaderLen(); - extendLayer(offsetToAddAt, option.getLength()); - memcpy(m_Data + offsetToAddAt, option.getDataPtr(), option.getLength()); + extendLayer(offsetToAddAt, option.getLength()); + memcpy(m_Data + offsetToAddAt, option.getDataPtr(), option.getLength()); - setLenOptions(uint32_t(getLenOptions() + option.getLength())); + setLenOptions(uint32_t(getLenOptions() + option.getLength())); - auto hdr = getSomeIpHeader(); - hdr->length = htobe32(be32toh(hdr->length) + (uint32_t)option.getLength()); + auto hdr = getSomeIpHeader(); + hdr->length = htobe32(be32toh(hdr->length) + (uint32_t)option.getLength()); - ++m_NumOptions; + ++m_NumOptions; } -bool SomeIpSdLayer::addOptionIndex(uint32_t indexEntry, uint32_t indexOffset) -{ - /* The SOME/IP-SD protocol supports two option runs. Runs meaning that two different starting indices with differing - length can be provided. Of course, this only works if the indices in both runs are consecutive. +bool SomeIpSdLayer::addOptionIndex(uint32_t indexEntry, uint32_t indexOffset) { + /* The SOME/IP-SD protocol supports two option runs. Runs meaning that two + different starting indices with differing length can be provided. Of course, + this only works if the indices in both runs are consecutive. + + So, indices like this would work: + 1 2 3 ; 7 8 + + What wouldn't work is this: + 1 2 3 ; 7 9 + 1 3 ; 7 8 + */ + + size_t offsetToAddAt = sizeof(someipsdhdr) + sizeof(uint32_t) + + indexEntry * sizeof(SomeIpSdEntry::someipsdhdrentry); + auto hdrEntry = (SomeIpSdEntry::someipsdhdrentry*)(m_Data + offsetToAddAt); + + uint8_t indexFirstOption = hdrEntry->indexFirstOption; + uint8_t lenFirstOption = hdrEntry->nrOpt1; - So, indices like this would work: - 1 2 3 ; 7 8 + if (lenFirstOption == 0) { + hdrEntry->indexFirstOption = indexOffset; + ++hdrEntry->nrOpt1; + return true; + } - What wouldn't work is this: - 1 2 3 ; 7 9 - 1 3 ; 7 8 - */ + if (static_cast(indexFirstOption + lenFirstOption + 1) == + indexOffset) { + ++hdrEntry->nrOpt1; + return true; + } - size_t offsetToAddAt = sizeof(someipsdhdr) + sizeof(uint32_t) + indexEntry*sizeof(SomeIpSdEntry::someipsdhdrentry); - auto hdrEntry = (SomeIpSdEntry::someipsdhdrentry *)(m_Data + offsetToAddAt); + uint8_t indexSecondOption = hdrEntry->indexSecondOption; + uint8_t lenSecondOption = hdrEntry->nrOpt2; - uint8_t indexFirstOption = hdrEntry->indexFirstOption; - uint8_t lenFirstOption = hdrEntry->nrOpt1; + if (lenSecondOption == 0) { + hdrEntry->indexFirstOption = indexOffset; + ++hdrEntry->nrOpt1; + return true; + } - if (lenFirstOption == 0) - { - hdrEntry->indexFirstOption = indexOffset; - ++hdrEntry->nrOpt1; - return true; - } - - if (static_cast(indexFirstOption + lenFirstOption + 1) == indexOffset) - { - ++hdrEntry->nrOpt1; - return true; - } - - uint8_t indexSecondOption = hdrEntry->indexSecondOption; - uint8_t lenSecondOption = hdrEntry->nrOpt2; + if (static_cast(indexSecondOption + lenSecondOption + 1) == + indexOffset) { + ++hdrEntry->nrOpt2; + return true; + } + + return false; +} + +SomeIpSdLayer::OptionPtr +SomeIpSdLayer::parseOption(SomeIpSdOption::OptionType type, + size_t offset) const { + switch (type) { + case SomeIpSdOption::OptionType::IPv4Endpoint: + case SomeIpSdOption::OptionType::IPv4Multicast: + case SomeIpSdOption::OptionType::IPv4SdEndpoint: { + return new SomeIpSdIPv4Option(this, offset); + } + case SomeIpSdOption::OptionType::IPv6Endpoint: + case SomeIpSdOption::OptionType::IPv6Multicast: + case SomeIpSdOption::OptionType::IPv6SdEndpoint: { + return new SomeIpSdIPv6Option(this, offset); + } + case SomeIpSdOption::OptionType::ConfigurationString: { + return new SomeIpSdConfigurationOption(this, offset); + } + case SomeIpSdOption::OptionType::LoadBalancing: { + return new SomeIpSdLoadBalancingOption(this, offset); + } + default: + break; + } + return nullptr; +} + +size_t SomeIpSdLayer::getLenEntries() const { return getLenEntries(m_Data); } + +size_t SomeIpSdLayer::getLenEntries(const uint8_t* data) { + return be32toh(*((uint32_t*)(data + sizeof(someipsdhdr)))); +} - if (lenSecondOption == 0) - { - hdrEntry->indexFirstOption = indexOffset; - ++hdrEntry->nrOpt1; - return true; - } - - if (static_cast(indexSecondOption + lenSecondOption + 1) == indexOffset) - { - ++hdrEntry->nrOpt2; - return true; - } - - return false; -} +size_t SomeIpSdLayer::getLenOptions() const { return getLenOptions(m_Data); } -SomeIpSdLayer::OptionPtr SomeIpSdLayer::parseOption(SomeIpSdOption::OptionType type, size_t offset) const -{ - switch (type) - { - case SomeIpSdOption::OptionType::IPv4Endpoint: - case SomeIpSdOption::OptionType::IPv4Multicast: - case SomeIpSdOption::OptionType::IPv4SdEndpoint: - { - return new SomeIpSdIPv4Option(this, offset); - } - case SomeIpSdOption::OptionType::IPv6Endpoint: - case SomeIpSdOption::OptionType::IPv6Multicast: - case SomeIpSdOption::OptionType::IPv6SdEndpoint: - { - return new SomeIpSdIPv6Option(this, offset); - } - case SomeIpSdOption::OptionType::ConfigurationString: - { - return new SomeIpSdConfigurationOption(this, offset); - } - case SomeIpSdOption::OptionType::LoadBalancing: - { - return new SomeIpSdLoadBalancingOption(this, offset); - } - default: - break; - } - return nullptr; +size_t SomeIpSdLayer::getLenOptions(const uint8_t* data) { + return be32toh(*((uint32_t*)(data + sizeof(someipsdhdr) + sizeof(uint32_t) + + getLenEntries(data)))); } -size_t SomeIpSdLayer::getLenEntries() const -{ - return getLenEntries(m_Data); -} - -size_t SomeIpSdLayer::getLenEntries(const uint8_t* data) -{ - return be32toh(*((uint32_t *)(data + sizeof(someipsdhdr)))); +void SomeIpSdLayer::setLenEntries(uint32_t length) { + *((uint32_t*)(m_Data + sizeof(someipsdhdr))) = htobe32(length); } -size_t SomeIpSdLayer::getLenOptions() const -{ - return getLenOptions(m_Data); -} - -size_t SomeIpSdLayer::getLenOptions(const uint8_t* data) -{ - return be32toh(*((uint32_t *)(data + sizeof(someipsdhdr) + sizeof(uint32_t) + getLenEntries(data)))); -} - -void SomeIpSdLayer::setLenEntries(uint32_t length) -{ - *((uint32_t *)(m_Data + sizeof(someipsdhdr))) = htobe32(length); -} - -void SomeIpSdLayer::setLenOptions(uint32_t length) -{ - *((uint32_t *)(m_Data + sizeof(someipsdhdr) + sizeof(uint32_t) + getLenEntries())) = htobe32(length); +void SomeIpSdLayer::setLenOptions(uint32_t length) { + *((uint32_t*)(m_Data + sizeof(someipsdhdr) + sizeof(uint32_t) + + getLenEntries())) = htobe32(length); } } // namespace pcpp diff --git a/Packet++/src/StpLayer.cpp b/Packet++/src/StpLayer.cpp index a433a1e400..c25f9b33d8 100644 --- a/Packet++/src/StpLayer.cpp +++ b/Packet++/src/StpLayer.cpp @@ -1,273 +1,316 @@ #define LOG_MODULE PacketLogModuleStpLayer #include "StpLayer.h" -#include "PayloadLayer.h" #include "EndianPortable.h" #include "Logger.h" +#include "PayloadLayer.h" -namespace pcpp -{ +namespace pcpp { // ---------------------- Class STPLayer ---------------------- MacAddress StpLayer::StpMulticastDstMAC("01:80:C2:00:00:00"); MacAddress StpLayer::StpUplinkFastMulticastDstMAC("01:00:0C:CD:CD:CD"); -MacAddress StpLayer::IDtoMacAddress(uint64_t id) -{ - return MacAddress((id >> 40) & 0xFF, (id >> 32) & 0xFF, (id >> 24) & 0xFF, (id >> 16) & 0xFF, (id >> 8) & 0xFF, - id & 0xFF); -} - -uint64_t StpLayer::macAddressToID(const pcpp::MacAddress &addr) -{ - uint8_t value[6]; - addr.copyTo(value); - return ((uint64_t(value[0]) << 40) | (uint64_t(value[1]) << 32) | (uint64_t(value[2]) << 24) | - (uint64_t(value[3]) << 16) | (uint64_t(value[4]) << 8) | (uint64_t(value[5]))); -} - -bool StpLayer::isDataValid(const uint8_t *data, size_t dataLen) { return data && dataLen; } - -StpLayer *StpLayer::parseStpLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) -{ - if (dataLen >= sizeof(stp_tcn_bpdu)) - { - stp_tcn_bpdu *ptr = (stp_tcn_bpdu *)data; - switch (ptr->type) - { - case 0x00: - return StpConfigurationBPDULayer::isDataValid(data, dataLen) - ? new StpConfigurationBPDULayer(data, dataLen, prevLayer, packet) - : nullptr; - case 0x02: - if (ptr->version == 0x2) - return RapidStpLayer::isDataValid(data, dataLen) - ? new RapidStpLayer(data, dataLen, prevLayer, packet) - : nullptr; - if (ptr->version == 0x3) - return MultipleStpLayer::isDataValid(data, dataLen) - ? new MultipleStpLayer(data, dataLen, prevLayer, packet) - : nullptr; - PCPP_LOG_DEBUG("Unknown Spanning Tree Version"); - return nullptr; - case 0x80: - return StpTopologyChangeBPDULayer::isDataValid(data, dataLen) - ? new StpTopologyChangeBPDULayer(data, dataLen, prevLayer, packet) - : nullptr; - // TODO: Per VLAN Spanning Tree+ (PVST+) - // TODO: Rapid Per VLAN Spanning Tree+ (RPVST+) - // TODO: Cisco Uplink Fast - default: - PCPP_LOG_DEBUG("Unknown Spanning Tree Protocol type"); - return nullptr; - } - } - - PCPP_LOG_DEBUG("Data length is less than any STP header"); - return nullptr; -} - -// ---------------------- Class StpTopologyChangeBPDULayer ---------------------- -StpTopologyChangeBPDULayer::StpTopologyChangeBPDULayer() : StpLayer(sizeof(stp_tcn_bpdu)) -{ - // Set initial values for TCN - setProtoId(0x0); - setVersion(0x0); - setType(0x80); -} - -void StpTopologyChangeBPDULayer::parseNextLayer() -{ - if (m_DataLen > sizeof(stp_tcn_bpdu)) - m_NextLayer = new PayloadLayer(m_Data, m_DataLen - sizeof(stp_tcn_bpdu), this, m_Packet); +MacAddress StpLayer::IDtoMacAddress(uint64_t id) { + return MacAddress((id >> 40) & 0xFF, (id >> 32) & 0xFF, (id >> 24) & 0xFF, + (id >> 16) & 0xFF, (id >> 8) & 0xFF, id & 0xFF); +} + +uint64_t StpLayer::macAddressToID(const pcpp::MacAddress& addr) { + uint8_t value[6]; + addr.copyTo(value); + return ((uint64_t(value[0]) << 40) | (uint64_t(value[1]) << 32) | + (uint64_t(value[2]) << 24) | (uint64_t(value[3]) << 16) | + (uint64_t(value[4]) << 8) | (uint64_t(value[5]))); +} + +bool StpLayer::isDataValid(const uint8_t* data, size_t dataLen) { + return data && dataLen; +} + +StpLayer* StpLayer::parseStpLayer(uint8_t* data, size_t dataLen, + Layer* prevLayer, Packet* packet) { + if (dataLen >= sizeof(stp_tcn_bpdu)) { + stp_tcn_bpdu* ptr = (stp_tcn_bpdu*)data; + switch (ptr->type) { + case 0x00: + return StpConfigurationBPDULayer::isDataValid(data, dataLen) + ? new StpConfigurationBPDULayer(data, dataLen, prevLayer, + packet) + : nullptr; + case 0x02: + if (ptr->version == 0x2) + return RapidStpLayer::isDataValid(data, dataLen) + ? new RapidStpLayer(data, dataLen, prevLayer, packet) + : nullptr; + if (ptr->version == 0x3) + return MultipleStpLayer::isDataValid(data, dataLen) + ? new MultipleStpLayer(data, dataLen, prevLayer, packet) + : nullptr; + PCPP_LOG_DEBUG("Unknown Spanning Tree Version"); + return nullptr; + case 0x80: + return StpTopologyChangeBPDULayer::isDataValid(data, dataLen) + ? new StpTopologyChangeBPDULayer(data, dataLen, prevLayer, + packet) + : nullptr; + // TODO: Per VLAN Spanning Tree+ (PVST+) + // TODO: Rapid Per VLAN Spanning Tree+ (RPVST+) + // TODO: Cisco Uplink Fast + default: + PCPP_LOG_DEBUG("Unknown Spanning Tree Protocol type"); + return nullptr; + } + } + + PCPP_LOG_DEBUG("Data length is less than any STP header"); + return nullptr; +} + +// ---------------------- Class StpTopologyChangeBPDULayer +// ---------------------- +StpTopologyChangeBPDULayer::StpTopologyChangeBPDULayer() + : StpLayer(sizeof(stp_tcn_bpdu)) { + // Set initial values for TCN + setProtoId(0x0); + setVersion(0x0); + setType(0x80); +} + +void StpTopologyChangeBPDULayer::parseNextLayer() { + if (m_DataLen > sizeof(stp_tcn_bpdu)) + m_NextLayer = new PayloadLayer(m_Data, m_DataLen - sizeof(stp_tcn_bpdu), + this, m_Packet); } // ---------------------- Class StpConfigurationBPDULayer ---------------------- -StpConfigurationBPDULayer::StpConfigurationBPDULayer() : StpTopologyChangeBPDULayer(sizeof(stp_conf_bpdu)) -{ - // Set initial value for configuration BPDU - setProtoId(0x0); - setVersion(0x0); - setType(0x0); +StpConfigurationBPDULayer::StpConfigurationBPDULayer() + : StpTopologyChangeBPDULayer(sizeof(stp_conf_bpdu)) { + // Set initial value for configuration BPDU + setProtoId(0x0); + setVersion(0x0); + setType(0x0); } -uint64_t StpConfigurationBPDULayer::getRootId() const { return be64toh(getStpConfHeader()->rootId); } +uint64_t StpConfigurationBPDULayer::getRootId() const { + return be64toh(getStpConfHeader()->rootId); +} -void StpConfigurationBPDULayer::setRootId(uint64_t value) { getStpConfHeader()->rootId = htobe64(value); } +void StpConfigurationBPDULayer::setRootId(uint64_t value) { + getStpConfHeader()->rootId = htobe64(value); +} -uint16_t StpConfigurationBPDULayer::getRootPriority() const { return be16toh(getStpConfHeader()->rootId) & 0xf000; } +uint16_t StpConfigurationBPDULayer::getRootPriority() const { + return be16toh(getStpConfHeader()->rootId) & 0xf000; +} -void StpConfigurationBPDULayer::setRootPriority(uint16_t value) -{ - getStpConfHeader()->rootId = (getStpConfHeader()->rootId & ~htobe16(0xf000)) | htobe16(value & 0xf000); +void StpConfigurationBPDULayer::setRootPriority(uint16_t value) { + getStpConfHeader()->rootId = + (getStpConfHeader()->rootId & ~htobe16(0xf000)) | htobe16(value & 0xf000); } -uint16_t StpConfigurationBPDULayer::getRootSystemIDExtension() const -{ - return be16toh(getStpConfHeader()->rootId) & 0x0fff; +uint16_t StpConfigurationBPDULayer::getRootSystemIDExtension() const { + return be16toh(getStpConfHeader()->rootId) & 0x0fff; } -void StpConfigurationBPDULayer::setRootSystemIDExtension(uint16_t value) -{ - getStpConfHeader()->rootId = (getStpConfHeader()->rootId & ~htobe16(0x0fff)) | htobe16(value & 0x0fff); +void StpConfigurationBPDULayer::setRootSystemIDExtension(uint16_t value) { + getStpConfHeader()->rootId = + (getStpConfHeader()->rootId & ~htobe16(0x0fff)) | htobe16(value & 0x0fff); } -void StpConfigurationBPDULayer::setRootSystemID(const pcpp::MacAddress &value) -{ - setRootId((getRootId() & (uint64_t(0xffff) << 48)) | macAddressToID(value)); +void StpConfigurationBPDULayer::setRootSystemID(const pcpp::MacAddress& value) { + setRootId((getRootId() & (uint64_t(0xffff) << 48)) | macAddressToID(value)); }; -uint32_t StpConfigurationBPDULayer::getPathCost() const { return be32toh(getStpConfHeader()->pathCost); } +uint32_t StpConfigurationBPDULayer::getPathCost() const { + return be32toh(getStpConfHeader()->pathCost); +} -void StpConfigurationBPDULayer::setPathCost(uint32_t value) { getStpConfHeader()->pathCost = htobe32(value); } +void StpConfigurationBPDULayer::setPathCost(uint32_t value) { + getStpConfHeader()->pathCost = htobe32(value); +} -uint64_t StpConfigurationBPDULayer::getBridgeId() const { return be64toh(getStpConfHeader()->bridgeId); } +uint64_t StpConfigurationBPDULayer::getBridgeId() const { + return be64toh(getStpConfHeader()->bridgeId); +} -void StpConfigurationBPDULayer::setBridgeId(uint64_t value) { getStpConfHeader()->bridgeId = htobe64(value); } +void StpConfigurationBPDULayer::setBridgeId(uint64_t value) { + getStpConfHeader()->bridgeId = htobe64(value); +} -uint16_t StpConfigurationBPDULayer::getBridgePriority() const -{ - return be16toh(getStpConfHeader()->bridgeId) & 0xf000; +uint16_t StpConfigurationBPDULayer::getBridgePriority() const { + return be16toh(getStpConfHeader()->bridgeId) & 0xf000; } -void StpConfigurationBPDULayer::setBridgePriority(uint16_t value) -{ - getStpConfHeader()->bridgeId = (getStpConfHeader()->bridgeId & ~htobe16(0xf000)) | htobe16(value & 0xf000); +void StpConfigurationBPDULayer::setBridgePriority(uint16_t value) { + getStpConfHeader()->bridgeId = + (getStpConfHeader()->bridgeId & ~htobe16(0xf000)) | + htobe16(value & 0xf000); } -uint16_t StpConfigurationBPDULayer::getBridgeSystemIDExtension() const -{ - return be16toh(getStpConfHeader()->bridgeId) & 0x0fff; +uint16_t StpConfigurationBPDULayer::getBridgeSystemIDExtension() const { + return be16toh(getStpConfHeader()->bridgeId) & 0x0fff; } -void StpConfigurationBPDULayer::setBridgeSystemIDExtension(uint16_t value) -{ - getStpConfHeader()->bridgeId = (getStpConfHeader()->bridgeId & ~htobe16(0x0fff)) | htobe16(value & 0x0fff); +void StpConfigurationBPDULayer::setBridgeSystemIDExtension(uint16_t value) { + getStpConfHeader()->bridgeId = + (getStpConfHeader()->bridgeId & ~htobe16(0x0fff)) | + htobe16(value & 0x0fff); } -void StpConfigurationBPDULayer::setBridgeSystemID(const pcpp::MacAddress &value) -{ - setBridgeId((getBridgeId() & (uint64_t(0xffff) << 48)) | macAddressToID(value)); +void StpConfigurationBPDULayer::setBridgeSystemID( + const pcpp::MacAddress& value) { + setBridgeId((getBridgeId() & (uint64_t(0xffff) << 48)) | + macAddressToID(value)); } -uint16_t StpConfigurationBPDULayer::getPortId() const { return be16toh(getStpConfHeader()->portId); } +uint16_t StpConfigurationBPDULayer::getPortId() const { + return be16toh(getStpConfHeader()->portId); +} -void StpConfigurationBPDULayer::setPortId(uint16_t value) { getStpConfHeader()->portId = htobe16(value); } +void StpConfigurationBPDULayer::setPortId(uint16_t value) { + getStpConfHeader()->portId = htobe16(value); +} -double StpConfigurationBPDULayer::getMessageAge() const { return getStpConfHeader()->msgAge; } +double StpConfigurationBPDULayer::getMessageAge() const { + return getStpConfHeader()->msgAge; +} -void StpConfigurationBPDULayer::setMessageAge(double value) { getStpConfHeader()->msgAge = value; } +void StpConfigurationBPDULayer::setMessageAge(double value) { + getStpConfHeader()->msgAge = value; +} -double StpConfigurationBPDULayer::getMaximumAge() const { return getStpConfHeader()->maxAge; } +double StpConfigurationBPDULayer::getMaximumAge() const { + return getStpConfHeader()->maxAge; +} -void StpConfigurationBPDULayer::setMaximumAge(double value) { getStpConfHeader()->maxAge = value; } +void StpConfigurationBPDULayer::setMaximumAge(double value) { + getStpConfHeader()->maxAge = value; +} -double StpConfigurationBPDULayer::getTransmissionInterval() const { return getStpConfHeader()->helloTime; } +double StpConfigurationBPDULayer::getTransmissionInterval() const { + return getStpConfHeader()->helloTime; +} -void StpConfigurationBPDULayer::setTransmissionInterval(double value) { getStpConfHeader()->helloTime = value; } +void StpConfigurationBPDULayer::setTransmissionInterval(double value) { + getStpConfHeader()->helloTime = value; +} -double StpConfigurationBPDULayer::getForwardDelay() const { return getStpConfHeader()->forwardDelay; } +double StpConfigurationBPDULayer::getForwardDelay() const { + return getStpConfHeader()->forwardDelay; +} -void StpConfigurationBPDULayer::setForwardDelay(double value) { getStpConfHeader()->forwardDelay = value; } +void StpConfigurationBPDULayer::setForwardDelay(double value) { + getStpConfHeader()->forwardDelay = value; +} -void StpConfigurationBPDULayer::parseNextLayer() -{ - if (m_DataLen > sizeof(stp_conf_bpdu)) - m_NextLayer = new PayloadLayer(m_Data, m_DataLen - sizeof(stp_conf_bpdu), this, m_Packet); +void StpConfigurationBPDULayer::parseNextLayer() { + if (m_DataLen > sizeof(stp_conf_bpdu)) + m_NextLayer = new PayloadLayer(m_Data, m_DataLen - sizeof(stp_conf_bpdu), + this, m_Packet); } // ---------------------- Class RapidStpLayer ---------------------- -RapidStpLayer::RapidStpLayer() : StpConfigurationBPDULayer(sizeof(rstp_conf_bpdu)) -{ - // Set initial value for Rapid STP - setProtoId(0x0); - setVersion(0x2); - setType(0x2); +RapidStpLayer::RapidStpLayer() + : StpConfigurationBPDULayer(sizeof(rstp_conf_bpdu)) { + // Set initial value for Rapid STP + setProtoId(0x0); + setVersion(0x2); + setType(0x2); } -void RapidStpLayer::parseNextLayer() -{ - if (m_DataLen > sizeof(rstp_conf_bpdu)) - m_NextLayer = new PayloadLayer(m_Data, m_DataLen - sizeof(rstp_conf_bpdu), this, m_Packet); +void RapidStpLayer::parseNextLayer() { + if (m_DataLen > sizeof(rstp_conf_bpdu)) + m_NextLayer = new PayloadLayer(m_Data, m_DataLen - sizeof(rstp_conf_bpdu), + this, m_Packet); } // ---------------------- Class MultipleStpLayer ---------------------- -MultipleStpLayer::MultipleStpLayer() : RapidStpLayer(sizeof(mstp_conf_bpdu)) -{ - // Set initial value for Multiple STP - setProtoId(0x0); - setVersion(0x3); - setType(0x2); +MultipleStpLayer::MultipleStpLayer() : RapidStpLayer(sizeof(mstp_conf_bpdu)) { + // Set initial value for Multiple STP + setProtoId(0x0); + setVersion(0x3); + setType(0x2); } -uint16_t MultipleStpLayer::getVersion3Len() const { return be16toh(getMstpHeader()->version3Len); } +uint16_t MultipleStpLayer::getVersion3Len() const { + return be16toh(getMstpHeader()->version3Len); +} -void MultipleStpLayer::setVersion3Len(uint16_t value) { getMstpHeader()->version3Len = htobe16(value); } +void MultipleStpLayer::setVersion3Len(uint16_t value) { + getMstpHeader()->version3Len = htobe16(value); +} -uint32_t MultipleStpLayer::getCISTIrpc() const { return be32toh(getMstpHeader()->irpc); } +uint32_t MultipleStpLayer::getCISTIrpc() const { + return be32toh(getMstpHeader()->irpc); +} -void MultipleStpLayer::setCISTIrpc(uint32_t value) { getMstpHeader()->irpc = htobe32(value); } +void MultipleStpLayer::setCISTIrpc(uint32_t value) { + getMstpHeader()->irpc = htobe32(value); +} -uint64_t MultipleStpLayer::getCISTBridgeId() const { return be64toh(getMstpHeader()->cistBridgeId); } +uint64_t MultipleStpLayer::getCISTBridgeId() const { + return be64toh(getMstpHeader()->cistBridgeId); +} -void MultipleStpLayer::setCISTBridgeId(uint64_t value) { getMstpHeader()->cistBridgeId = htobe64(value); } +void MultipleStpLayer::setCISTBridgeId(uint64_t value) { + getMstpHeader()->cistBridgeId = htobe64(value); +} -uint16_t MultipleStpLayer::getCISTBridgePriority() const { return be16toh(getMstpHeader()->cistBridgeId) & 0xf000; } +uint16_t MultipleStpLayer::getCISTBridgePriority() const { + return be16toh(getMstpHeader()->cistBridgeId) & 0xf000; +} -void MultipleStpLayer::setCISTBridgePriority(uint16_t value) -{ - getMstpHeader()->cistBridgeId = (getMstpHeader()->cistBridgeId & ~htobe16(0xf000)) | htobe16(value & 0xf000); +void MultipleStpLayer::setCISTBridgePriority(uint16_t value) { + getMstpHeader()->cistBridgeId = + (getMstpHeader()->cistBridgeId & ~htobe16(0xf000)) | + htobe16(value & 0xf000); } -uint16_t MultipleStpLayer::getCISTBridgeSystemIDExtension() const -{ - return be16toh(getMstpHeader()->cistBridgeId) & 0x0fff; +uint16_t MultipleStpLayer::getCISTBridgeSystemIDExtension() const { + return be16toh(getMstpHeader()->cistBridgeId) & 0x0fff; } -void MultipleStpLayer::setCISTBridgeSystemIDExtension(uint16_t value) -{ - getMstpHeader()->cistBridgeId = (getMstpHeader()->cistBridgeId & ~htobe16(0x0fff)) | htobe16(value & 0x0fff); +void MultipleStpLayer::setCISTBridgeSystemIDExtension(uint16_t value) { + getMstpHeader()->cistBridgeId = + (getMstpHeader()->cistBridgeId & ~htobe16(0x0fff)) | + htobe16(value & 0x0fff); } -void MultipleStpLayer::setCISTBridgeSystemID(const pcpp::MacAddress &value) -{ - setCISTBridgeId((getCISTBridgeId() & (uint64_t(0xffff) << 48)) | macAddressToID(value)); +void MultipleStpLayer::setCISTBridgeSystemID(const pcpp::MacAddress& value) { + setCISTBridgeId((getCISTBridgeId() & (uint64_t(0xffff) << 48)) | + macAddressToID(value)); } -std::string MultipleStpLayer::getMstConfigurationName() const -{ - std::string str = std::string((char *)(getMstpHeader()->mstConfigName), 32); - str.erase(std::find(str.begin(), str.end(), '\0'), str.end()); - return str; +std::string MultipleStpLayer::getMstConfigurationName() const { + std::string str = std::string((char*)(getMstpHeader()->mstConfigName), 32); + str.erase(std::find(str.begin(), str.end(), '\0'), str.end()); + return str; } -uint16_t MultipleStpLayer::getMstConfigRevision() const -{ - return be16toh(getMstpHeader()->mstConfigRevision); +uint16_t MultipleStpLayer::getMstConfigRevision() const { + return be16toh(getMstpHeader()->mstConfigRevision); } -void MultipleStpLayer::setMstConfigRevision(uint16_t value) -{ - getMstpHeader()->mstConfigRevision = htobe16(value); +void MultipleStpLayer::setMstConfigRevision(uint16_t value) { + getMstpHeader()->mstConfigRevision = htobe16(value); } -void MultipleStpLayer::setMstConfigDigest(const uint8_t *value, uint8_t len) -{ - memset(getMstpHeader()->mstConfigDigest, 0, 16); - memcpy(getMstpHeader()->mstConfigDigest, value, std::min(len, 16)); +void MultipleStpLayer::setMstConfigDigest(const uint8_t* value, uint8_t len) { + memset(getMstpHeader()->mstConfigDigest, 0, 16); + memcpy(getMstpHeader()->mstConfigDigest, value, std::min(len, 16)); } -void MultipleStpLayer::setMstConfigurationName(const std::string &value) -{ - memset(getMstpHeader()->mstConfigName, 0, 32); - memcpy(getMstpHeader()->mstConfigName, value.c_str(), std::min(value.size(), 32)); +void MultipleStpLayer::setMstConfigurationName(const std::string& value) { + memset(getMstpHeader()->mstConfigName, 0, 32); + memcpy(getMstpHeader()->mstConfigName, value.c_str(), + std::min(value.size(), 32)); } -msti_conf_msg *MultipleStpLayer::getMstiConfMessages() const -{ - if (getNumberOfMSTIConfMessages()) - return (msti_conf_msg *)(m_Data + sizeof(mstp_conf_bpdu)); - return nullptr; +msti_conf_msg* MultipleStpLayer::getMstiConfMessages() const { + if (getNumberOfMSTIConfMessages()) + return (msti_conf_msg*)(m_Data + sizeof(mstp_conf_bpdu)); + return nullptr; } } // namespace pcpp diff --git a/Packet++/src/TLVData.cpp b/Packet++/src/TLVData.cpp index 161fe723cb..95cacaf6ff 100644 --- a/Packet++/src/TLVData.cpp +++ b/Packet++/src/TLVData.cpp @@ -1,113 +1,98 @@ #include "TLVData.h" -#include "GeneralUtils.h" #include "EndianPortable.h" +#include "GeneralUtils.h" -namespace pcpp -{ +namespace pcpp { -TLVRecordBuilder::TLVRecordBuilder() -{ - m_RecType = 0; - m_RecValueLen = 0; - m_RecValue = nullptr; +TLVRecordBuilder::TLVRecordBuilder() { + m_RecType = 0; + m_RecValueLen = 0; + m_RecValue = nullptr; } -TLVRecordBuilder::TLVRecordBuilder(uint32_t recType, const uint8_t* recValue, uint8_t recValueLen) -{ - init(recType, recValue, recValueLen); +TLVRecordBuilder::TLVRecordBuilder(uint32_t recType, const uint8_t* recValue, + uint8_t recValueLen) { + init(recType, recValue, recValueLen); } -TLVRecordBuilder::TLVRecordBuilder(uint32_t recType, uint8_t recValue) -{ - init(recType, &recValue, sizeof(uint8_t)); +TLVRecordBuilder::TLVRecordBuilder(uint32_t recType, uint8_t recValue) { + init(recType, &recValue, sizeof(uint8_t)); } -TLVRecordBuilder::TLVRecordBuilder(uint32_t recType, uint16_t recValue) -{ - recValue = htobe16(recValue); - init(recType, (uint8_t*)&recValue, sizeof(uint16_t)); +TLVRecordBuilder::TLVRecordBuilder(uint32_t recType, uint16_t recValue) { + recValue = htobe16(recValue); + init(recType, (uint8_t*)&recValue, sizeof(uint16_t)); } -TLVRecordBuilder::TLVRecordBuilder(uint32_t recType, uint32_t recValue) -{ - recValue = htobe32(recValue); - init(recType, (uint8_t*)&recValue, sizeof(uint32_t)); +TLVRecordBuilder::TLVRecordBuilder(uint32_t recType, uint32_t recValue) { + recValue = htobe32(recValue); + init(recType, (uint8_t*)&recValue, sizeof(uint32_t)); } -TLVRecordBuilder::TLVRecordBuilder(uint32_t recType, const IPv4Address& recValue) -{ - uint32_t recIntValue = recValue.toInt(); - init(recType, (uint8_t*)&recIntValue, sizeof(uint32_t)); +TLVRecordBuilder::TLVRecordBuilder(uint32_t recType, + const IPv4Address& recValue) { + uint32_t recIntValue = recValue.toInt(); + init(recType, (uint8_t*)&recIntValue, sizeof(uint32_t)); } -TLVRecordBuilder::TLVRecordBuilder(uint32_t recType, const std::string& recValue, bool valueIsHexString) -{ - m_RecType = 0; - m_RecValueLen = 0; - m_RecValue = nullptr; - - if (valueIsHexString) - { - uint8_t recValueByteArr[512]; - size_t byteArraySize = hexStringToByteArray(recValue, recValueByteArr, 512); - if (byteArraySize > 0) - { - init(recType, recValueByteArr, byteArraySize); - } - } - else - { - uint8_t* recValueByteArr = (uint8_t*)recValue.c_str(); - init(recType, recValueByteArr, recValue.length()); - } +TLVRecordBuilder::TLVRecordBuilder(uint32_t recType, + const std::string& recValue, + bool valueIsHexString) { + m_RecType = 0; + m_RecValueLen = 0; + m_RecValue = nullptr; + + if (valueIsHexString) { + uint8_t recValueByteArr[512]; + size_t byteArraySize = hexStringToByteArray(recValue, recValueByteArr, 512); + if (byteArraySize > 0) { + init(recType, recValueByteArr, byteArraySize); + } + } else { + uint8_t* recValueByteArr = (uint8_t*)recValue.c_str(); + init(recType, recValueByteArr, recValue.length()); + } } -void TLVRecordBuilder::copyData(const TLVRecordBuilder& other) -{ - m_RecType = other.m_RecType; - m_RecValueLen = other.m_RecValueLen; - m_RecValue = nullptr; - if (other.m_RecValue != nullptr) - { - m_RecValue = new uint8_t[m_RecValueLen]; - memcpy(m_RecValue, other.m_RecValue, m_RecValueLen); - } +void TLVRecordBuilder::copyData(const TLVRecordBuilder& other) { + m_RecType = other.m_RecType; + m_RecValueLen = other.m_RecValueLen; + m_RecValue = nullptr; + if (other.m_RecValue != nullptr) { + m_RecValue = new uint8_t[m_RecValueLen]; + memcpy(m_RecValue, other.m_RecValue, m_RecValueLen); + } } -TLVRecordBuilder::TLVRecordBuilder(const TLVRecordBuilder& other) -{ - copyData(other); +TLVRecordBuilder::TLVRecordBuilder(const TLVRecordBuilder& other) { + copyData(other); } -TLVRecordBuilder& TLVRecordBuilder::operator=(const TLVRecordBuilder& other) -{ - if (m_RecValue != nullptr) - { - delete [] m_RecValue; - m_RecValue = nullptr; - } +TLVRecordBuilder& TLVRecordBuilder::operator=(const TLVRecordBuilder& other) { + if (m_RecValue != nullptr) { + delete[] m_RecValue; + m_RecValue = nullptr; + } - copyData(other); + copyData(other); - return *this; + return *this; } -TLVRecordBuilder::~TLVRecordBuilder() -{ - if (m_RecValue != nullptr) delete [] m_RecValue; +TLVRecordBuilder::~TLVRecordBuilder() { + if (m_RecValue != nullptr) + delete[] m_RecValue; } -void TLVRecordBuilder::init(uint32_t recType, const uint8_t* recValue, size_t recValueLen) -{ - m_RecType = recType; - m_RecValueLen = recValueLen; - m_RecValue = new uint8_t[recValueLen]; - if (recValue != nullptr) - memcpy(m_RecValue, recValue, recValueLen); - else - memset(m_RecValue, 0, recValueLen); +void TLVRecordBuilder::init(uint32_t recType, const uint8_t* recValue, + size_t recValueLen) { + m_RecType = recType; + m_RecValueLen = recValueLen; + m_RecValue = new uint8_t[recValueLen]; + if (recValue != nullptr) + memcpy(m_RecValue, recValue, recValueLen); + else + memset(m_RecValue, 0, recValueLen); } - - -} +} // namespace pcpp diff --git a/Packet++/src/TcpLayer.cpp b/Packet++/src/TcpLayer.cpp index 1b51207288..c48520b8f3 100644 --- a/Packet++/src/TcpLayer.cpp +++ b/Packet++/src/TcpLayer.cpp @@ -1,27 +1,26 @@ #define LOG_MODULE PacketLogModuleTcpLayer -#include "EndianPortable.h" #include "TcpLayer.h" +#include "BgpLayer.h" +#include "DnsLayer.h" +#include "EndianPortable.h" +#include "FtpLayer.h" +#include "HttpLayer.h" #include "IPv4Layer.h" #include "IPv6Layer.h" +#include "Logger.h" +#include "PacketUtils.h" #include "PayloadLayer.h" -#include "HttpLayer.h" +#include "SSHLayer.h" #include "SSLLayer.h" #include "SipLayer.h" -#include "BgpLayer.h" -#include "SSHLayer.h" -#include "DnsLayer.h" +#include "SomeIpLayer.h" #include "TelnetLayer.h" #include "TpktLayer.h" -#include "FtpLayer.h" -#include "SomeIpLayer.h" -#include "PacketUtils.h" -#include "Logger.h" -#include #include +#include -namespace pcpp -{ +namespace pcpp { #define TCPOPT_DUMMY 0xff @@ -29,397 +28,384 @@ namespace pcpp /// TcpOptionBuilder /// ~~~~~~~~~~~~~~~~ -TcpOptionBuilder::TcpOptionBuilder(NopEolOptionTypes optionType) -{ - switch (optionType) - { - case EOL: - init((uint8_t)PCPP_TCPOPT_EOL, nullptr, 0); - break; - case NOP: - default: - init((uint8_t)PCPP_TCPOPT_NOP, nullptr, 0); - break; - } +TcpOptionBuilder::TcpOptionBuilder(NopEolOptionTypes optionType) { + switch (optionType) { + case EOL: + init((uint8_t)PCPP_TCPOPT_EOL, nullptr, 0); + break; + case NOP: + default: + init((uint8_t)PCPP_TCPOPT_NOP, nullptr, 0); + break; + } } -TcpOption TcpOptionBuilder::build() const -{ - uint8_t recType = static_cast(m_RecType); - size_t optionSize = m_RecValueLen + 2*sizeof(uint8_t); - - if (recType == (uint8_t)PCPP_TCPOPT_EOL || recType == (uint8_t)PCPP_TCPOPT_NOP) - { - if (m_RecValueLen != 0) - { - PCPP_LOG_ERROR("TCP NOP and TCP EOL options are 1-byte long and don't have option value. Tried to set option value of size " << m_RecValueLen); - return TcpOption(nullptr); - } - - optionSize = 1; - } - - uint8_t* recordBuffer = new uint8_t[optionSize]; - memset(recordBuffer, 0, optionSize); - recordBuffer[0] = recType; - if (optionSize > 1) - { - recordBuffer[1] = static_cast(optionSize); - if (optionSize > 2 && m_RecValue != nullptr) - memcpy(recordBuffer+2, m_RecValue, m_RecValueLen); - } - - return TcpOption(recordBuffer); +TcpOption TcpOptionBuilder::build() const { + uint8_t recType = static_cast(m_RecType); + size_t optionSize = m_RecValueLen + 2 * sizeof(uint8_t); + + if (recType == (uint8_t)PCPP_TCPOPT_EOL || + recType == (uint8_t)PCPP_TCPOPT_NOP) { + if (m_RecValueLen != 0) { + PCPP_LOG_ERROR("TCP NOP and TCP EOL options are 1-byte long and don't " + "have option value. Tried to set option value of size " + << m_RecValueLen); + return TcpOption(nullptr); + } + + optionSize = 1; + } + + uint8_t* recordBuffer = new uint8_t[optionSize]; + memset(recordBuffer, 0, optionSize); + recordBuffer[0] = recType; + if (optionSize > 1) { + recordBuffer[1] = static_cast(optionSize); + if (optionSize > 2 && m_RecValue != nullptr) + memcpy(recordBuffer + 2, m_RecValue, m_RecValueLen); + } + + return TcpOption(recordBuffer); } - - /// ~~~~~~~~ /// TcpLayer /// ~~~~~~~~ -uint16_t TcpLayer::getSrcPort() const -{ - return be16toh(getTcpHeader()->portSrc); +uint16_t TcpLayer::getSrcPort() const { + return be16toh(getTcpHeader()->portSrc); } -uint16_t TcpLayer::getDstPort() const -{ - return be16toh(getTcpHeader()->portDst); +uint16_t TcpLayer::getDstPort() const { + return be16toh(getTcpHeader()->portDst); } -TcpOption TcpLayer::getTcpOption(TcpOptionType option) const -{ - return m_OptionReader.getTLVRecord((uint8_t)option, getOptionsBasePtr(), getHeaderLen() - sizeof(tcphdr)); +TcpOption TcpLayer::getTcpOption(TcpOptionType option) const { + return m_OptionReader.getTLVRecord((uint8_t)option, getOptionsBasePtr(), + getHeaderLen() - sizeof(tcphdr)); } -TcpOption TcpLayer::getFirstTcpOption() const -{ - return m_OptionReader.getFirstTLVRecord(getOptionsBasePtr(), getHeaderLen() - sizeof(tcphdr)); +TcpOption TcpLayer::getFirstTcpOption() const { + return m_OptionReader.getFirstTLVRecord(getOptionsBasePtr(), + getHeaderLen() - sizeof(tcphdr)); } -TcpOption TcpLayer::getNextTcpOption(TcpOption& tcpOption) const -{ - TcpOption nextOpt = m_OptionReader.getNextTLVRecord(tcpOption, getOptionsBasePtr(), getHeaderLen() - sizeof(tcphdr)); - if (nextOpt.isNotNull() && nextOpt.getType() == TCPOPT_DUMMY) - return TcpOption(nullptr); +TcpOption TcpLayer::getNextTcpOption(TcpOption& tcpOption) const { + TcpOption nextOpt = m_OptionReader.getNextTLVRecord( + tcpOption, getOptionsBasePtr(), getHeaderLen() - sizeof(tcphdr)); + if (nextOpt.isNotNull() && nextOpt.getType() == TCPOPT_DUMMY) + return TcpOption(nullptr); - return nextOpt; + return nextOpt; } -size_t TcpLayer::getTcpOptionCount() const -{ - return m_OptionReader.getTLVRecordCount(getOptionsBasePtr(), getHeaderLen() - sizeof(tcphdr)); +size_t TcpLayer::getTcpOptionCount() const { + return m_OptionReader.getTLVRecordCount(getOptionsBasePtr(), + getHeaderLen() - sizeof(tcphdr)); } -TcpOption TcpLayer::addTcpOption(const TcpOptionBuilder& optionBuilder) -{ - return addTcpOptionAt(optionBuilder, getHeaderLen()-m_NumOfTrailingBytes); +TcpOption TcpLayer::addTcpOption(const TcpOptionBuilder& optionBuilder) { + return addTcpOptionAt(optionBuilder, getHeaderLen() - m_NumOfTrailingBytes); } -TcpOption TcpLayer::addTcpOptionAfter(const TcpOptionBuilder& optionBuilder, TcpOptionType prevOptionType) -{ - int offset = 0; - - if (prevOptionType == TCPOPT_Unknown) - { - offset = sizeof(tcphdr); - } - else - { - TcpOption prevOpt = getTcpOption(prevOptionType); - if (prevOpt.isNull()) - { - PCPP_LOG_ERROR("Previous option of type " << (int)prevOptionType << " not found, cannot add a new TCP option"); - return TcpOption(nullptr); - } - - offset = prevOpt.getRecordBasePtr() + prevOpt.getTotalSize() - m_Data; - } - - return addTcpOptionAt(optionBuilder, offset); +TcpOption TcpLayer::addTcpOptionAfter(const TcpOptionBuilder& optionBuilder, + TcpOptionType prevOptionType) { + int offset = 0; + + if (prevOptionType == TCPOPT_Unknown) { + offset = sizeof(tcphdr); + } else { + TcpOption prevOpt = getTcpOption(prevOptionType); + if (prevOpt.isNull()) { + PCPP_LOG_ERROR("Previous option of type " + << (int)prevOptionType + << " not found, cannot add a new TCP option"); + return TcpOption(nullptr); + } + + offset = prevOpt.getRecordBasePtr() + prevOpt.getTotalSize() - m_Data; + } + + return addTcpOptionAt(optionBuilder, offset); } -bool TcpLayer::removeTcpOption(TcpOptionType optionType) -{ - TcpOption opt = getTcpOption(optionType); - if (opt.isNull()) - { - return false; - } +bool TcpLayer::removeTcpOption(TcpOptionType optionType) { + TcpOption opt = getTcpOption(optionType); + if (opt.isNull()) { + return false; + } - // calculate total TCP option size - TcpOption curOpt = getFirstTcpOption(); - size_t totalOptSize = 0; - while (!curOpt.isNull()) - { - totalOptSize += curOpt.getTotalSize(); - curOpt = getNextTcpOption(curOpt); - } - totalOptSize -= opt.getTotalSize(); + // calculate total TCP option size + TcpOption curOpt = getFirstTcpOption(); + size_t totalOptSize = 0; + while (!curOpt.isNull()) { + totalOptSize += curOpt.getTotalSize(); + curOpt = getNextTcpOption(curOpt); + } + totalOptSize -= opt.getTotalSize(); + int offset = opt.getRecordBasePtr() - m_Data; - int offset = opt.getRecordBasePtr() - m_Data; + if (!shortenLayer(offset, opt.getTotalSize())) { + return false; + } - if (!shortenLayer(offset, opt.getTotalSize())) - { - return false; - } + adjustTcpOptionTrailer(totalOptSize); - adjustTcpOptionTrailer(totalOptSize); + m_OptionReader.changeTLVRecordCount(-1); - m_OptionReader.changeTLVRecordCount(-1); - - return true; + return true; } -bool TcpLayer::removeAllTcpOptions() -{ - int offset = sizeof(tcphdr); +bool TcpLayer::removeAllTcpOptions() { + int offset = sizeof(tcphdr); - if (!shortenLayer(offset, getHeaderLen()-offset)) - return false; + if (!shortenLayer(offset, getHeaderLen() - offset)) + return false; - getTcpHeader()->dataOffset = sizeof(tcphdr)/4; - m_NumOfTrailingBytes = 0; - m_OptionReader.changeTLVRecordCount(0-getTcpOptionCount()); - return true; + getTcpHeader()->dataOffset = sizeof(tcphdr) / 4; + m_NumOfTrailingBytes = 0; + m_OptionReader.changeTLVRecordCount(0 - getTcpOptionCount()); + return true; } -TcpOption TcpLayer::addTcpOptionAt(const TcpOptionBuilder& optionBuilder, int offset) -{ - TcpOption newOption = optionBuilder.build(); - if (newOption.isNull()) - return newOption; +TcpOption TcpLayer::addTcpOptionAt(const TcpOptionBuilder& optionBuilder, + int offset) { + TcpOption newOption = optionBuilder.build(); + if (newOption.isNull()) + return newOption; - // calculate total TCP option size - TcpOption curOpt = getFirstTcpOption(); - size_t totalOptSize = 0; - while (!curOpt.isNull()) - { - totalOptSize += curOpt.getTotalSize(); - curOpt = getNextTcpOption(curOpt); - } - totalOptSize += newOption.getTotalSize(); + // calculate total TCP option size + TcpOption curOpt = getFirstTcpOption(); + size_t totalOptSize = 0; + while (!curOpt.isNull()) { + totalOptSize += curOpt.getTotalSize(); + curOpt = getNextTcpOption(curOpt); + } + totalOptSize += newOption.getTotalSize(); - size_t sizeToExtend = newOption.getTotalSize(); + size_t sizeToExtend = newOption.getTotalSize(); - if (!extendLayer(offset, sizeToExtend)) - { - PCPP_LOG_ERROR("Could not extend TcpLayer in [" << sizeToExtend << "] bytes"); - newOption.purgeRecordData(); - return TcpOption(nullptr); - } + if (!extendLayer(offset, sizeToExtend)) { + PCPP_LOG_ERROR("Could not extend TcpLayer in [" << sizeToExtend + << "] bytes"); + newOption.purgeRecordData(); + return TcpOption(nullptr); + } - memcpy(m_Data + offset, newOption.getRecordBasePtr(), newOption.getTotalSize()); + memcpy(m_Data + offset, newOption.getRecordBasePtr(), + newOption.getTotalSize()); - newOption.purgeRecordData(); + newOption.purgeRecordData(); - adjustTcpOptionTrailer(totalOptSize); + adjustTcpOptionTrailer(totalOptSize); - m_OptionReader.changeTLVRecordCount(1); + m_OptionReader.changeTLVRecordCount(1); - uint8_t* newOptPtr = m_Data + offset; + uint8_t* newOptPtr = m_Data + offset; - return TcpOption(newOptPtr); + return TcpOption(newOptPtr); } -void TcpLayer::adjustTcpOptionTrailer(size_t totalOptSize) -{ - int newNumberOfTrailingBytes = 0; - while ((totalOptSize + newNumberOfTrailingBytes) % 4 != 0) - newNumberOfTrailingBytes++; +void TcpLayer::adjustTcpOptionTrailer(size_t totalOptSize) { + int newNumberOfTrailingBytes = 0; + while ((totalOptSize + newNumberOfTrailingBytes) % 4 != 0) + newNumberOfTrailingBytes++; - if (newNumberOfTrailingBytes < m_NumOfTrailingBytes) - shortenLayer(sizeof(tcphdr)+totalOptSize, m_NumOfTrailingBytes - newNumberOfTrailingBytes - 1); - else if (newNumberOfTrailingBytes > m_NumOfTrailingBytes) - extendLayer(sizeof(tcphdr)+totalOptSize, newNumberOfTrailingBytes - m_NumOfTrailingBytes); + if (newNumberOfTrailingBytes < m_NumOfTrailingBytes) + shortenLayer(sizeof(tcphdr) + totalOptSize, + m_NumOfTrailingBytes - newNumberOfTrailingBytes - 1); + else if (newNumberOfTrailingBytes > m_NumOfTrailingBytes) + extendLayer(sizeof(tcphdr) + totalOptSize, + newNumberOfTrailingBytes - m_NumOfTrailingBytes); - m_NumOfTrailingBytes = newNumberOfTrailingBytes; + m_NumOfTrailingBytes = newNumberOfTrailingBytes; - for (int i = 0; i < m_NumOfTrailingBytes; i++) - m_Data[sizeof(tcphdr) + totalOptSize + i] = TCPOPT_DUMMY; + for (int i = 0; i < m_NumOfTrailingBytes; i++) + m_Data[sizeof(tcphdr) + totalOptSize + i] = TCPOPT_DUMMY; - getTcpHeader()->dataOffset = (sizeof(tcphdr) + totalOptSize + m_NumOfTrailingBytes)/4; + getTcpHeader()->dataOffset = + (sizeof(tcphdr) + totalOptSize + m_NumOfTrailingBytes) / 4; } -uint16_t TcpLayer::calculateChecksum(bool writeResultToPacket) -{ - tcphdr* tcpHdr = getTcpHeader(); - uint16_t checksumRes = 0; - uint16_t currChecksumValue = tcpHdr->headerChecksum; - - if (m_PrevLayer != nullptr) - { - tcpHdr->headerChecksum = 0; - PCPP_LOG_DEBUG("TCP data len = " << m_DataLen); - - if (m_PrevLayer->getProtocol() == IPv4) - { - IPv4Address srcIP = ((IPv4Layer*)m_PrevLayer)->getSrcIPv4Address(); - IPv4Address dstIP = ((IPv4Layer*)m_PrevLayer)->getDstIPv4Address(); - - checksumRes = pcpp::computePseudoHdrChecksum((uint8_t *) tcpHdr, getDataLen(), IPAddress::IPv4AddressType, - PACKETPP_IPPROTO_TCP, srcIP, dstIP); - - PCPP_LOG_DEBUG("calculated IPv4 TCP checksum = 0x" << std::uppercase << std::hex << checksumRes); - } - else if (m_PrevLayer->getProtocol() == IPv6) - { - IPv6Address srcIP = ((IPv6Layer*)m_PrevLayer)->getSrcIPv6Address(); - IPv6Address dstIP = ((IPv6Layer*)m_PrevLayer)->getDstIPv6Address(); - - checksumRes = computePseudoHdrChecksum((uint8_t *) tcpHdr, getDataLen(), IPAddress::IPv6AddressType, - PACKETPP_IPPROTO_TCP, srcIP, dstIP); - - PCPP_LOG_DEBUG("calculated IPv6 TCP checksum = 0xX" << std::uppercase << std::hex << checksumRes); - } - } - - if(writeResultToPacket) - tcpHdr->headerChecksum = htobe16(checksumRes); - else - tcpHdr->headerChecksum = currChecksumValue; - - return checksumRes; -} +uint16_t TcpLayer::calculateChecksum(bool writeResultToPacket) { + tcphdr* tcpHdr = getTcpHeader(); + uint16_t checksumRes = 0; + uint16_t currChecksumValue = tcpHdr->headerChecksum; -void TcpLayer::initLayer() -{ - m_DataLen = sizeof(tcphdr); - m_Data = new uint8_t[m_DataLen]; - memset(m_Data, 0, m_DataLen); - m_Protocol = TCP; - m_NumOfTrailingBytes = 0; - getTcpHeader()->dataOffset = sizeof(tcphdr)/4; + if (m_PrevLayer != nullptr) { + tcpHdr->headerChecksum = 0; + PCPP_LOG_DEBUG("TCP data len = " << m_DataLen); + + if (m_PrevLayer->getProtocol() == IPv4) { + IPv4Address srcIP = ((IPv4Layer*)m_PrevLayer)->getSrcIPv4Address(); + IPv4Address dstIP = ((IPv4Layer*)m_PrevLayer)->getDstIPv4Address(); + + checksumRes = pcpp::computePseudoHdrChecksum( + (uint8_t*)tcpHdr, getDataLen(), IPAddress::IPv4AddressType, + PACKETPP_IPPROTO_TCP, srcIP, dstIP); + + PCPP_LOG_DEBUG("calculated IPv4 TCP checksum = 0x" + << std::uppercase << std::hex << checksumRes); + } else if (m_PrevLayer->getProtocol() == IPv6) { + IPv6Address srcIP = ((IPv6Layer*)m_PrevLayer)->getSrcIPv6Address(); + IPv6Address dstIP = ((IPv6Layer*)m_PrevLayer)->getDstIPv6Address(); + + checksumRes = computePseudoHdrChecksum( + (uint8_t*)tcpHdr, getDataLen(), IPAddress::IPv6AddressType, + PACKETPP_IPPROTO_TCP, srcIP, dstIP); + + PCPP_LOG_DEBUG("calculated IPv6 TCP checksum = 0xX" + << std::uppercase << std::hex << checksumRes); + } + } + + if (writeResultToPacket) + tcpHdr->headerChecksum = htobe16(checksumRes); + else + tcpHdr->headerChecksum = currChecksumValue; + + return checksumRes; } -TcpLayer::TcpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) -{ - m_Protocol = TCP; - m_NumOfTrailingBytes = 0; +void TcpLayer::initLayer() { + m_DataLen = sizeof(tcphdr); + m_Data = new uint8_t[m_DataLen]; + memset(m_Data, 0, m_DataLen); + m_Protocol = TCP; + m_NumOfTrailingBytes = 0; + getTcpHeader()->dataOffset = sizeof(tcphdr) / 4; } -TcpLayer::TcpLayer() -{ - initLayer(); +TcpLayer::TcpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, + Packet* packet) + : Layer(data, dataLen, prevLayer, packet) { + m_Protocol = TCP; + m_NumOfTrailingBytes = 0; } -TcpLayer::TcpLayer(uint16_t portSrc, uint16_t portDst) -{ - initLayer(); - getTcpHeader()->portDst = htobe16(portDst); - getTcpHeader()->portSrc = htobe16(portSrc); +TcpLayer::TcpLayer() { initLayer(); } + +TcpLayer::TcpLayer(uint16_t portSrc, uint16_t portDst) { + initLayer(); + getTcpHeader()->portDst = htobe16(portDst); + getTcpHeader()->portSrc = htobe16(portSrc); } -void TcpLayer::copyLayerData(const TcpLayer& other) -{ - m_OptionReader = other.m_OptionReader; - m_NumOfTrailingBytes = other.m_NumOfTrailingBytes; +void TcpLayer::copyLayerData(const TcpLayer& other) { + m_OptionReader = other.m_OptionReader; + m_NumOfTrailingBytes = other.m_NumOfTrailingBytes; } -TcpLayer::TcpLayer(const TcpLayer& other) : Layer(other) -{ - copyLayerData(other); +TcpLayer::TcpLayer(const TcpLayer& other) : Layer(other) { + copyLayerData(other); } -TcpLayer& TcpLayer::operator=(const TcpLayer& other) -{ - Layer::operator=(other); +TcpLayer& TcpLayer::operator=(const TcpLayer& other) { + Layer::operator=(other); - copyLayerData(other); + copyLayerData(other); - return *this; + return *this; } -void TcpLayer::parseNextLayer() -{ - size_t headerLen = getHeaderLen(); - if (m_DataLen <= headerLen) - return; - - uint8_t* payload = m_Data + headerLen; - size_t payloadLen = m_DataLen - headerLen; - uint16_t portDst = getDstPort(); - uint16_t portSrc = getSrcPort(); - - if (HttpMessage::isHttpPort(portDst) && HttpRequestFirstLine::parseMethod((char*)payload, payloadLen) != HttpRequestLayer::HttpMethodUnknown) - m_NextLayer = new HttpRequestLayer(payload, payloadLen, this, m_Packet); - else if (HttpMessage::isHttpPort(portSrc) && HttpResponseFirstLine::parseVersion((char*)payload, payloadLen) != HttpVersion::HttpVersionUnknown && !HttpResponseFirstLine::parseStatusCode((char*)payload, payloadLen).isUnsupportedCode()) - m_NextLayer = new HttpResponseLayer(payload, payloadLen, this, m_Packet); - else if (SSLLayer::IsSSLMessage(portSrc, portDst, payload, payloadLen)) - m_NextLayer = SSLLayer::createSSLMessage(payload, payloadLen, this, m_Packet); - else if (SipLayer::isSipPort(portDst) || SipLayer::isSipPort(portSrc)) - { - if (SipRequestFirstLine::parseMethod((char*)payload, payloadLen) != SipRequestLayer::SipMethodUnknown) - m_NextLayer = new SipRequestLayer(payload, payloadLen, this, m_Packet); - else if (SipResponseFirstLine::parseStatusCode((char*)payload, payloadLen) != SipResponseLayer::SipStatusCodeUnknown) - m_NextLayer = new SipResponseLayer(payload, payloadLen, this, m_Packet); - else - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - } - else if (BgpLayer::isBgpPort(portSrc, portDst)) - { - m_NextLayer = BgpLayer::parseBgpLayer(payload, payloadLen, this, m_Packet); - if (!m_NextLayer) - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - } - else if (SSHLayer::isSSHPort(portSrc, portDst)) - m_NextLayer = SSHLayer::createSSHMessage(payload, payloadLen, this, m_Packet); - else if (DnsLayer::isDataValid(payload, payloadLen, true) && (DnsLayer::isDnsPort(portDst) || DnsLayer::isDnsPort(portSrc))) - m_NextLayer = new DnsOverTcpLayer(payload, payloadLen, this, m_Packet); - else if (TelnetLayer::isDataValid(payload, payloadLen) && (TelnetLayer::isTelnetPort(portDst) || TelnetLayer::isTelnetPort(portSrc))) - m_NextLayer = new TelnetLayer(payload, payloadLen, this, m_Packet); - else if (FtpLayer::isFtpPort(portSrc) && FtpLayer::isDataValid(payload, payloadLen)) - m_NextLayer = new FtpResponseLayer(payload, payloadLen, this, m_Packet); - else if (FtpLayer::isFtpPort(portDst) && FtpLayer::isDataValid(payload, payloadLen)) - m_NextLayer = new FtpRequestLayer(payload, payloadLen, this, m_Packet); - else if (FtpLayer::isFtpDataPort(portSrc) || FtpLayer::isFtpDataPort(portDst)) - m_NextLayer = new FtpDataLayer(payload, payloadLen, this, m_Packet); - else if (SomeIpLayer::isSomeIpPort(portSrc) || SomeIpLayer::isSomeIpPort(portDst)) - m_NextLayer = SomeIpLayer::parseSomeIpLayer(payload, payloadLen, this, m_Packet); - else if (TpktLayer::isDataValid(payload, payloadLen) && TpktLayer::isTpktPort(portSrc, portDst)) - m_NextLayer = new TpktLayer(payload, payloadLen, this, m_Packet); - else - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); +void TcpLayer::parseNextLayer() { + size_t headerLen = getHeaderLen(); + if (m_DataLen <= headerLen) + return; + + uint8_t* payload = m_Data + headerLen; + size_t payloadLen = m_DataLen - headerLen; + uint16_t portDst = getDstPort(); + uint16_t portSrc = getSrcPort(); + + if (HttpMessage::isHttpPort(portDst) && + HttpRequestFirstLine::parseMethod((char*)payload, payloadLen) != + HttpRequestLayer::HttpMethodUnknown) + m_NextLayer = new HttpRequestLayer(payload, payloadLen, this, m_Packet); + else if (HttpMessage::isHttpPort(portSrc) && + HttpResponseFirstLine::parseVersion((char*)payload, payloadLen) != + HttpVersion::HttpVersionUnknown && + !HttpResponseFirstLine::parseStatusCode((char*)payload, payloadLen) + .isUnsupportedCode()) + m_NextLayer = new HttpResponseLayer(payload, payloadLen, this, m_Packet); + else if (SSLLayer::IsSSLMessage(portSrc, portDst, payload, payloadLen)) + m_NextLayer = + SSLLayer::createSSLMessage(payload, payloadLen, this, m_Packet); + else if (SipLayer::isSipPort(portDst) || SipLayer::isSipPort(portSrc)) { + if (SipRequestFirstLine::parseMethod((char*)payload, payloadLen) != + SipRequestLayer::SipMethodUnknown) + m_NextLayer = new SipRequestLayer(payload, payloadLen, this, m_Packet); + else if (SipResponseFirstLine::parseStatusCode((char*)payload, + payloadLen) != + SipResponseLayer::SipStatusCodeUnknown) + m_NextLayer = new SipResponseLayer(payload, payloadLen, this, m_Packet); + else + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + } else if (BgpLayer::isBgpPort(portSrc, portDst)) { + m_NextLayer = BgpLayer::parseBgpLayer(payload, payloadLen, this, m_Packet); + if (!m_NextLayer) + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + } else if (SSHLayer::isSSHPort(portSrc, portDst)) + m_NextLayer = + SSHLayer::createSSHMessage(payload, payloadLen, this, m_Packet); + else if (DnsLayer::isDataValid(payload, payloadLen, true) && + (DnsLayer::isDnsPort(portDst) || DnsLayer::isDnsPort(portSrc))) + m_NextLayer = new DnsOverTcpLayer(payload, payloadLen, this, m_Packet); + else if (TelnetLayer::isDataValid(payload, payloadLen) && + (TelnetLayer::isTelnetPort(portDst) || + TelnetLayer::isTelnetPort(portSrc))) + m_NextLayer = new TelnetLayer(payload, payloadLen, this, m_Packet); + else if (FtpLayer::isFtpPort(portSrc) && + FtpLayer::isDataValid(payload, payloadLen)) + m_NextLayer = new FtpResponseLayer(payload, payloadLen, this, m_Packet); + else if (FtpLayer::isFtpPort(portDst) && + FtpLayer::isDataValid(payload, payloadLen)) + m_NextLayer = new FtpRequestLayer(payload, payloadLen, this, m_Packet); + else if (FtpLayer::isFtpDataPort(portSrc) || FtpLayer::isFtpDataPort(portDst)) + m_NextLayer = new FtpDataLayer(payload, payloadLen, this, m_Packet); + else if (SomeIpLayer::isSomeIpPort(portSrc) || + SomeIpLayer::isSomeIpPort(portDst)) + m_NextLayer = + SomeIpLayer::parseSomeIpLayer(payload, payloadLen, this, m_Packet); + else if (TpktLayer::isDataValid(payload, payloadLen) && + TpktLayer::isTpktPort(portSrc, portDst)) + m_NextLayer = new TpktLayer(payload, payloadLen, this, m_Packet); + else + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); } -void TcpLayer::computeCalculateFields() -{ - tcphdr* tcpHdr = getTcpHeader(); +void TcpLayer::computeCalculateFields() { + tcphdr* tcpHdr = getTcpHeader(); - tcpHdr->dataOffset = getHeaderLen() >> 2; - calculateChecksum(true); + tcpHdr->dataOffset = getHeaderLen() >> 2; + calculateChecksum(true); } -std::string TcpLayer::toString() const -{ - tcphdr* hdr = getTcpHeader(); - std::string result = "TCP Layer, "; - if (hdr->synFlag) - { - if (hdr->ackFlag) - result += "[SYN, ACK], "; - else - result += "[SYN], "; - } - else if (hdr->finFlag) - { - if (hdr->ackFlag) - result += "[FIN, ACK], "; - else - result += "[FIN], "; - } - else if (hdr->ackFlag) - result += "[ACK], "; - - std::ostringstream srcPortStream; - srcPortStream << getSrcPort(); - std::ostringstream dstPortStream; - dstPortStream << getDstPort(); - result += "Src port: " + srcPortStream.str() + ", Dst port: " + dstPortStream.str(); - - return result; +std::string TcpLayer::toString() const { + tcphdr* hdr = getTcpHeader(); + std::string result = "TCP Layer, "; + if (hdr->synFlag) { + if (hdr->ackFlag) + result += "[SYN, ACK], "; + else + result += "[SYN], "; + } else if (hdr->finFlag) { + if (hdr->ackFlag) + result += "[FIN, ACK], "; + else + result += "[FIN], "; + } else if (hdr->ackFlag) + result += "[ACK], "; + + std::ostringstream srcPortStream; + srcPortStream << getSrcPort(); + std::ostringstream dstPortStream; + dstPortStream << getDstPort(); + result += + "Src port: " + srcPortStream.str() + ", Dst port: " + dstPortStream.str(); + + return result; } } // namespace pcpp diff --git a/Packet++/src/TcpReassembly.cpp b/Packet++/src/TcpReassembly.cpp index 9642fb6f08..3b5f5599d5 100644 --- a/Packet++/src/TcpReassembly.cpp +++ b/Packet++/src/TcpReassembly.cpp @@ -1,772 +1,845 @@ #define LOG_MODULE PacketLogModuleTcpReassembly #include "TcpReassembly.h" -#include "TcpLayer.h" +#include "EndianPortable.h" #include "IPLayer.h" -#include "PacketUtils.h" #include "Logger.h" +#include "PacketUtils.h" +#include "TcpLayer.h" +#include "TimespecTimeval.h" #include #include -#include "EndianPortable.h" -#include "TimespecTimeval.h" #ifdef _MSC_VER #include #endif #define PURGE_FREQ_SECS 1 -#define SEQ_LT(a,b) ((int32_t)((a)-(b)) < 0) -#define SEQ_LEQ(a,b) ((int32_t)((a)-(b)) <= 0) -#define SEQ_GT(a,b) ((int32_t)((a)-(b)) > 0) -#define SEQ_GEQ(a,b) ((int32_t)((a)-(b)) >= 0) +#define SEQ_LT(a, b) ((int32_t)((a) - (b)) < 0) +#define SEQ_LEQ(a, b) ((int32_t)((a) - (b)) <= 0) +#define SEQ_GT(a, b) ((int32_t)((a) - (b)) > 0) +#define SEQ_GEQ(a, b) ((int32_t)((a) - (b)) >= 0) -namespace pcpp -{ +namespace pcpp { -static timeval timespecToTimeval(const timespec& in) -{ - timeval out; - TIMESPEC_TO_TIMEVAL(&out, &in); - return out; +static timeval timespecToTimeval(const timespec& in) { + timeval out; + TIMESPEC_TO_TIMEVAL(&out, &in); + return out; } - -TcpReassembly::TcpReassembly(OnTcpMessageReady onMessageReadyCallback, void* userCookie, OnTcpConnectionStart onConnectionStartCallback, OnTcpConnectionEnd onConnectionEndCallback, const TcpReassemblyConfiguration &config) -{ - m_OnMessageReadyCallback = onMessageReadyCallback; - m_UserCookie = userCookie; - m_OnConnStart = onConnectionStartCallback; - m_OnConnEnd = onConnectionEndCallback; - m_ClosedConnectionDelay = (config.closedConnectionDelay > 0) ? config.closedConnectionDelay : 5; - m_RemoveConnInfo = config.removeConnInfo; - m_MaxNumToClean = (config.removeConnInfo == true && config.maxNumToClean == 0) ? 30 : config.maxNumToClean; - m_MaxOutOfOrderFragments = config.maxOutOfOrderFragments; - m_PurgeTimepoint = time(nullptr) + PURGE_FREQ_SECS; - m_EnableBaseBufferClearCondition = config.enableBaseBufferClearCondition; +TcpReassembly::TcpReassembly(OnTcpMessageReady onMessageReadyCallback, + void* userCookie, + OnTcpConnectionStart onConnectionStartCallback, + OnTcpConnectionEnd onConnectionEndCallback, + const TcpReassemblyConfiguration& config) { + m_OnMessageReadyCallback = onMessageReadyCallback; + m_UserCookie = userCookie; + m_OnConnStart = onConnectionStartCallback; + m_OnConnEnd = onConnectionEndCallback; + m_ClosedConnectionDelay = + (config.closedConnectionDelay > 0) ? config.closedConnectionDelay : 5; + m_RemoveConnInfo = config.removeConnInfo; + m_MaxNumToClean = (config.removeConnInfo == true && config.maxNumToClean == 0) + ? 30 + : config.maxNumToClean; + m_MaxOutOfOrderFragments = config.maxOutOfOrderFragments; + m_PurgeTimepoint = time(nullptr) + PURGE_FREQ_SECS; + m_EnableBaseBufferClearCondition = config.enableBaseBufferClearCondition; } - -TcpReassembly::ReassemblyStatus TcpReassembly::reassemblePacket(Packet& tcpData) -{ - // automatic cleanup - if (m_RemoveConnInfo == true) - { - if (time(nullptr) >= m_PurgeTimepoint) - { - purgeClosedConnections(); - m_PurgeTimepoint = time(nullptr) + PURGE_FREQ_SECS; - } - } - - - // calculate packet's source and dest IP address - IPAddress srcIP, dstIP; - - if (tcpData.isPacketOfType(IP)) - { - const IPLayer* ipLayer = tcpData.getLayerOfType(); - srcIP = ipLayer->getSrcIPAddress(); - dstIP = ipLayer->getDstIPAddress(); - } - else - return NonIpPacket; - - // in real traffic the IP addresses cannot be an unspecified - if (!srcIP.isValid() || !dstIP.isValid()) - return NonIpPacket; - - - // Ignore non-TCP packets - TcpLayer* tcpLayer = tcpData.getLayerOfType(true); // lookup in reverse order - if (tcpLayer == nullptr) - { - return NonTcpPacket; - } - - // Ignore the packet if it's an ICMP packet that has a TCP layer - // Several ICMP messages (like "destination unreachable") have TCP data as part of the ICMP message. - // This is not real TCP data and packet can be ignored - if (tcpData.isPacketOfType(ICMP)) - { - PCPP_LOG_DEBUG("Packet is of type ICMP so TCP data is probably part of the ICMP message. Ignoring this packet"); - return NonTcpPacket; - } - - ReassemblyStatus status = TcpMessageHandled; - - // set the TCP payload size - size_t tcpPayloadSize = tcpLayer->getLayerPayloadSize(); - - // calculate if this packet has FIN or RST flags - bool isFin = (tcpLayer->getTcpHeader()->finFlag == 1); - bool isRst = (tcpLayer->getTcpHeader()->rstFlag == 1); - bool isFinOrRst = isFin || isRst; - - // ignore ACK packets or TCP packets with no payload (except for SYN, FIN or RST packets which we'll later need) - if (tcpPayloadSize == 0 && tcpLayer->getTcpHeader()->synFlag == 0 && !isFinOrRst) - { - return Ignore_PacketWithNoData; - } - - TcpReassemblyData* tcpReassemblyData = nullptr; - - // calculate flow key for this packet - uint32_t flowKey = hash5Tuple(&tcpData); - - // time stamp for this packet - timeval currTime = timespecToTimeval(tcpData.getRawPacket()->getPacketTimeStamp()); - - // find the connection in the connection map - ConnectionList::iterator iter = m_ConnectionList.find(flowKey); - - if (iter == m_ConnectionList.end()) - { - // if it's a packet of a new connection, create a TcpReassemblyData object and add it to the active connection list - std::pair pair = m_ConnectionList.insert(std::make_pair(flowKey, TcpReassemblyData())); - tcpReassemblyData = &pair.first->second; - tcpReassemblyData->connData.srcIP = srcIP; - tcpReassemblyData->connData.dstIP = dstIP; - tcpReassemblyData->connData.srcPort = tcpLayer->getSrcPort(); - tcpReassemblyData->connData.dstPort = tcpLayer->getDstPort(); - tcpReassemblyData->connData.flowKey = flowKey; - tcpReassemblyData->connData.setStartTime(currTime); - - m_ConnectionInfo[flowKey] = tcpReassemblyData->connData; - - // fire connection start callback - if (m_OnConnStart != nullptr) - m_OnConnStart(tcpReassemblyData->connData, m_UserCookie); - } - else // connection already exists - { - // if this packet belongs to a connection that was already closed (for example: data packet that comes after FIN), ignore it. - if (iter->second.closed) - { - PCPP_LOG_DEBUG("Ignoring packet of already closed flow [0x" << std::hex << flowKey << "]"); - return Ignore_PacketOfClosedFlow; - } - - tcpReassemblyData = &iter->second; - - if (currTime.tv_sec > tcpReassemblyData->connData.endTime.tv_sec) - { - tcpReassemblyData->connData.setEndTime(currTime); - m_ConnectionInfo[flowKey].setEndTime(currTime); - } - else if (currTime.tv_sec == tcpReassemblyData->connData.endTime.tv_sec) - { - if (currTime.tv_usec > tcpReassemblyData->connData.endTime.tv_usec) - { - tcpReassemblyData->connData.setEndTime(currTime); - m_ConnectionInfo[flowKey].setEndTime(currTime); - } - } - } - - timeval timestampOfTheReceivedPacket = currTime; - int8_t sideIndex = -1; - bool first = false; - - // calculate packet's source port - uint16_t srcPort = tcpLayer->getTcpHeader()->portSrc; - - // if this is a new connection and it's the first packet we see on that connection - if (tcpReassemblyData->numOfSides == 0) - { - PCPP_LOG_DEBUG("Setting side for new connection"); - - // open the first side of the connection, side index is 0 - sideIndex = 0; - tcpReassemblyData->twoSides[sideIndex].srcIP = srcIP; - tcpReassemblyData->twoSides[sideIndex].srcPort = srcPort; - tcpReassemblyData->numOfSides++; - first = true; - } - // if there is already one side in this connection (which will be at side index 0) - else if (tcpReassemblyData->numOfSides == 1) - { - // check if packet belongs to that side - if (tcpReassemblyData->twoSides[0].srcPort == srcPort && tcpReassemblyData->twoSides[0].srcIP == srcIP) - { - sideIndex = 0; - } - else - { - // this means packet belong to the second side which doesn't yet exist. Open a second side with side index 1 - PCPP_LOG_DEBUG("Setting second side of a connection"); - sideIndex = 1; - tcpReassemblyData->twoSides[sideIndex].srcIP = srcIP; - tcpReassemblyData->twoSides[sideIndex].srcPort = srcPort; - tcpReassemblyData->numOfSides++; - first = true; - } - } - // if there are already 2 sides open for this connection - else if (tcpReassemblyData->numOfSides == 2) - { - // check if packet matches side 0 - if (tcpReassemblyData->twoSides[0].srcPort == srcPort && tcpReassemblyData->twoSides[0].srcIP == srcIP) - { - sideIndex = 0; - } - // check if packet matches side 1 - else if (tcpReassemblyData->twoSides[1].srcPort == srcPort && tcpReassemblyData->twoSides[1].srcIP == srcIP) - { - sideIndex = 1; - } - // packet doesn't match either side. This case doesn't make sense but it's handled anyway. Packet will be ignored - else - { - PCPP_LOG_ERROR("Error occurred - packet doesn't match either side of the connection!!"); - return Error_PacketDoesNotMatchFlow; - } - } - // there are more than 2 side - this case doesn't make sense and shouldn't happen, but handled anyway. Packet will be ignored - else - { - PCPP_LOG_ERROR("Error occurred - connection has more than 2 sides!!"); - return Error_PacketDoesNotMatchFlow; - } - - // if this side already got FIN or RST packet before, ignore this packet as this side is considered closed - if (tcpReassemblyData->twoSides[sideIndex].gotFinOrRst) - { - PCPP_LOG_DEBUG("Got a packet after FIN or RST were already seen on this side (" << sideIndex << "). Ignoring this packet"); - return Ignore_PacketOfClosedFlow; - } - - // handle FIN/RST packets that don't contain additional TCP data - if (isFinOrRst && tcpPayloadSize == 0) - { - PCPP_LOG_DEBUG("Got FIN or RST packet without data on side " << sideIndex); - - handleFinOrRst(tcpReassemblyData, sideIndex, flowKey, isRst); - return FIN_RSTWithNoData; - } - - // check if this packet contains data from a different side than the side seen before. - // If this is the case then treat the out-of-order packet list as missing data and send them to the user (callback) together with an indication that some data was missing. - // Why? because a new packet from the other side means the previous message was probably already received and a new message is starting. - // In this case out-of-order packets are probably actually missing data - // For example: let's assume these are HTTP messages. If we're seeing the first packet of a response this means the server has already received the full request and is now starting - // to send the response. So if we still have out-of-order packets from the request it probably means that some packets were lost during the capture. So we don't expect the client to - // continue sending packets of the previous request, so we'll treat the out-of-order packets as missing data - // - // I'm aware that there are edge cases where the situation I described above is not true, but at some point we must clean the out-of-order packet list to avoid memory leak. - // I decided to do what Wireshark does and clean this list when starting to see a message from the other side - - // Since there are instances where this buffer clear condition can lead to declaration of excessive missing packets. Hence user should have a config file parameter - // to disable this and purely rely on max buffer size condition. As none of them are perfect solutions this will give user a little more control over it. - - if (m_EnableBaseBufferClearCondition && !first && tcpPayloadSize > 0 && tcpReassemblyData->prevSide != -1 && tcpReassemblyData->prevSide != sideIndex && - tcpReassemblyData->twoSides[tcpReassemblyData->prevSide].tcpFragmentList.size() > 0) - { - PCPP_LOG_DEBUG("Seeing a first data packet from a different side. Previous side was " << tcpReassemblyData->prevSide << ", current side is " << sideIndex); - checkOutOfOrderFragments(tcpReassemblyData, tcpReassemblyData->prevSide, true); - } - tcpReassemblyData->prevSide = sideIndex; - - // extract sequence value from packet - uint32_t sequence = be32toh(tcpLayer->getTcpHeader()->sequenceNumber); - - // if it's the first packet we see on this side of the connection - if (first) - { - PCPP_LOG_DEBUG("First data from this side of the connection"); - - // set initial sequence - tcpReassemblyData->twoSides[sideIndex].sequence = sequence + tcpPayloadSize; - if (tcpLayer->getTcpHeader()->synFlag != 0) - tcpReassemblyData->twoSides[sideIndex].sequence++; - - // send data to the callback - if (tcpPayloadSize != 0 && m_OnMessageReadyCallback != nullptr) - { - TcpStreamData streamData(tcpLayer->getLayerPayload(), tcpPayloadSize, 0, tcpReassemblyData->connData, timestampOfTheReceivedPacket); - m_OnMessageReadyCallback(sideIndex, streamData, m_UserCookie); - } - status = TcpMessageHandled; - - // handle case where this packet is FIN or RST (although it's unlikely) - if (isFinOrRst) - handleFinOrRst(tcpReassemblyData, sideIndex, flowKey, isRst); - - // return - nothing else to do here - return status; - } - - // if packet sequence is smaller than expected - this means that part or all of the TCP data is being re-transmitted - if (SEQ_LT(sequence, tcpReassemblyData->twoSides[sideIndex].sequence)) - { - PCPP_LOG_DEBUG("Found new data with the sequence lower than expected"); - - // calculate the sequence after this packet to see if this TCP payload contains also new data - uint32_t newSequence = sequence + tcpPayloadSize; - - // this means that some of payload is new - if (SEQ_GT(newSequence, tcpReassemblyData->twoSides[sideIndex].sequence)) - { - // calculate the size of the new data - uint32_t newLength = tcpReassemblyData->twoSides[sideIndex].sequence - sequence; - - PCPP_LOG_DEBUG("Although sequence is lower than expected payload is long enough to contain new data. Calling the callback with the new data"); - - // update the sequence for this side to include the new data that was seen - tcpReassemblyData->twoSides[sideIndex].sequence += tcpPayloadSize - newLength; - - // send only the new data to the callback - if (m_OnMessageReadyCallback != nullptr) - { - TcpStreamData streamData(tcpLayer->getLayerPayload() + newLength, tcpPayloadSize - newLength, 0, tcpReassemblyData->connData, timestampOfTheReceivedPacket); - m_OnMessageReadyCallback(sideIndex, streamData, m_UserCookie); - } - status = TcpMessageHandled; - } - else - { - status = Ignore_Retransimission; - } - - // handle case where this packet is FIN or RST - if (isFinOrRst) - handleFinOrRst(tcpReassemblyData, sideIndex, flowKey, isRst); - - // return - nothing else to do here - return status; - } - - // if packet sequence is exactly as expected - this is the "good" case and the most common one - else if (sequence == tcpReassemblyData->twoSides[sideIndex].sequence) - { - // if TCP data size is 0 - nothing to do - if (tcpPayloadSize == 0) - { - PCPP_LOG_DEBUG("Payload length is 0, doing nothing"); - - // handle case where this packet is FIN or RST - if (isFinOrRst) - { - handleFinOrRst(tcpReassemblyData, sideIndex, flowKey, isRst); - status = FIN_RSTWithNoData; - } - else - { - status = Ignore_PacketWithNoData; - } - - return status; - } - - PCPP_LOG_DEBUG("Found new data with expected sequence. Calling the callback"); - - // update the sequence for this side to include TCP data from this packet - tcpReassemblyData->twoSides[sideIndex].sequence += tcpPayloadSize; - - // if this is a SYN packet - add +1 to the sequence - if (tcpLayer->getTcpHeader()->synFlag != 0) - tcpReassemblyData->twoSides[sideIndex].sequence++; - - // send the data to the callback - if (m_OnMessageReadyCallback != nullptr) - { - TcpStreamData streamData(tcpLayer->getLayerPayload(), tcpPayloadSize, 0, tcpReassemblyData->connData,timestampOfTheReceivedPacket); - m_OnMessageReadyCallback(sideIndex, streamData, m_UserCookie); - } - status = TcpMessageHandled; - - // now that we've seen new data, go over the list of out-of-order packets and see if one or more of them fits now - checkOutOfOrderFragments(tcpReassemblyData, sideIndex, false); - - // handle case where this packet is FIN or RST - if (isFinOrRst) - handleFinOrRst(tcpReassemblyData, sideIndex, flowKey, isRst); - - // return - nothing else to do here - return status; - } - - // this case means sequence size of the packet is higher than expected which means the packet is out-of-order or some packets were lost (missing data). - // we don't know which of the 2 cases it is at this point so we just add this data to the out-of-order packet list - else - { - // if TCP data size is 0 - nothing to do - if (tcpPayloadSize == 0) - { - PCPP_LOG_DEBUG("Payload length is 0, doing nothing"); - - // handle case where this packet is FIN or RST - if (isFinOrRst) - { - handleFinOrRst(tcpReassemblyData, sideIndex, flowKey, isRst); - status = FIN_RSTWithNoData; - } - else - { - status = Ignore_PacketWithNoData; - } - - return status; - } - - // create a new TcpFragment, copy the TCP data to it and add this packet to the the out-of-order packet list - TcpFragment* newTcpFrag = new TcpFragment(); - newTcpFrag->data = new uint8_t[tcpPayloadSize]; - newTcpFrag->dataLength = tcpPayloadSize; - newTcpFrag->sequence = sequence; - newTcpFrag->timestamp = timestampOfTheReceivedPacket; - memcpy(newTcpFrag->data, tcpLayer->getLayerPayload(), tcpPayloadSize); - tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.pushBack(newTcpFrag); - - PCPP_LOG_DEBUG("Found out-of-order packet and added a new TCP fragment with size " << tcpPayloadSize << " to the out-of-order list of side " << sideIndex); - status = OutOfOrderTcpMessageBuffered; - - // check if we've stored too many out-of-order fragments; if so, consider missing packets lost and - // continue processing until the number of stored fragments is lower than the acceptable limit again - if (m_MaxOutOfOrderFragments > 0 && tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.size() > m_MaxOutOfOrderFragments) - { - checkOutOfOrderFragments(tcpReassemblyData, sideIndex, false); - } - - // handle case where this packet is FIN or RST - if (isFinOrRst) - { - handleFinOrRst(tcpReassemblyData, sideIndex, flowKey, isRst); - } - - return status; - } +TcpReassembly::ReassemblyStatus +TcpReassembly::reassemblePacket(Packet& tcpData) { + // automatic cleanup + if (m_RemoveConnInfo == true) { + if (time(nullptr) >= m_PurgeTimepoint) { + purgeClosedConnections(); + m_PurgeTimepoint = time(nullptr) + PURGE_FREQ_SECS; + } + } + + // calculate packet's source and dest IP address + IPAddress srcIP, dstIP; + + if (tcpData.isPacketOfType(IP)) { + const IPLayer* ipLayer = tcpData.getLayerOfType(); + srcIP = ipLayer->getSrcIPAddress(); + dstIP = ipLayer->getDstIPAddress(); + } else + return NonIpPacket; + + // in real traffic the IP addresses cannot be an unspecified + if (!srcIP.isValid() || !dstIP.isValid()) + return NonIpPacket; + + // Ignore non-TCP packets + TcpLayer* tcpLayer = + tcpData.getLayerOfType(true); // lookup in reverse order + if (tcpLayer == nullptr) { + return NonTcpPacket; + } + + // Ignore the packet if it's an ICMP packet that has a TCP layer + // Several ICMP messages (like "destination unreachable") have TCP data as + // part of the ICMP message. This is not real TCP data and packet can be + // ignored + if (tcpData.isPacketOfType(ICMP)) { + PCPP_LOG_DEBUG("Packet is of type ICMP so TCP data is probably part of the " + "ICMP message. Ignoring this packet"); + return NonTcpPacket; + } + + ReassemblyStatus status = TcpMessageHandled; + + // set the TCP payload size + size_t tcpPayloadSize = tcpLayer->getLayerPayloadSize(); + + // calculate if this packet has FIN or RST flags + bool isFin = (tcpLayer->getTcpHeader()->finFlag == 1); + bool isRst = (tcpLayer->getTcpHeader()->rstFlag == 1); + bool isFinOrRst = isFin || isRst; + + // ignore ACK packets or TCP packets with no payload (except for SYN, FIN or + // RST packets which we'll later need) + if (tcpPayloadSize == 0 && tcpLayer->getTcpHeader()->synFlag == 0 && + !isFinOrRst) { + return Ignore_PacketWithNoData; + } + + TcpReassemblyData* tcpReassemblyData = nullptr; + + // calculate flow key for this packet + uint32_t flowKey = hash5Tuple(&tcpData); + + // time stamp for this packet + timeval currTime = + timespecToTimeval(tcpData.getRawPacket()->getPacketTimeStamp()); + + // find the connection in the connection map + ConnectionList::iterator iter = m_ConnectionList.find(flowKey); + + if (iter == m_ConnectionList.end()) { + // if it's a packet of a new connection, create a TcpReassemblyData object + // and add it to the active connection list + std::pair pair = + m_ConnectionList.insert(std::make_pair(flowKey, TcpReassemblyData())); + tcpReassemblyData = &pair.first->second; + tcpReassemblyData->connData.srcIP = srcIP; + tcpReassemblyData->connData.dstIP = dstIP; + tcpReassemblyData->connData.srcPort = tcpLayer->getSrcPort(); + tcpReassemblyData->connData.dstPort = tcpLayer->getDstPort(); + tcpReassemblyData->connData.flowKey = flowKey; + tcpReassemblyData->connData.setStartTime(currTime); + + m_ConnectionInfo[flowKey] = tcpReassemblyData->connData; + + // fire connection start callback + if (m_OnConnStart != nullptr) + m_OnConnStart(tcpReassemblyData->connData, m_UserCookie); + } else // connection already exists + { + // if this packet belongs to a connection that was already closed (for + // example: data packet that comes after FIN), ignore it. + if (iter->second.closed) { + PCPP_LOG_DEBUG("Ignoring packet of already closed flow [0x" + << std::hex << flowKey << "]"); + return Ignore_PacketOfClosedFlow; + } + + tcpReassemblyData = &iter->second; + + if (currTime.tv_sec > tcpReassemblyData->connData.endTime.tv_sec) { + tcpReassemblyData->connData.setEndTime(currTime); + m_ConnectionInfo[flowKey].setEndTime(currTime); + } else if (currTime.tv_sec == tcpReassemblyData->connData.endTime.tv_sec) { + if (currTime.tv_usec > tcpReassemblyData->connData.endTime.tv_usec) { + tcpReassemblyData->connData.setEndTime(currTime); + m_ConnectionInfo[flowKey].setEndTime(currTime); + } + } + } + + timeval timestampOfTheReceivedPacket = currTime; + int8_t sideIndex = -1; + bool first = false; + + // calculate packet's source port + uint16_t srcPort = tcpLayer->getTcpHeader()->portSrc; + + // if this is a new connection and it's the first packet we see on that + // connection + if (tcpReassemblyData->numOfSides == 0) { + PCPP_LOG_DEBUG("Setting side for new connection"); + + // open the first side of the connection, side index is 0 + sideIndex = 0; + tcpReassemblyData->twoSides[sideIndex].srcIP = srcIP; + tcpReassemblyData->twoSides[sideIndex].srcPort = srcPort; + tcpReassemblyData->numOfSides++; + first = true; + } + // if there is already one side in this connection (which will be at side + // index 0) + else if (tcpReassemblyData->numOfSides == 1) { + // check if packet belongs to that side + if (tcpReassemblyData->twoSides[0].srcPort == srcPort && + tcpReassemblyData->twoSides[0].srcIP == srcIP) { + sideIndex = 0; + } else { + // this means packet belong to the second side which doesn't yet exist. + // Open a second side with side index 1 + PCPP_LOG_DEBUG("Setting second side of a connection"); + sideIndex = 1; + tcpReassemblyData->twoSides[sideIndex].srcIP = srcIP; + tcpReassemblyData->twoSides[sideIndex].srcPort = srcPort; + tcpReassemblyData->numOfSides++; + first = true; + } + } + // if there are already 2 sides open for this connection + else if (tcpReassemblyData->numOfSides == 2) { + // check if packet matches side 0 + if (tcpReassemblyData->twoSides[0].srcPort == srcPort && + tcpReassemblyData->twoSides[0].srcIP == srcIP) { + sideIndex = 0; + } + // check if packet matches side 1 + else if (tcpReassemblyData->twoSides[1].srcPort == srcPort && + tcpReassemblyData->twoSides[1].srcIP == srcIP) { + sideIndex = 1; + } + // packet doesn't match either side. This case doesn't make sense but it's + // handled anyway. Packet will be ignored + else { + PCPP_LOG_ERROR("Error occurred - packet doesn't match either side of the " + "connection!!"); + return Error_PacketDoesNotMatchFlow; + } + } + // there are more than 2 side - this case doesn't make sense and shouldn't + // happen, but handled anyway. Packet will be ignored + else { + PCPP_LOG_ERROR("Error occurred - connection has more than 2 sides!!"); + return Error_PacketDoesNotMatchFlow; + } + + // if this side already got FIN or RST packet before, ignore this packet as + // this side is considered closed + if (tcpReassemblyData->twoSides[sideIndex].gotFinOrRst) { + PCPP_LOG_DEBUG( + "Got a packet after FIN or RST were already seen on this side (" + << sideIndex << "). Ignoring this packet"); + return Ignore_PacketOfClosedFlow; + } + + // handle FIN/RST packets that don't contain additional TCP data + if (isFinOrRst && tcpPayloadSize == 0) { + PCPP_LOG_DEBUG("Got FIN or RST packet without data on side " << sideIndex); + + handleFinOrRst(tcpReassemblyData, sideIndex, flowKey, isRst); + return FIN_RSTWithNoData; + } + + // check if this packet contains data from a different side than the side seen + // before. If this is the case then treat the out-of-order packet list as + // missing data and send them to the user (callback) together with an + // indication that some data was missing. Why? because a new packet from the + // other side means the previous message was probably already received and a + // new message is starting. In this case out-of-order packets are probably + // actually missing data For example: let's assume these are HTTP messages. If + // we're seeing the first packet of a response this means the server has + // already received the full request and is now starting to send the response. + // So if we still have out-of-order packets from the request it probably means + // that some packets were lost during the capture. So we don't expect the + // client to continue sending packets of the previous request, so we'll treat + // the out-of-order packets as missing data + // + // I'm aware that there are edge cases where the situation I described above + // is not true, but at some point we must clean the out-of-order packet list + // to avoid memory leak. I decided to do what Wireshark does and clean this + // list when starting to see a message from the other side + + // Since there are instances where this buffer clear condition can lead to + // declaration of excessive missing packets. Hence user should have a config + // file parameter to disable this and purely rely on max buffer size + // condition. As none of them are perfect solutions this will give user a + // little more control over it. + + if (m_EnableBaseBufferClearCondition && !first && tcpPayloadSize > 0 && + tcpReassemblyData->prevSide != -1 && + tcpReassemblyData->prevSide != sideIndex && + tcpReassemblyData->twoSides[tcpReassemblyData->prevSide] + .tcpFragmentList.size() > 0) { + PCPP_LOG_DEBUG( + "Seeing a first data packet from a different side. Previous side was " + << tcpReassemblyData->prevSide << ", current side is " << sideIndex); + checkOutOfOrderFragments(tcpReassemblyData, tcpReassemblyData->prevSide, + true); + } + tcpReassemblyData->prevSide = sideIndex; + + // extract sequence value from packet + uint32_t sequence = be32toh(tcpLayer->getTcpHeader()->sequenceNumber); + + // if it's the first packet we see on this side of the connection + if (first) { + PCPP_LOG_DEBUG("First data from this side of the connection"); + + // set initial sequence + tcpReassemblyData->twoSides[sideIndex].sequence = sequence + tcpPayloadSize; + if (tcpLayer->getTcpHeader()->synFlag != 0) + tcpReassemblyData->twoSides[sideIndex].sequence++; + + // send data to the callback + if (tcpPayloadSize != 0 && m_OnMessageReadyCallback != nullptr) { + TcpStreamData streamData(tcpLayer->getLayerPayload(), tcpPayloadSize, 0, + tcpReassemblyData->connData, + timestampOfTheReceivedPacket); + m_OnMessageReadyCallback(sideIndex, streamData, m_UserCookie); + } + status = TcpMessageHandled; + + // handle case where this packet is FIN or RST (although it's unlikely) + if (isFinOrRst) + handleFinOrRst(tcpReassemblyData, sideIndex, flowKey, isRst); + + // return - nothing else to do here + return status; + } + + // if packet sequence is smaller than expected - this means that part or all + // of the TCP data is being re-transmitted + if (SEQ_LT(sequence, tcpReassemblyData->twoSides[sideIndex].sequence)) { + PCPP_LOG_DEBUG("Found new data with the sequence lower than expected"); + + // calculate the sequence after this packet to see if this TCP payload + // contains also new data + uint32_t newSequence = sequence + tcpPayloadSize; + + // this means that some of payload is new + if (SEQ_GT(newSequence, tcpReassemblyData->twoSides[sideIndex].sequence)) { + // calculate the size of the new data + uint32_t newLength = + tcpReassemblyData->twoSides[sideIndex].sequence - sequence; + + PCPP_LOG_DEBUG( + "Although sequence is lower than expected payload is long enough to " + "contain new data. Calling the callback with the new data"); + + // update the sequence for this side to include the new data that was seen + tcpReassemblyData->twoSides[sideIndex].sequence += + tcpPayloadSize - newLength; + + // send only the new data to the callback + if (m_OnMessageReadyCallback != nullptr) { + TcpStreamData streamData( + tcpLayer->getLayerPayload() + newLength, tcpPayloadSize - newLength, + 0, tcpReassemblyData->connData, timestampOfTheReceivedPacket); + m_OnMessageReadyCallback(sideIndex, streamData, m_UserCookie); + } + status = TcpMessageHandled; + } else { + status = Ignore_Retransimission; + } + + // handle case where this packet is FIN or RST + if (isFinOrRst) + handleFinOrRst(tcpReassemblyData, sideIndex, flowKey, isRst); + + // return - nothing else to do here + return status; + } + + // if packet sequence is exactly as expected - this is the "good" case and the + // most common one + else if (sequence == tcpReassemblyData->twoSides[sideIndex].sequence) { + // if TCP data size is 0 - nothing to do + if (tcpPayloadSize == 0) { + PCPP_LOG_DEBUG("Payload length is 0, doing nothing"); + + // handle case where this packet is FIN or RST + if (isFinOrRst) { + handleFinOrRst(tcpReassemblyData, sideIndex, flowKey, isRst); + status = FIN_RSTWithNoData; + } else { + status = Ignore_PacketWithNoData; + } + + return status; + } + + PCPP_LOG_DEBUG( + "Found new data with expected sequence. Calling the callback"); + + // update the sequence for this side to include TCP data from this packet + tcpReassemblyData->twoSides[sideIndex].sequence += tcpPayloadSize; + + // if this is a SYN packet - add +1 to the sequence + if (tcpLayer->getTcpHeader()->synFlag != 0) + tcpReassemblyData->twoSides[sideIndex].sequence++; + + // send the data to the callback + if (m_OnMessageReadyCallback != nullptr) { + TcpStreamData streamData(tcpLayer->getLayerPayload(), tcpPayloadSize, 0, + tcpReassemblyData->connData, + timestampOfTheReceivedPacket); + m_OnMessageReadyCallback(sideIndex, streamData, m_UserCookie); + } + status = TcpMessageHandled; + + // now that we've seen new data, go over the list of out-of-order packets + // and see if one or more of them fits now + checkOutOfOrderFragments(tcpReassemblyData, sideIndex, false); + + // handle case where this packet is FIN or RST + if (isFinOrRst) + handleFinOrRst(tcpReassemblyData, sideIndex, flowKey, isRst); + + // return - nothing else to do here + return status; + } + + // this case means sequence size of the packet is higher than expected which + // means the packet is out-of-order or some packets were lost (missing data). + // we don't know which of the 2 cases it is at this point so we just add this + // data to the out-of-order packet list + else { + // if TCP data size is 0 - nothing to do + if (tcpPayloadSize == 0) { + PCPP_LOG_DEBUG("Payload length is 0, doing nothing"); + + // handle case where this packet is FIN or RST + if (isFinOrRst) { + handleFinOrRst(tcpReassemblyData, sideIndex, flowKey, isRst); + status = FIN_RSTWithNoData; + } else { + status = Ignore_PacketWithNoData; + } + + return status; + } + + // create a new TcpFragment, copy the TCP data to it and add this packet to + // the the out-of-order packet list + TcpFragment* newTcpFrag = new TcpFragment(); + newTcpFrag->data = new uint8_t[tcpPayloadSize]; + newTcpFrag->dataLength = tcpPayloadSize; + newTcpFrag->sequence = sequence; + newTcpFrag->timestamp = timestampOfTheReceivedPacket; + memcpy(newTcpFrag->data, tcpLayer->getLayerPayload(), tcpPayloadSize); + tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.pushBack(newTcpFrag); + + PCPP_LOG_DEBUG( + "Found out-of-order packet and added a new TCP fragment with size " + << tcpPayloadSize << " to the out-of-order list of side " << sideIndex); + status = OutOfOrderTcpMessageBuffered; + + // check if we've stored too many out-of-order fragments; if so, consider + // missing packets lost and continue processing until the number of stored + // fragments is lower than the acceptable limit again + if (m_MaxOutOfOrderFragments > 0 && + tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.size() > + m_MaxOutOfOrderFragments) { + checkOutOfOrderFragments(tcpReassemblyData, sideIndex, false); + } + + // handle case where this packet is FIN or RST + if (isFinOrRst) { + handleFinOrRst(tcpReassemblyData, sideIndex, flowKey, isRst); + } + + return status; + } } -TcpReassembly::ReassemblyStatus TcpReassembly::reassemblePacket(RawPacket* tcpRawData) -{ - Packet parsedPacket(tcpRawData, false); - return reassemblePacket(parsedPacket); +TcpReassembly::ReassemblyStatus +TcpReassembly::reassemblePacket(RawPacket* tcpRawData) { + Packet parsedPacket(tcpRawData, false); + return reassemblePacket(parsedPacket); } -static std::string prepareMissingDataMessage(uint32_t missingDataLen) -{ - std::stringstream missingDataTextStream; - missingDataTextStream << '[' << missingDataLen << " bytes missing]"; - return missingDataTextStream.str(); +static std::string prepareMissingDataMessage(uint32_t missingDataLen) { + std::stringstream missingDataTextStream; + missingDataTextStream << '[' << missingDataLen << " bytes missing]"; + return missingDataTextStream.str(); } -void TcpReassembly::handleFinOrRst(TcpReassemblyData* tcpReassemblyData, int8_t sideIndex, uint32_t flowKey, bool isRst) -{ - // if this side already saw a FIN or RST packet, do nothing and return - if (tcpReassemblyData->twoSides[sideIndex].gotFinOrRst) - return; - - PCPP_LOG_DEBUG("Handling FIN or RST packet on side " << sideIndex); - - // set FIN/RST flag for this side - tcpReassemblyData->twoSides[sideIndex].gotFinOrRst = true; - - // check if the other side also sees FIN or RST packet. If so - just close the flow. Otherwise - clear the out-of-order packets for this side - int otherSideIndex = 1 - sideIndex; - if (tcpReassemblyData->twoSides[otherSideIndex].gotFinOrRst) - { - closeConnectionInternal(flowKey, TcpReassembly::TcpReassemblyConnectionClosedByFIN_RST); - return; - } - else - checkOutOfOrderFragments(tcpReassemblyData, sideIndex, true); - - // and if it's a rst, close the flow unilaterally - if(isRst) - closeConnectionInternal(flowKey, TcpReassembly::TcpReassemblyConnectionClosedByFIN_RST); +void TcpReassembly::handleFinOrRst(TcpReassemblyData* tcpReassemblyData, + int8_t sideIndex, uint32_t flowKey, + bool isRst) { + // if this side already saw a FIN or RST packet, do nothing and return + if (tcpReassemblyData->twoSides[sideIndex].gotFinOrRst) + return; + + PCPP_LOG_DEBUG("Handling FIN or RST packet on side " << sideIndex); + + // set FIN/RST flag for this side + tcpReassemblyData->twoSides[sideIndex].gotFinOrRst = true; + + // check if the other side also sees FIN or RST packet. If so - just close the + // flow. Otherwise - clear the out-of-order packets for this side + int otherSideIndex = 1 - sideIndex; + if (tcpReassemblyData->twoSides[otherSideIndex].gotFinOrRst) { + closeConnectionInternal( + flowKey, TcpReassembly::TcpReassemblyConnectionClosedByFIN_RST); + return; + } else + checkOutOfOrderFragments(tcpReassemblyData, sideIndex, true); + + // and if it's a rst, close the flow unilaterally + if (isRst) + closeConnectionInternal( + flowKey, TcpReassembly::TcpReassemblyConnectionClosedByFIN_RST); } -void TcpReassembly::checkOutOfOrderFragments(TcpReassemblyData* tcpReassemblyData, int8_t sideIndex, bool cleanWholeFragList) -{ - bool foundSomething = false; - - do - { - PCPP_LOG_DEBUG("Starting first iteration of checkOutOfOrderFragments - looking for fragments that match the current sequence or have smaller sequence"); - - int index = 0; - foundSomething = false; - - do - { - index = 0; - foundSomething = false; - - // first fragment list iteration - go over the whole fragment list and see if can find fragments that match the current sequence - // or have smaller sequence but have big enough payload to get new data - while (index < (int)tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.size()) - { - TcpFragment* curTcpFrag = tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.at(index); - - // if fragment sequence matches the current sequence - if (curTcpFrag->sequence == tcpReassemblyData->twoSides[sideIndex].sequence) - { - // update sequence - tcpReassemblyData->twoSides[sideIndex].sequence += curTcpFrag->dataLength; - if (curTcpFrag->data != nullptr) - { - PCPP_LOG_DEBUG("Found an out-of-order packet matching to the current sequence with size " << curTcpFrag->dataLength << " on side " << sideIndex << ". Pulling it out of the list and sending the data to the callback"); - - // send new data to callback - - if (m_OnMessageReadyCallback != nullptr) - { - TcpStreamData streamData(curTcpFrag->data, curTcpFrag->dataLength, 0, tcpReassemblyData->connData, curTcpFrag->timestamp); - m_OnMessageReadyCallback(sideIndex, streamData, m_UserCookie); - } - } - - - // remove fragment from list - tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.erase(tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.begin() + index); - - foundSomething = true; - - continue; - } - - // if fragment sequence has lower sequence than the current sequence - if (SEQ_LT(curTcpFrag->sequence, tcpReassemblyData->twoSides[sideIndex].sequence)) - { - // check if it still has new data - uint32_t newSequence = curTcpFrag->sequence + curTcpFrag->dataLength; - - // it has new data - if (SEQ_GT(newSequence, tcpReassemblyData->twoSides[sideIndex].sequence)) - { - // calculate the delta new data size - uint32_t newLength = tcpReassemblyData->twoSides[sideIndex].sequence - curTcpFrag->sequence; - - PCPP_LOG_DEBUG("Found a fragment in the out-of-order list which its sequence is lower than expected but its payload is long enough to contain new data. " - "Calling the callback with the new data. Fragment size is " << curTcpFrag->dataLength << " on side " << sideIndex << ", new data size is " << (int)(curTcpFrag->dataLength - newLength)); - - // update current sequence with the delta new data size - tcpReassemblyData->twoSides[sideIndex].sequence += curTcpFrag->dataLength - newLength; - - // send only the new data to the callback - if (m_OnMessageReadyCallback != nullptr) - { - TcpStreamData streamData(curTcpFrag->data + newLength, curTcpFrag->dataLength - newLength, 0, tcpReassemblyData->connData, curTcpFrag->timestamp); - m_OnMessageReadyCallback(sideIndex, streamData, m_UserCookie); - } - - foundSomething = true; - } - else - { - PCPP_LOG_DEBUG("Found a fragment in the out-of-order list which doesn't contain any new data, ignoring it. Fragment size is " << curTcpFrag->dataLength << " on side " << sideIndex); - } - - // delete fragment from list - tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.erase(tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.begin() + index); - - continue; - } - - //if got to here it means the fragment has higher sequence than current sequence, increment index and continue - index++; - } - - // if managed to find new segment, do the search all over again - } while (foundSomething); - - - // if got here it means we're left only with fragments that have higher sequence than current sequence. This means out-of-order packets or - // missing data. If we don't want to clear the frag list yet and the number of out of order fragments isn't above the configured limit, - // assume it's out-of-order and return - if (!cleanWholeFragList && (m_MaxOutOfOrderFragments == 0 || tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.size() <= m_MaxOutOfOrderFragments)) - { - return; - } - - PCPP_LOG_DEBUG("Starting second iteration of checkOutOfOrderFragments - handle missing data"); - - // second fragment list iteration - now we're left only with fragments that have higher sequence than current sequence. This means missing data. - // Search for the fragment with the closest sequence to the current one - - uint32_t closestSequence = 0xffffffff; - bool closestSequenceDefined = false; - int closestSequenceFragIndex = -1; - index = 0; - - while (index < (int)tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.size()) - { - // extract segment at current index - TcpFragment* curTcpFrag = tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.at(index); - - // check if its sequence is closer than current closest sequence - if (!closestSequenceDefined || SEQ_LT(curTcpFrag->sequence, closestSequence)) - { - closestSequence = curTcpFrag->sequence; - closestSequenceFragIndex = index; - closestSequenceDefined = true; - } - - index++; - } - - // this means fragment list is not empty at this stage - if (closestSequenceFragIndex > -1) - { - // get the fragment with the closest sequence - TcpFragment* curTcpFrag = tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.at(closestSequenceFragIndex); - - // calculate number of missing bytes - uint32_t missingDataLen = curTcpFrag->sequence - tcpReassemblyData->twoSides[sideIndex].sequence; - - // update sequence - tcpReassemblyData->twoSides[sideIndex].sequence = curTcpFrag->sequence + curTcpFrag->dataLength; - if (curTcpFrag->data != nullptr) - { - // send new data to callback - if (m_OnMessageReadyCallback != nullptr) - { - // prepare missing data text - std::string missingDataTextStr = prepareMissingDataMessage(missingDataLen); - - // add missing data text to the data that will be sent to the callback. This means that the data will look something like: - // "[xx bytes missing]" - std::vector dataWithMissingDataText; - dataWithMissingDataText.reserve(missingDataTextStr.length() + curTcpFrag->dataLength); - dataWithMissingDataText.insert(dataWithMissingDataText.end(), missingDataTextStr.begin(), missingDataTextStr.end()); - dataWithMissingDataText.insert(dataWithMissingDataText.end(), curTcpFrag->data, curTcpFrag->data + curTcpFrag->dataLength); - - //TcpStreamData streamData(curTcpFrag->data, curTcpFrag->dataLength, tcpReassemblyData->connData); - TcpStreamData streamData(&dataWithMissingDataText[0], dataWithMissingDataText.size(), missingDataLen, tcpReassemblyData->connData, curTcpFrag->timestamp); - m_OnMessageReadyCallback(sideIndex, streamData, m_UserCookie); - - PCPP_LOG_DEBUG("Found missing data on side " << sideIndex << ": " << missingDataLen << " byte are missing. Sending the closest fragment which is in size " << curTcpFrag->dataLength << " + missing text message which size is " << missingDataTextStr.length()); - } - } - - // remove fragment from list - tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.erase(tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.begin() + closestSequenceFragIndex); - - PCPP_LOG_DEBUG("Calling checkOutOfOrderFragments again from the start"); - - // call the method again from the start to do the whole search again (both iterations). - // the stop condition is when the list is empty (so closestSequenceFragIndex == -1) - foundSomething = true; - } - - } while (foundSomething); +void TcpReassembly::checkOutOfOrderFragments( + TcpReassemblyData* tcpReassemblyData, int8_t sideIndex, + bool cleanWholeFragList) { + bool foundSomething = false; + + do { + PCPP_LOG_DEBUG( + "Starting first iteration of checkOutOfOrderFragments - looking for " + "fragments that match the current sequence or have smaller sequence"); + + int index = 0; + foundSomething = false; + + do { + index = 0; + foundSomething = false; + + // first fragment list iteration - go over the whole fragment list and see + // if can find fragments that match the current sequence or have smaller + // sequence but have big enough payload to get new data + while ( + index < + (int)tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.size()) { + TcpFragment* curTcpFrag = + tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.at(index); + + // if fragment sequence matches the current sequence + if (curTcpFrag->sequence == + tcpReassemblyData->twoSides[sideIndex].sequence) { + // update sequence + tcpReassemblyData->twoSides[sideIndex].sequence += + curTcpFrag->dataLength; + if (curTcpFrag->data != nullptr) { + PCPP_LOG_DEBUG("Found an out-of-order packet matching to the " + "current sequence with size " + << curTcpFrag->dataLength << " on side " << sideIndex + << ". Pulling it out of the list and sending the " + "data to the callback"); + + // send new data to callback + + if (m_OnMessageReadyCallback != nullptr) { + TcpStreamData streamData(curTcpFrag->data, curTcpFrag->dataLength, + 0, tcpReassemblyData->connData, + curTcpFrag->timestamp); + m_OnMessageReadyCallback(sideIndex, streamData, m_UserCookie); + } + } + + // remove fragment from list + tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.erase( + tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.begin() + + index); + + foundSomething = true; + + continue; + } + + // if fragment sequence has lower sequence than the current sequence + if (SEQ_LT(curTcpFrag->sequence, + tcpReassemblyData->twoSides[sideIndex].sequence)) { + // check if it still has new data + uint32_t newSequence = curTcpFrag->sequence + curTcpFrag->dataLength; + + // it has new data + if (SEQ_GT(newSequence, + tcpReassemblyData->twoSides[sideIndex].sequence)) { + // calculate the delta new data size + uint32_t newLength = + tcpReassemblyData->twoSides[sideIndex].sequence - + curTcpFrag->sequence; + + PCPP_LOG_DEBUG( + "Found a fragment in the out-of-order list which its sequence " + "is lower than expected but its payload is long enough to " + "contain new data. " + "Calling the callback with the new data. Fragment size is " + << curTcpFrag->dataLength << " on side " << sideIndex + << ", new data size is " + << (int)(curTcpFrag->dataLength - newLength)); + + // update current sequence with the delta new data size + tcpReassemblyData->twoSides[sideIndex].sequence += + curTcpFrag->dataLength - newLength; + + // send only the new data to the callback + if (m_OnMessageReadyCallback != nullptr) { + TcpStreamData streamData(curTcpFrag->data + newLength, + curTcpFrag->dataLength - newLength, 0, + tcpReassemblyData->connData, + curTcpFrag->timestamp); + m_OnMessageReadyCallback(sideIndex, streamData, m_UserCookie); + } + + foundSomething = true; + } else { + PCPP_LOG_DEBUG( + "Found a fragment in the out-of-order list which doesn't " + "contain any new data, ignoring it. Fragment size is " + << curTcpFrag->dataLength << " on side " << sideIndex); + } + + // delete fragment from list + tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.erase( + tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.begin() + + index); + + continue; + } + + // if got to here it means the fragment has higher sequence than current + // sequence, increment index and continue + index++; + } + + // if managed to find new segment, do the search all over again + } while (foundSomething); + + // if got here it means we're left only with fragments that have higher + // sequence than current sequence. This means out-of-order packets or + // missing data. If we don't want to clear the frag list yet and the number + // of out of order fragments isn't above the configured limit, assume it's + // out-of-order and return + if (!cleanWholeFragList && + (m_MaxOutOfOrderFragments == 0 || + tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.size() <= + m_MaxOutOfOrderFragments)) { + return; + } + + PCPP_LOG_DEBUG("Starting second iteration of checkOutOfOrderFragments - " + "handle missing data"); + + // second fragment list iteration - now we're left only with fragments that + // have higher sequence than current sequence. This means missing data. + // Search for the fragment with the closest sequence to the current one + + uint32_t closestSequence = 0xffffffff; + bool closestSequenceDefined = false; + int closestSequenceFragIndex = -1; + index = 0; + + while (index < + (int)tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.size()) { + // extract segment at current index + TcpFragment* curTcpFrag = + tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.at(index); + + // check if its sequence is closer than current closest sequence + if (!closestSequenceDefined || + SEQ_LT(curTcpFrag->sequence, closestSequence)) { + closestSequence = curTcpFrag->sequence; + closestSequenceFragIndex = index; + closestSequenceDefined = true; + } + + index++; + } + + // this means fragment list is not empty at this stage + if (closestSequenceFragIndex > -1) { + // get the fragment with the closest sequence + TcpFragment* curTcpFrag = + tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.at( + closestSequenceFragIndex); + + // calculate number of missing bytes + uint32_t missingDataLen = curTcpFrag->sequence - + tcpReassemblyData->twoSides[sideIndex].sequence; + + // update sequence + tcpReassemblyData->twoSides[sideIndex].sequence = + curTcpFrag->sequence + curTcpFrag->dataLength; + if (curTcpFrag->data != nullptr) { + // send new data to callback + if (m_OnMessageReadyCallback != nullptr) { + // prepare missing data text + std::string missingDataTextStr = + prepareMissingDataMessage(missingDataLen); + + // add missing data text to the data that will be sent to the + // callback. This means that the data will look something like: + // "[xx bytes missing]" + std::vector dataWithMissingDataText; + dataWithMissingDataText.reserve(missingDataTextStr.length() + + curTcpFrag->dataLength); + dataWithMissingDataText.insert(dataWithMissingDataText.end(), + missingDataTextStr.begin(), + missingDataTextStr.end()); + dataWithMissingDataText.insert( + dataWithMissingDataText.end(), curTcpFrag->data, + curTcpFrag->data + curTcpFrag->dataLength); + + // TcpStreamData streamData(curTcpFrag->data, curTcpFrag->dataLength, + // tcpReassemblyData->connData); + TcpStreamData streamData(&dataWithMissingDataText[0], + dataWithMissingDataText.size(), + missingDataLen, tcpReassemblyData->connData, + curTcpFrag->timestamp); + m_OnMessageReadyCallback(sideIndex, streamData, m_UserCookie); + + PCPP_LOG_DEBUG("Found missing data on side " + << sideIndex << ": " << missingDataLen + << " byte are missing. Sending the closest fragment " + "which is in size " + << curTcpFrag->dataLength + << " + missing text message which size is " + << missingDataTextStr.length()); + } + } + + // remove fragment from list + tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.erase( + tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.begin() + + closestSequenceFragIndex); + + PCPP_LOG_DEBUG("Calling checkOutOfOrderFragments again from the start"); + + // call the method again from the start to do the whole search again (both + // iterations). the stop condition is when the list is empty (so + // closestSequenceFragIndex == -1) + foundSomething = true; + } + + } while (foundSomething); } -void TcpReassembly::closeConnection(uint32_t flowKey) -{ - closeConnectionInternal(flowKey, TcpReassembly::TcpReassemblyConnectionClosedManually); +void TcpReassembly::closeConnection(uint32_t flowKey) { + closeConnectionInternal(flowKey, + TcpReassembly::TcpReassemblyConnectionClosedManually); } -void TcpReassembly::closeConnectionInternal(uint32_t flowKey, ConnectionEndReason reason) -{ - ConnectionList::iterator iter = m_ConnectionList.find(flowKey); - if (iter == m_ConnectionList.end()) - { - PCPP_LOG_ERROR("Cannot close flow with key 0x" << std::uppercase << std::hex << flowKey << ": cannot find flow"); - return; - } +void TcpReassembly::closeConnectionInternal(uint32_t flowKey, + ConnectionEndReason reason) { + ConnectionList::iterator iter = m_ConnectionList.find(flowKey); + if (iter == m_ConnectionList.end()) { + PCPP_LOG_ERROR("Cannot close flow with key 0x" << std::uppercase << std::hex + << flowKey + << ": cannot find flow"); + return; + } - TcpReassemblyData& tcpReassemblyData = iter->second; + TcpReassemblyData& tcpReassemblyData = iter->second; - if (tcpReassemblyData.closed) // the connection is already closed - return; + if (tcpReassemblyData.closed) // the connection is already closed + return; - PCPP_LOG_DEBUG("Closing connection with flow key 0x" << std::hex << flowKey); + PCPP_LOG_DEBUG("Closing connection with flow key 0x" << std::hex << flowKey); - PCPP_LOG_DEBUG("Calling checkOutOfOrderFragments on side 0"); - checkOutOfOrderFragments(&tcpReassemblyData, 0, true); + PCPP_LOG_DEBUG("Calling checkOutOfOrderFragments on side 0"); + checkOutOfOrderFragments(&tcpReassemblyData, 0, true); - PCPP_LOG_DEBUG("Calling checkOutOfOrderFragments on side 1"); - checkOutOfOrderFragments(&tcpReassemblyData, 1, true); + PCPP_LOG_DEBUG("Calling checkOutOfOrderFragments on side 1"); + checkOutOfOrderFragments(&tcpReassemblyData, 1, true); - if (m_OnConnEnd != nullptr) - m_OnConnEnd(tcpReassemblyData.connData, reason, m_UserCookie); + if (m_OnConnEnd != nullptr) + m_OnConnEnd(tcpReassemblyData.connData, reason, m_UserCookie); - tcpReassemblyData.closed = true; // mark the connection as closed - insertIntoCleanupList(flowKey); + tcpReassemblyData.closed = true; // mark the connection as closed + insertIntoCleanupList(flowKey); - PCPP_LOG_DEBUG("Connection with flow key 0x" << std::hex << flowKey << " is closed"); + PCPP_LOG_DEBUG("Connection with flow key 0x" << std::hex << flowKey + << " is closed"); } -void TcpReassembly::closeAllConnections() -{ - PCPP_LOG_DEBUG("Closing all flows"); +void TcpReassembly::closeAllConnections() { + PCPP_LOG_DEBUG("Closing all flows"); - ConnectionList::iterator iter = m_ConnectionList.begin(), iterEnd = m_ConnectionList.end(); - for (; iter != iterEnd; ++iter) - { - TcpReassemblyData& tcpReassemblyData = iter->second; + ConnectionList::iterator iter = m_ConnectionList.begin(), + iterEnd = m_ConnectionList.end(); + for (; iter != iterEnd; ++iter) { + TcpReassemblyData& tcpReassemblyData = iter->second; - if (tcpReassemblyData.closed) // the connection is already closed, skip it - continue; + if (tcpReassemblyData.closed) // the connection is already closed, skip it + continue; - uint32_t flowKey = tcpReassemblyData.connData.flowKey; - PCPP_LOG_DEBUG("Closing connection with flow key 0x" << std::hex << flowKey); + uint32_t flowKey = tcpReassemblyData.connData.flowKey; + PCPP_LOG_DEBUG("Closing connection with flow key 0x" << std::hex + << flowKey); - PCPP_LOG_DEBUG("Calling checkOutOfOrderFragments on side 0"); - checkOutOfOrderFragments(&tcpReassemblyData, 0, true); + PCPP_LOG_DEBUG("Calling checkOutOfOrderFragments on side 0"); + checkOutOfOrderFragments(&tcpReassemblyData, 0, true); - PCPP_LOG_DEBUG("Calling checkOutOfOrderFragments on side 1"); - checkOutOfOrderFragments(&tcpReassemblyData, 1, true); + PCPP_LOG_DEBUG("Calling checkOutOfOrderFragments on side 1"); + checkOutOfOrderFragments(&tcpReassemblyData, 1, true); - if (m_OnConnEnd != nullptr) - m_OnConnEnd(tcpReassemblyData.connData, TcpReassemblyConnectionClosedManually, m_UserCookie); + if (m_OnConnEnd != nullptr) + m_OnConnEnd(tcpReassemblyData.connData, + TcpReassemblyConnectionClosedManually, m_UserCookie); - tcpReassemblyData.closed = true; // mark the connection as closed - insertIntoCleanupList(flowKey); + tcpReassemblyData.closed = true; // mark the connection as closed + insertIntoCleanupList(flowKey); - PCPP_LOG_DEBUG("Connection with flow key 0x" << std::hex << flowKey << " is closed"); - } + PCPP_LOG_DEBUG("Connection with flow key 0x" << std::hex << flowKey + << " is closed"); + } } -int TcpReassembly::isConnectionOpen(const ConnectionData& connection) const -{ - ConnectionList::const_iterator iter = m_ConnectionList.find(connection.flowKey); - if (iter != m_ConnectionList.end()) - return iter->second.closed == false; +int TcpReassembly::isConnectionOpen(const ConnectionData& connection) const { + ConnectionList::const_iterator iter = + m_ConnectionList.find(connection.flowKey); + if (iter != m_ConnectionList.end()) + return iter->second.closed == false; - return -1; + return -1; } -void TcpReassembly::insertIntoCleanupList(uint32_t flowKey) -{ - // m_CleanupList is a map with key of type time_t (expiration time). The mapped type is a list that stores the flow keys to be cleared in certain point of time. - // m_CleanupList.insert inserts an empty list if the container does not already contain an element with an equivalent key, - // otherwise this method returns an iterator to the element that prevents insertion. - std::pair pair = m_CleanupList.insert(std::make_pair(time(nullptr) + m_ClosedConnectionDelay, CleanupList::mapped_type())); - - // getting the reference to list - CleanupList::mapped_type& keysList = pair.first->second; - keysList.push_front(flowKey); +void TcpReassembly::insertIntoCleanupList(uint32_t flowKey) { + // m_CleanupList is a map with key of type time_t (expiration time). The + // mapped type is a list that stores the flow keys to be cleared in certain + // point of time. m_CleanupList.insert inserts an empty list if the container + // does not already contain an element with an equivalent key, otherwise this + // method returns an iterator to the element that prevents insertion. + std::pair pair = + m_CleanupList.insert(std::make_pair( + time(nullptr) + m_ClosedConnectionDelay, CleanupList::mapped_type())); + + // getting the reference to list + CleanupList::mapped_type& keysList = pair.first->second; + keysList.push_front(flowKey); } -uint32_t TcpReassembly::purgeClosedConnections(uint32_t maxNumToClean) -{ - uint32_t count = 0; - - if (maxNumToClean == 0) - maxNumToClean = m_MaxNumToClean; - - CleanupList::iterator iterTime = m_CleanupList.begin(), iterTimeEnd = m_CleanupList.upper_bound(time(nullptr)); - while (iterTime != iterTimeEnd && count < maxNumToClean) - { - CleanupList::mapped_type& keysList = iterTime->second; - - for (; !keysList.empty() && count < maxNumToClean; ++count) - { - CleanupList::mapped_type::const_reference key = keysList.front(); - m_ConnectionInfo.erase(key); - m_ConnectionList.erase(key); - keysList.pop_front(); - } - - if (keysList.empty()) - m_CleanupList.erase(iterTime++); - else - ++iterTime; - } - - return count; -} +uint32_t TcpReassembly::purgeClosedConnections(uint32_t maxNumToClean) { + uint32_t count = 0; + + if (maxNumToClean == 0) + maxNumToClean = m_MaxNumToClean; + CleanupList::iterator iterTime = m_CleanupList.begin(), + iterTimeEnd = m_CleanupList.upper_bound(time(nullptr)); + while (iterTime != iterTimeEnd && count < maxNumToClean) { + CleanupList::mapped_type& keysList = iterTime->second; + + for (; !keysList.empty() && count < maxNumToClean; ++count) { + CleanupList::mapped_type::const_reference key = keysList.front(); + m_ConnectionInfo.erase(key); + m_ConnectionList.erase(key); + keysList.pop_front(); + } + + if (keysList.empty()) + m_CleanupList.erase(iterTime++); + else + ++iterTime; + } + + return count; } + +} // namespace pcpp diff --git a/Packet++/src/TelnetLayer.cpp b/Packet++/src/TelnetLayer.cpp index 3f0e58b272..a6dae42624 100644 --- a/Packet++/src/TelnetLayer.cpp +++ b/Packet++/src/TelnetLayer.cpp @@ -8,472 +8,445 @@ #include -namespace pcpp -{ +namespace pcpp { -bool TelnetLayer::isDataField(uint8_t *pos) const -{ - // "FF FF" means data - return pos[0] != static_cast(TelnetCommand::InterpretAsCommand) || pos[1] == static_cast(TelnetCommand::InterpretAsCommand); +bool TelnetLayer::isDataField(uint8_t* pos) const { + // "FF FF" means data + return pos[0] != static_cast(TelnetCommand::InterpretAsCommand) || + pos[1] == static_cast(TelnetCommand::InterpretAsCommand); } -bool TelnetLayer::isCommandField(uint8_t *pos) const -{ - return !isDataField(pos); +bool TelnetLayer::isCommandField(uint8_t* pos) const { + return !isDataField(pos); } -size_t TelnetLayer::distanceToNextIAC(uint8_t *startPos, size_t maxLength) -{ - uint8_t *pos = nullptr; - size_t addition = 0; - size_t currentOffset = 0; - do - { - // If it is second turn position should be adjusted to after second FF - if (addition) - addition += 2; - - pos = (uint8_t *)memchr(startPos + currentOffset + 1, static_cast(TelnetCommand::InterpretAsCommand), maxLength - currentOffset); - if (pos) - addition += pos - (startPos + currentOffset); - else - addition += maxLength - currentOffset; - currentOffset = currentOffset + addition; - // "FF FF" means data continue - } while (pos && (pos[1] == static_cast(TelnetCommand::InterpretAsCommand)) && (currentOffset < maxLength)); - - return addition; +size_t TelnetLayer::distanceToNextIAC(uint8_t* startPos, size_t maxLength) { + uint8_t* pos = nullptr; + size_t addition = 0; + size_t currentOffset = 0; + do { + // If it is second turn position should be adjusted to after second FF + if (addition) + addition += 2; + + pos = (uint8_t*)memchr(startPos + currentOffset + 1, + static_cast(TelnetCommand::InterpretAsCommand), + maxLength - currentOffset); + if (pos) + addition += pos - (startPos + currentOffset); + else + addition += maxLength - currentOffset; + currentOffset = currentOffset + addition; + // "FF FF" means data continue + } while (pos && + (pos[1] == static_cast(TelnetCommand::InterpretAsCommand)) && + (currentOffset < maxLength)); + + return addition; } -size_t TelnetLayer::getFieldLen(uint8_t *startPos, size_t maxLength) -{ - // Check first byte is IAC - if (startPos && (startPos[0] == static_cast(TelnetCommand::InterpretAsCommand)) && (maxLength >= 2)) - { - // If subnegotiation parse until next IAC - if (startPos[1] == static_cast(TelnetCommand::Subnegotiation)) - return distanceToNextIAC(startPos, maxLength); - // Only WILL, WONT, DO, DONT have option. Ref http://pcmicro.com/netfoss/telnet.html - else if (startPos[1] >= static_cast(TelnetCommand::WillPerform) && startPos[1] <= static_cast(TelnetCommand::DontPerform)) - return 3; - return 2; - } - return distanceToNextIAC(startPos, maxLength); +size_t TelnetLayer::getFieldLen(uint8_t* startPos, size_t maxLength) { + // Check first byte is IAC + if (startPos && + (startPos[0] == static_cast(TelnetCommand::InterpretAsCommand)) && + (maxLength >= 2)) { + // If subnegotiation parse until next IAC + if (startPos[1] == static_cast(TelnetCommand::Subnegotiation)) + return distanceToNextIAC(startPos, maxLength); + // Only WILL, WONT, DO, DONT have option. Ref + // http://pcmicro.com/netfoss/telnet.html + else if (startPos[1] >= static_cast(TelnetCommand::WillPerform) && + startPos[1] <= static_cast(TelnetCommand::DontPerform)) + return 3; + return 2; + } + return distanceToNextIAC(startPos, maxLength); } -uint8_t *TelnetLayer::getNextDataField(uint8_t *pos, size_t len) -{ - size_t offset = 0; - while (offset < len) - { - // Move to next field - size_t length = getFieldLen(pos, len - offset); - pos += length; - offset += length; - - if (isDataField(pos)) - return pos; - } - - return nullptr; +uint8_t* TelnetLayer::getNextDataField(uint8_t* pos, size_t len) { + size_t offset = 0; + while (offset < len) { + // Move to next field + size_t length = getFieldLen(pos, len - offset); + pos += length; + offset += length; + + if (isDataField(pos)) + return pos; + } + + return nullptr; } -uint8_t *TelnetLayer::getNextCommandField(uint8_t *pos, size_t len) -{ - size_t offset = 0; - while (offset < len) - { - // Move to next field - size_t length = getFieldLen(pos, len - offset); - pos += length; - offset += length; - - if (isCommandField(pos)) - return pos; - } - - return nullptr; +uint8_t* TelnetLayer::getNextCommandField(uint8_t* pos, size_t len) { + size_t offset = 0; + while (offset < len) { + // Move to next field + size_t length = getFieldLen(pos, len - offset); + pos += length; + offset += length; + + if (isCommandField(pos)) + return pos; + } + + return nullptr; } -int16_t TelnetLayer::getSubCommand(uint8_t *pos, size_t len) -{ - if (len < 3 || pos[1] < static_cast(TelnetCommand::Subnegotiation)) - return static_cast(TelnetOption::TelnetOptionNoOption); - return pos[2]; +int16_t TelnetLayer::getSubCommand(uint8_t* pos, size_t len) { + if (len < 3 || pos[1] < static_cast(TelnetCommand::Subnegotiation)) + return static_cast(TelnetOption::TelnetOptionNoOption); + return pos[2]; } -uint8_t *TelnetLayer::getCommandData(uint8_t *pos, size_t &len) -{ - if (pos[1] == static_cast(TelnetCommand::Subnegotiation) && len > 3) - { - len -= 3; - return &pos[3]; - } - len = 0; - return nullptr; +uint8_t* TelnetLayer::getCommandData(uint8_t* pos, size_t& len) { + if (pos[1] == static_cast(TelnetCommand::Subnegotiation) && len > 3) { + len -= 3; + return &pos[3]; + } + len = 0; + return nullptr; } -std::string TelnetLayer::getDataAsString(bool removeEscapeCharacters) -{ - uint8_t *dataPos = nullptr; - if (isDataField(m_Data)) - dataPos = m_Data; - else - dataPos = getNextDataField(m_Data, m_DataLen); - - if (!dataPos) - { - PCPP_LOG_DEBUG("Packet does not have a data field"); - return std::string(); - } - - // Convert to string - if (removeEscapeCharacters) - { - std::stringstream ss; - for (size_t idx = 0; idx < m_DataLen - (dataPos - m_Data) + 1; ++idx) - { - if (int(dataPos[idx]) < 127 && int(dataPos[idx]) > 31) // From SPACE to ~ - ss << dataPos[idx]; - } - return ss.str(); - } - return std::string((char *)m_Data, m_DataLen); +std::string TelnetLayer::getDataAsString(bool removeEscapeCharacters) { + uint8_t* dataPos = nullptr; + if (isDataField(m_Data)) + dataPos = m_Data; + else + dataPos = getNextDataField(m_Data, m_DataLen); + + if (!dataPos) { + PCPP_LOG_DEBUG("Packet does not have a data field"); + return std::string(); + } + + // Convert to string + if (removeEscapeCharacters) { + std::stringstream ss; + for (size_t idx = 0; idx < m_DataLen - (dataPos - m_Data) + 1; ++idx) { + if (int(dataPos[idx]) < 127 && int(dataPos[idx]) > 31) // From SPACE to ~ + ss << dataPos[idx]; + } + return ss.str(); + } + return std::string((char*)m_Data, m_DataLen); } -size_t TelnetLayer::getTotalNumberOfCommands() -{ - size_t ctr = 0; - if (isCommandField(m_Data)) - ++ctr; - - uint8_t *pos = m_Data; - while (pos != nullptr) - { - size_t offset = pos - m_Data; - pos = getNextCommandField(pos, m_DataLen - offset); - if (pos) - ++ctr; - } - - return ctr; +size_t TelnetLayer::getTotalNumberOfCommands() { + size_t ctr = 0; + if (isCommandField(m_Data)) + ++ctr; + + uint8_t* pos = m_Data; + while (pos != nullptr) { + size_t offset = pos - m_Data; + pos = getNextCommandField(pos, m_DataLen - offset); + if (pos) + ++ctr; + } + + return ctr; } -size_t TelnetLayer::getNumberOfCommands(TelnetCommand command) -{ - if (static_cast(command) < 0) - return 0; - - size_t ctr = 0; - if (isCommandField(m_Data) && m_Data[1] == static_cast(command)) - ++ctr; - - uint8_t *pos = m_Data; - while (pos != nullptr) - { - size_t offset = pos - m_Data; - pos = getNextCommandField(pos, m_DataLen - offset); - if (pos && pos[1] == static_cast(command)) - ++ctr; - } - - return ctr; +size_t TelnetLayer::getNumberOfCommands(TelnetCommand command) { + if (static_cast(command) < 0) + return 0; + + size_t ctr = 0; + if (isCommandField(m_Data) && m_Data[1] == static_cast(command)) + ++ctr; + + uint8_t* pos = m_Data; + while (pos != nullptr) { + size_t offset = pos - m_Data; + pos = getNextCommandField(pos, m_DataLen - offset); + if (pos && pos[1] == static_cast(command)) + ++ctr; + } + + return ctr; } -TelnetLayer::TelnetCommand TelnetLayer::getFirstCommand() -{ - // If starts with command - if (isCommandField(m_Data)) - return static_cast(m_Data[1]); - - // Check is there any command - uint8_t *pos = getNextCommandField(m_Data, m_DataLen); - if (pos) - return static_cast(pos[1]); - return TelnetCommand::TelnetCommandEndOfPacket; +TelnetLayer::TelnetCommand TelnetLayer::getFirstCommand() { + // If starts with command + if (isCommandField(m_Data)) + return static_cast(m_Data[1]); + + // Check is there any command + uint8_t* pos = getNextCommandField(m_Data, m_DataLen); + if (pos) + return static_cast(pos[1]); + return TelnetCommand::TelnetCommandEndOfPacket; } -TelnetLayer::TelnetCommand TelnetLayer::getNextCommand() -{ - if (lastPositionOffset == SIZE_MAX) - { - lastPositionOffset = 0; - if (isCommandField(m_Data)) - return static_cast(m_Data[1]); - } - - uint8_t *pos = getNextCommandField(&m_Data[lastPositionOffset], m_DataLen - lastPositionOffset); - if (pos) - { - lastPositionOffset = pos - m_Data; - return static_cast(pos[1]); - } - lastPositionOffset = SIZE_MAX; - return TelnetCommand::TelnetCommandEndOfPacket; +TelnetLayer::TelnetCommand TelnetLayer::getNextCommand() { + if (lastPositionOffset == SIZE_MAX) { + lastPositionOffset = 0; + if (isCommandField(m_Data)) + return static_cast(m_Data[1]); + } + + uint8_t* pos = getNextCommandField(&m_Data[lastPositionOffset], + m_DataLen - lastPositionOffset); + if (pos) { + lastPositionOffset = pos - m_Data; + return static_cast(pos[1]); + } + lastPositionOffset = SIZE_MAX; + return TelnetCommand::TelnetCommandEndOfPacket; } -TelnetLayer::TelnetOption TelnetLayer::getOption() -{ - if (lastPositionOffset < m_DataLen) - return static_cast(getSubCommand( - &m_Data[lastPositionOffset], getFieldLen(&m_Data[lastPositionOffset], m_DataLen - lastPositionOffset))); - return TelnetOption::TelnetOptionNoOption; +TelnetLayer::TelnetOption TelnetLayer::getOption() { + if (lastPositionOffset < m_DataLen) + return static_cast( + getSubCommand(&m_Data[lastPositionOffset], + getFieldLen(&m_Data[lastPositionOffset], + m_DataLen - lastPositionOffset))); + return TelnetOption::TelnetOptionNoOption; } -TelnetLayer::TelnetOption TelnetLayer::getOption(TelnetCommand command) -{ - // Check input - if (static_cast(command) < 0) - { - PCPP_LOG_ERROR("Command type can't be negative"); - return TelnetOption::TelnetOptionNoOption; - } - - if (isCommandField(m_Data) && m_Data[1] == static_cast(command)) - return static_cast(getSubCommand(m_Data, getFieldLen(m_Data, m_DataLen))); - - uint8_t *pos = m_Data; - while (pos != nullptr) - { - size_t offset = pos - m_Data; - pos = getNextCommandField(pos, m_DataLen - offset); - - if (pos && pos[1] == static_cast(command)) - return static_cast(getSubCommand(pos, getFieldLen(pos, m_DataLen - offset))); - } - - PCPP_LOG_DEBUG("Can't find requested command"); - return TelnetOption::TelnetOptionNoOption; +TelnetLayer::TelnetOption TelnetLayer::getOption(TelnetCommand command) { + // Check input + if (static_cast(command) < 0) { + PCPP_LOG_ERROR("Command type can't be negative"); + return TelnetOption::TelnetOptionNoOption; + } + + if (isCommandField(m_Data) && m_Data[1] == static_cast(command)) + return static_cast( + getSubCommand(m_Data, getFieldLen(m_Data, m_DataLen))); + + uint8_t* pos = m_Data; + while (pos != nullptr) { + size_t offset = pos - m_Data; + pos = getNextCommandField(pos, m_DataLen - offset); + + if (pos && pos[1] == static_cast(command)) + return static_cast( + getSubCommand(pos, getFieldLen(pos, m_DataLen - offset))); + } + + PCPP_LOG_DEBUG("Can't find requested command"); + return TelnetOption::TelnetOptionNoOption; } -uint8_t *TelnetLayer::getOptionData(size_t &length) -{ - if (lastPositionOffset < m_DataLen) - { - size_t lenBuffer = getFieldLen(&m_Data[lastPositionOffset], m_DataLen - lastPositionOffset); - uint8_t *posBuffer = getCommandData(&m_Data[lastPositionOffset], lenBuffer); - - length = lenBuffer; - return posBuffer; - } - return nullptr; +uint8_t* TelnetLayer::getOptionData(size_t& length) { + if (lastPositionOffset < m_DataLen) { + size_t lenBuffer = getFieldLen(&m_Data[lastPositionOffset], + m_DataLen - lastPositionOffset); + uint8_t* posBuffer = getCommandData(&m_Data[lastPositionOffset], lenBuffer); + + length = lenBuffer; + return posBuffer; + } + return nullptr; } -uint8_t *TelnetLayer::getOptionData(TelnetCommand command, size_t &length) -{ - // Check input - if (static_cast(command) < 0) - { - PCPP_LOG_ERROR("Command type can't be negative"); - length = 0; - return nullptr; - } - - if (isCommandField(m_Data) && m_Data[1] == static_cast(command)) - { - size_t lenBuffer = getFieldLen(m_Data, m_DataLen); - uint8_t *posBuffer = getCommandData(m_Data, lenBuffer); - - length = lenBuffer; - return posBuffer; - } - - uint8_t *pos = m_Data; - while (pos != nullptr) - { - size_t offset = pos - m_Data; - pos = getNextCommandField(pos, m_DataLen - offset); - - if (pos && pos[1] == static_cast(command)) - { - size_t lenBuffer = getFieldLen(m_Data, m_DataLen); - uint8_t *posBuffer = getCommandData(m_Data, lenBuffer); - - length = lenBuffer; - return posBuffer; - } - } - - PCPP_LOG_DEBUG("Can't find requested command"); - length = 0; - return nullptr; +uint8_t* TelnetLayer::getOptionData(TelnetCommand command, size_t& length) { + // Check input + if (static_cast(command) < 0) { + PCPP_LOG_ERROR("Command type can't be negative"); + length = 0; + return nullptr; + } + + if (isCommandField(m_Data) && m_Data[1] == static_cast(command)) { + size_t lenBuffer = getFieldLen(m_Data, m_DataLen); + uint8_t* posBuffer = getCommandData(m_Data, lenBuffer); + + length = lenBuffer; + return posBuffer; + } + + uint8_t* pos = m_Data; + while (pos != nullptr) { + size_t offset = pos - m_Data; + pos = getNextCommandField(pos, m_DataLen - offset); + + if (pos && pos[1] == static_cast(command)) { + size_t lenBuffer = getFieldLen(m_Data, m_DataLen); + uint8_t* posBuffer = getCommandData(m_Data, lenBuffer); + + length = lenBuffer; + return posBuffer; + } + } + + PCPP_LOG_DEBUG("Can't find requested command"); + length = 0; + return nullptr; } -std::string TelnetLayer::getTelnetCommandAsString(TelnetCommand val) -{ - switch (val) - { - case TelnetCommand::TelnetCommandEndOfPacket: - return "Reached end of packet while parsing"; - case TelnetCommand::EndOfFile: - return "End of File"; - case TelnetCommand::Suspend: - return "Suspend current process"; - case TelnetCommand::Abort: - return "Abort Process"; - case TelnetCommand::EndOfRecordCommand: - return "End of Record"; - case TelnetCommand::SubnegotiationEnd: - return "Subnegotiation End"; - case TelnetCommand::NoOperation: - return "No Operation"; - case TelnetCommand::DataMark: - return "Data Mark"; - case TelnetCommand::Break: - return "Break"; - case TelnetCommand::InterruptProcess: - return "Interrupt Process"; - case TelnetCommand::AbortOutput: - return "Abort Output"; - case TelnetCommand::AreYouThere: - return "Are You There"; - case TelnetCommand::EraseCharacter: - return "Erase Character"; - case TelnetCommand::EraseLine: - return "Erase Line"; - case TelnetCommand::GoAhead: - return "Go Ahead"; - case TelnetCommand::Subnegotiation: - return "Subnegotiation"; - case TelnetCommand::WillPerform: - return "Will Perform"; - case TelnetCommand::WontPerform: - return "Wont Perform"; - case TelnetCommand::DoPerform: - return "Do Perform"; - case TelnetCommand::DontPerform: - return "Dont Perform"; - case TelnetCommand::InterpretAsCommand: - return "Interpret As Command"; - default: - return "Unknown Command"; - } +std::string TelnetLayer::getTelnetCommandAsString(TelnetCommand val) { + switch (val) { + case TelnetCommand::TelnetCommandEndOfPacket: + return "Reached end of packet while parsing"; + case TelnetCommand::EndOfFile: + return "End of File"; + case TelnetCommand::Suspend: + return "Suspend current process"; + case TelnetCommand::Abort: + return "Abort Process"; + case TelnetCommand::EndOfRecordCommand: + return "End of Record"; + case TelnetCommand::SubnegotiationEnd: + return "Subnegotiation End"; + case TelnetCommand::NoOperation: + return "No Operation"; + case TelnetCommand::DataMark: + return "Data Mark"; + case TelnetCommand::Break: + return "Break"; + case TelnetCommand::InterruptProcess: + return "Interrupt Process"; + case TelnetCommand::AbortOutput: + return "Abort Output"; + case TelnetCommand::AreYouThere: + return "Are You There"; + case TelnetCommand::EraseCharacter: + return "Erase Character"; + case TelnetCommand::EraseLine: + return "Erase Line"; + case TelnetCommand::GoAhead: + return "Go Ahead"; + case TelnetCommand::Subnegotiation: + return "Subnegotiation"; + case TelnetCommand::WillPerform: + return "Will Perform"; + case TelnetCommand::WontPerform: + return "Wont Perform"; + case TelnetCommand::DoPerform: + return "Do Perform"; + case TelnetCommand::DontPerform: + return "Dont Perform"; + case TelnetCommand::InterpretAsCommand: + return "Interpret As Command"; + default: + return "Unknown Command"; + } } -std::string TelnetLayer::getTelnetOptionAsString(TelnetOption val) -{ - switch (val) - { - case TelnetOption::TelnetOptionNoOption: - return "No option for this command"; - case TelnetOption::TransmitBinary: - return "Binary Transmission"; - case TelnetOption::Echo: - return "Echo"; - case TelnetOption::Reconnection: - return "Reconnection"; - case TelnetOption::SuppressGoAhead: - return "Suppress Go Ahead"; - case TelnetOption::ApproxMsgSizeNegotiation: - return "Negotiate approximate message size"; - case TelnetOption::Status: - return "Status"; - case TelnetOption::TimingMark: - return "Timing Mark"; - case TelnetOption::RemoteControlledTransAndEcho: - return "Remote Controlled Transmission and Echo"; - case TelnetOption::OutputLineWidth: - return "Output Line Width"; - case TelnetOption::OutputPageSize: - return "Output Page Size"; - case TelnetOption::OutputCarriageReturnDisposition: - return "Negotiate About Output Carriage-Return Disposition"; - case TelnetOption::OutputHorizontalTabStops: - return "Negotiate About Output Horizontal Tabstops"; - case TelnetOption::OutputHorizontalTabDisposition: - return "Negotiate About Output Horizontal Tab Disposition"; - case TelnetOption::OutputFormfeedDisposition: - return "Negotiate About Output Formfeed Disposition"; - case TelnetOption::OutputVerticalTabStops: - return "Negotiate About Vertical Tabstops"; - case TelnetOption::OutputVerticalTabDisposition: - return "Negotiate About Output Vertcial Tab Disposition"; - case TelnetOption::OutputLinefeedDisposition: - return "Negotiate About Output Linefeed Disposition"; - case TelnetOption::ExtendedASCII: - return "Extended ASCII"; - case TelnetOption::Logout: - return "Logout"; - case TelnetOption::ByteMacro: - return "Byte Macro"; - case TelnetOption::DataEntryTerminal: - return "Data Entry Terminal"; - case TelnetOption::SUPDUP: - return "SUPDUP"; - case TelnetOption::SUPDUPOutput: - return "SUPDUP Output"; - case TelnetOption::SendLocation: - return "Send Location"; - case TelnetOption::TerminalType: - return "Terminal Type"; - case TelnetOption::EndOfRecordOption: - return "End Of Record"; - case TelnetOption::TACACSUserIdentification: - return "TACACS User Identification"; - case TelnetOption::OutputMarking: - return "Output Marking"; - case TelnetOption::TerminalLocationNumber: - return "Terminal Location Number"; - case TelnetOption::Telnet3270Regime: - return "Telnet 3270 Regime"; - case TelnetOption::X3Pad: - return "X3 Pad"; - case TelnetOption::NegotiateAboutWindowSize: - return "Negotiate About Window Size"; - case TelnetOption::TerminalSpeed: - return "Terminal Speed"; - case TelnetOption::RemoteFlowControl: - return "Remote Flow Control"; - case TelnetOption::Linemode: - return "Line mode"; - case TelnetOption::XDisplayLocation: - return "X Display Location"; - case TelnetOption::EnvironmentOption: - return "Environment Option"; - case TelnetOption::AuthenticationOption: - return "Authentication Option"; - case TelnetOption::EncryptionOption: - return "Encryption Option"; - case TelnetOption::NewEnvironmentOption: - return "New Environment Option"; - case TelnetOption::TN3270E: - return "TN3270E"; - case TelnetOption::XAuth: - return "X Server Authentication"; - case TelnetOption::Charset: - return "Charset"; - case TelnetOption::TelnetRemoteSerialPort: - return "Telnet Remote Serial Port"; - case TelnetOption::ComPortControlOption: - return "Com Port Control Option"; - case TelnetOption::TelnetSuppressLocalEcho: - return "Telnet Suppress Local Echo"; - case TelnetOption::TelnetStartTLS: - return "Telnet Start TLS"; - case TelnetOption::Kermit: - return "Kermit"; - case TelnetOption::SendURL: - return "Send URL"; - case TelnetOption::ForwardX: - return "Forward X Server"; - case TelnetOption::TelOptPragmaLogon: - return "Telnet Option Pragma Logon"; - case TelnetOption::TelOptSSPILogon: - return "Telnet Option SSPI Logon"; - case TelnetOption::TelOptPragmaHeartbeat: - return "Telnet Option Pragma Heartbeat"; - case TelnetOption::ExtendedOptions: - return "Extended option list"; - default: - return "Unknown Option"; - } +std::string TelnetLayer::getTelnetOptionAsString(TelnetOption val) { + switch (val) { + case TelnetOption::TelnetOptionNoOption: + return "No option for this command"; + case TelnetOption::TransmitBinary: + return "Binary Transmission"; + case TelnetOption::Echo: + return "Echo"; + case TelnetOption::Reconnection: + return "Reconnection"; + case TelnetOption::SuppressGoAhead: + return "Suppress Go Ahead"; + case TelnetOption::ApproxMsgSizeNegotiation: + return "Negotiate approximate message size"; + case TelnetOption::Status: + return "Status"; + case TelnetOption::TimingMark: + return "Timing Mark"; + case TelnetOption::RemoteControlledTransAndEcho: + return "Remote Controlled Transmission and Echo"; + case TelnetOption::OutputLineWidth: + return "Output Line Width"; + case TelnetOption::OutputPageSize: + return "Output Page Size"; + case TelnetOption::OutputCarriageReturnDisposition: + return "Negotiate About Output Carriage-Return Disposition"; + case TelnetOption::OutputHorizontalTabStops: + return "Negotiate About Output Horizontal Tabstops"; + case TelnetOption::OutputHorizontalTabDisposition: + return "Negotiate About Output Horizontal Tab Disposition"; + case TelnetOption::OutputFormfeedDisposition: + return "Negotiate About Output Formfeed Disposition"; + case TelnetOption::OutputVerticalTabStops: + return "Negotiate About Vertical Tabstops"; + case TelnetOption::OutputVerticalTabDisposition: + return "Negotiate About Output Vertcial Tab Disposition"; + case TelnetOption::OutputLinefeedDisposition: + return "Negotiate About Output Linefeed Disposition"; + case TelnetOption::ExtendedASCII: + return "Extended ASCII"; + case TelnetOption::Logout: + return "Logout"; + case TelnetOption::ByteMacro: + return "Byte Macro"; + case TelnetOption::DataEntryTerminal: + return "Data Entry Terminal"; + case TelnetOption::SUPDUP: + return "SUPDUP"; + case TelnetOption::SUPDUPOutput: + return "SUPDUP Output"; + case TelnetOption::SendLocation: + return "Send Location"; + case TelnetOption::TerminalType: + return "Terminal Type"; + case TelnetOption::EndOfRecordOption: + return "End Of Record"; + case TelnetOption::TACACSUserIdentification: + return "TACACS User Identification"; + case TelnetOption::OutputMarking: + return "Output Marking"; + case TelnetOption::TerminalLocationNumber: + return "Terminal Location Number"; + case TelnetOption::Telnet3270Regime: + return "Telnet 3270 Regime"; + case TelnetOption::X3Pad: + return "X3 Pad"; + case TelnetOption::NegotiateAboutWindowSize: + return "Negotiate About Window Size"; + case TelnetOption::TerminalSpeed: + return "Terminal Speed"; + case TelnetOption::RemoteFlowControl: + return "Remote Flow Control"; + case TelnetOption::Linemode: + return "Line mode"; + case TelnetOption::XDisplayLocation: + return "X Display Location"; + case TelnetOption::EnvironmentOption: + return "Environment Option"; + case TelnetOption::AuthenticationOption: + return "Authentication Option"; + case TelnetOption::EncryptionOption: + return "Encryption Option"; + case TelnetOption::NewEnvironmentOption: + return "New Environment Option"; + case TelnetOption::TN3270E: + return "TN3270E"; + case TelnetOption::XAuth: + return "X Server Authentication"; + case TelnetOption::Charset: + return "Charset"; + case TelnetOption::TelnetRemoteSerialPort: + return "Telnet Remote Serial Port"; + case TelnetOption::ComPortControlOption: + return "Com Port Control Option"; + case TelnetOption::TelnetSuppressLocalEcho: + return "Telnet Suppress Local Echo"; + case TelnetOption::TelnetStartTLS: + return "Telnet Start TLS"; + case TelnetOption::Kermit: + return "Kermit"; + case TelnetOption::SendURL: + return "Send URL"; + case TelnetOption::ForwardX: + return "Forward X Server"; + case TelnetOption::TelOptPragmaLogon: + return "Telnet Option Pragma Logon"; + case TelnetOption::TelOptSSPILogon: + return "Telnet Option SSPI Logon"; + case TelnetOption::TelOptPragmaHeartbeat: + return "Telnet Option Pragma Heartbeat"; + case TelnetOption::ExtendedOptions: + return "Extended option list"; + default: + return "Unknown Option"; + } } -std::string TelnetLayer::toString() const -{ - if (isDataField(m_Data)) - return "Telnet Data"; - return "Telnet Control"; +std::string TelnetLayer::toString() const { + if (isDataField(m_Data)) + return "Telnet Data"; + return "Telnet Control"; } } // namespace pcpp diff --git a/Packet++/src/TextBasedProtocol.cpp b/Packet++/src/TextBasedProtocol.cpp index 9b50723803..3d1890a0d0 100644 --- a/Packet++/src/TextBasedProtocol.cpp +++ b/Packet++/src/TextBasedProtocol.cpp @@ -1,693 +1,702 @@ #include "TextBasedProtocol.h" #include "Logger.h" #include "PayloadLayer.h" -#include #include -#include #include +#include +#include -namespace pcpp -{ +namespace pcpp { // this implementation of strnlen is required since mingw doesn't have strnlen -size_t tbp_my_own_strnlen(const char* s, size_t maxlen) -{ - if (s == nullptr || maxlen == 0) - return 0; +size_t tbp_my_own_strnlen(const char* s, size_t maxlen) { + if (s == nullptr || maxlen == 0) + return 0; - size_t i = 0; - for(; (i < maxlen) && s[i]; ++i); - return i; + size_t i = 0; + for (; (i < maxlen) && s[i]; ++i) + ; + return i; } - // -------- Class TextBasedProtocolMessage ----------------- - -TextBasedProtocolMessage::TextBasedProtocolMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet), - m_FieldList(nullptr), m_LastField(nullptr), m_FieldsOffset(0) {} - -TextBasedProtocolMessage::TextBasedProtocolMessage(const TextBasedProtocolMessage& other) : Layer(other) -{ - copyDataFrom(other); -} - -TextBasedProtocolMessage& TextBasedProtocolMessage::operator=(const TextBasedProtocolMessage& other) -{ - Layer::operator=(other); - HeaderField* curField = m_FieldList; - while (curField != nullptr) - { - HeaderField* temp = curField; - curField = curField->getNextField(); - delete temp; - } - - copyDataFrom(other); - - return *this; -} - -void TextBasedProtocolMessage::copyDataFrom(const TextBasedProtocolMessage& other) -{ - // copy field list - if (other.m_FieldList != nullptr) - { - m_FieldList = new HeaderField(*(other.m_FieldList)); - HeaderField* curField = m_FieldList; - curField->attachToTextBasedProtocolMessage(this, other.m_FieldList->m_NameOffsetInMessage); - HeaderField* curOtherField = other.m_FieldList; - while (curOtherField->getNextField() != nullptr) - { - HeaderField* newField = new HeaderField(*(curOtherField->getNextField())); - newField->attachToTextBasedProtocolMessage(this, curOtherField->getNextField()->m_NameOffsetInMessage); - curField->setNextField(newField); - curField = curField->getNextField(); - curOtherField = curOtherField->getNextField(); - } - - m_LastField = curField; - } - else - { - m_FieldList = nullptr; - m_LastField = nullptr; - } - - m_FieldsOffset = other.m_FieldsOffset; - - // copy map - for(HeaderField* field = m_FieldList; field != nullptr; field = field->getNextField()) - { - m_FieldNameToFieldMap.insert(std::pair(field->getFieldName(), field)); - } -} - - -void TextBasedProtocolMessage::parseFields() -{ - char nameValueSeparator = getHeaderFieldNameValueSeparator(); - bool spacesAllowedBetweenNameAndValue = spacesAllowedBetweenHeaderFieldNameAndValue(); - - HeaderField* firstField = new HeaderField(this, m_FieldsOffset, nameValueSeparator, spacesAllowedBetweenNameAndValue); - PCPP_LOG_DEBUG("Added new field: name='" << firstField->getFieldName() << "'; offset in packet=" << firstField->m_NameOffsetInMessage << "; length=" << firstField->getFieldSize()); - PCPP_LOG_DEBUG(" Field value = " << firstField->getFieldValue()); - - if (m_FieldList == nullptr) - m_FieldList = firstField; - else - m_FieldList->setNextField(firstField); - - std::string fieldName = firstField->getFieldName(); - std::transform(fieldName.begin(), fieldName.end(), fieldName.begin(), ::tolower); - m_FieldNameToFieldMap.insert(std::pair(fieldName, firstField)); - - // Last field will be empty and contain just "\n" or "\r\n". This field will mark the end of the header - HeaderField* curField = m_FieldList; - int curOffset = m_FieldsOffset; - // last field can be one of: - // a.) \r\n\r\n or \n\n marking the end of the header - // b.) the end of the packet - while (!curField->isEndOfHeader() && curOffset + curField->getFieldSize() < m_DataLen) - { - curOffset += curField->getFieldSize(); - HeaderField* newField = new HeaderField(this, curOffset, nameValueSeparator, spacesAllowedBetweenNameAndValue); - if(newField->getFieldSize() > 0) - { - PCPP_LOG_DEBUG("Added new field: name='" << newField->getFieldName() << "'; offset in packet=" << newField->m_NameOffsetInMessage << "; length=" << newField->getFieldSize()); - PCPP_LOG_DEBUG(" Field value = " << newField->getFieldValue()); - curField->setNextField(newField); - curField = newField; - fieldName = newField->getFieldName(); - std::transform(fieldName.begin(), fieldName.end(), fieldName.begin(), ::tolower); - m_FieldNameToFieldMap.insert(std::pair(fieldName, newField)); - } - else - { - delete newField; - break; - } - } - - m_LastField = curField; -} - - -TextBasedProtocolMessage::~TextBasedProtocolMessage() -{ - while (m_FieldList != nullptr) - { - HeaderField* temp = m_FieldList; - m_FieldList = m_FieldList->getNextField(); - delete temp; - } -} - - -HeaderField* TextBasedProtocolMessage::addField(const std::string& fieldName, const std::string& fieldValue) -{ - HeaderField newField(fieldName, fieldValue, getHeaderFieldNameValueSeparator(), spacesAllowedBetweenHeaderFieldNameAndValue()); - return addField(newField); -} - -HeaderField* TextBasedProtocolMessage::addField(const HeaderField& newField) -{ - return insertField(m_LastField, newField); -} - -HeaderField* TextBasedProtocolMessage::addEndOfHeader() -{ - HeaderField endOfHeaderField(PCPP_END_OF_TEXT_BASED_PROTOCOL_HEADER, "", '\0', false); - return insertField(m_LastField, endOfHeaderField); -} - - -HeaderField* TextBasedProtocolMessage::insertField(HeaderField* prevField, const std::string& fieldName, const std::string& fieldValue) -{ - HeaderField newField(fieldName, fieldValue, getHeaderFieldNameValueSeparator(), spacesAllowedBetweenHeaderFieldNameAndValue()); - return insertField(prevField, newField); -} - -HeaderField* TextBasedProtocolMessage::insertField(std::string prevFieldName, const std::string& fieldName, const std::string& fieldValue) -{ - if (prevFieldName == "") - { - return insertField(nullptr, fieldName, fieldValue); - } - else - { - HeaderField* prevField = getFieldByName(prevFieldName); - if (prevField == nullptr) - return nullptr; - - return insertField(prevField, fieldName, fieldValue); - } -} - - -HeaderField* TextBasedProtocolMessage::insertField(HeaderField* prevField, const HeaderField& newField) -{ - if (newField.m_TextBasedProtocolMessage != nullptr) - { - PCPP_LOG_ERROR("This field is already associated with another message"); - return nullptr; - } - - if (prevField != nullptr && prevField->getFieldName() == PCPP_END_OF_TEXT_BASED_PROTOCOL_HEADER) - { - PCPP_LOG_ERROR("Cannot add a field after end of header"); - return nullptr; - } - - HeaderField* newFieldToAdd = new HeaderField(newField); - - int newFieldOffset = m_FieldsOffset; - if (prevField != nullptr) - newFieldOffset = prevField->m_NameOffsetInMessage + prevField->getFieldSize(); - - // extend layer to make room for the new field. Field will be added just before the last field - if (!extendLayer(newFieldOffset, newFieldToAdd->getFieldSize())) - { - PCPP_LOG_ERROR("Cannot extend layer to insert the header"); - delete newFieldToAdd; - return nullptr; - } - - HeaderField* curField = m_FieldList; - if (prevField != nullptr) - curField = prevField->getNextField(); - - // go over all fields after prevField and update their offsets - shiftFieldsOffset(curField, newFieldToAdd->getFieldSize()); - - // copy new field data to message - memcpy(m_Data + newFieldOffset, newFieldToAdd->m_NewFieldData, newFieldToAdd->getFieldSize()); - - // attach new field to message - newFieldToAdd->attachToTextBasedProtocolMessage(this, newFieldOffset); - - // insert field into fields link list - if (prevField == nullptr) - { - newFieldToAdd->setNextField(m_FieldList); - m_FieldList = newFieldToAdd; - } - else - { - newFieldToAdd->setNextField(prevField->getNextField()); - prevField->setNextField(newFieldToAdd); - } - - // if newField is the last field, update m_LastField - if (newFieldToAdd->getNextField() == nullptr) - m_LastField = newFieldToAdd; - - // insert the new field into name to field map - std::string fieldName = newFieldToAdd->getFieldName(); - std::transform(fieldName.begin(), fieldName.end(), fieldName.begin(), ::tolower); - m_FieldNameToFieldMap.insert(std::pair(fieldName, newFieldToAdd)); - - return newFieldToAdd; -} - -bool TextBasedProtocolMessage::removeField(std::string fieldName, int index) -{ - std::transform(fieldName.begin(), fieldName.end(), fieldName.begin(), ::tolower); - - HeaderField* fieldToRemove = nullptr; - - std::pair ::iterator, std::multimap::iterator> range; - range = m_FieldNameToFieldMap.equal_range(fieldName); - int i = 0; - for (std::multimap::iterator iter = range.first; iter != range.second; ++iter) - { - if (i == index) - { - fieldToRemove = iter->second; - break; - } - - i++; - } - - if (fieldToRemove != nullptr) - return removeField(fieldToRemove); - else - { - PCPP_LOG_ERROR("Cannot find field '" << fieldName << "'"); - return false; - } -} - -bool TextBasedProtocolMessage::removeField(HeaderField* fieldToRemove) -{ - if (fieldToRemove == nullptr) - return true; - - if (fieldToRemove->m_TextBasedProtocolMessage != this) - { - PCPP_LOG_ERROR("Field isn't associated with this message"); - return false; - } - - std::string fieldName = fieldToRemove->getFieldName(); - - // shorten layer and delete this field - if (!shortenLayer(fieldToRemove->m_NameOffsetInMessage, fieldToRemove->getFieldSize())) - { - PCPP_LOG_ERROR("Cannot shorten layer"); - return false; - } - - // update offsets of all fields after this field - HeaderField* curField = fieldToRemove->getNextField(); - shiftFieldsOffset(curField, 0-fieldToRemove->getFieldSize()); -// while (curField != NULL) -// { -// curField->m_NameOffsetInMessage -= fieldToRemove->getFieldSize(); -// if (curField->m_ValueOffsetInMessage != -1) -// curField->m_ValueOffsetInMessage -= fieldToRemove->getFieldSize(); -// -// curField = curField->getNextField(); -// } - - // update fields link list - if (fieldToRemove == m_FieldList) - m_FieldList = m_FieldList->getNextField(); - else - { - curField = m_FieldList; - while (curField->getNextField() != fieldToRemove) - curField = curField->getNextField(); - - curField->setNextField(fieldToRemove->getNextField()); - } - - // re-calculate m_LastField if needed - if (fieldToRemove == m_LastField) - { - if (m_FieldList == nullptr) - m_LastField = nullptr; - else - { - curField = m_FieldList; - while (curField->getNextField() != nullptr) - curField = curField->getNextField(); - m_LastField = curField; - } - } - - // remove the hash entry for this field - std::transform(fieldName.begin(), fieldName.end(), fieldName.begin(), ::tolower); - std::pair ::iterator, std::multimap::iterator> range; - range = m_FieldNameToFieldMap.equal_range(fieldName); - for (std::multimap::iterator iter = range.first; iter != range.second; ++iter) - { - if (iter->second == fieldToRemove) - { - m_FieldNameToFieldMap.erase(iter); - break; - } - } - - // finally - delete this field - delete fieldToRemove; - - return true; -} - -bool TextBasedProtocolMessage::isHeaderComplete() const -{ - if (m_LastField == nullptr) - return false; - - return (m_LastField->getFieldName() == PCPP_END_OF_TEXT_BASED_PROTOCOL_HEADER); -} - -void TextBasedProtocolMessage::shiftFieldsOffset(HeaderField* fromField, int numOfBytesToShift) -{ - while (fromField != nullptr) - { - fromField->m_NameOffsetInMessage += numOfBytesToShift; - if (fromField->m_ValueOffsetInMessage != -1) - fromField->m_ValueOffsetInMessage += numOfBytesToShift; - fromField = fromField->getNextField(); - } -} - -HeaderField* TextBasedProtocolMessage::getFieldByName(std::string fieldName, int index) const -{ - std::transform(fieldName.begin(), fieldName.end(), fieldName.begin(), ::tolower); - - std::pair ::const_iterator, std::multimap::const_iterator> range; - range = m_FieldNameToFieldMap.equal_range(fieldName); - int i = 0; - for (std::multimap::const_iterator iter = range.first; iter != range.second; ++iter) - { - if (i == index) - return iter->second; - - i++; - } - - return nullptr; +TextBasedProtocolMessage::TextBasedProtocolMessage(uint8_t* data, + size_t dataLen, + Layer* prevLayer, + Packet* packet) + : Layer(data, dataLen, prevLayer, packet), m_FieldList(nullptr), + m_LastField(nullptr), m_FieldsOffset(0) {} + +TextBasedProtocolMessage::TextBasedProtocolMessage( + const TextBasedProtocolMessage& other) + : Layer(other) { + copyDataFrom(other); +} + +TextBasedProtocolMessage& +TextBasedProtocolMessage::operator=(const TextBasedProtocolMessage& other) { + Layer::operator=(other); + HeaderField* curField = m_FieldList; + while (curField != nullptr) { + HeaderField* temp = curField; + curField = curField->getNextField(); + delete temp; + } + + copyDataFrom(other); + + return *this; +} + +void TextBasedProtocolMessage::copyDataFrom( + const TextBasedProtocolMessage& other) { + // copy field list + if (other.m_FieldList != nullptr) { + m_FieldList = new HeaderField(*(other.m_FieldList)); + HeaderField* curField = m_FieldList; + curField->attachToTextBasedProtocolMessage( + this, other.m_FieldList->m_NameOffsetInMessage); + HeaderField* curOtherField = other.m_FieldList; + while (curOtherField->getNextField() != nullptr) { + HeaderField* newField = new HeaderField(*(curOtherField->getNextField())); + newField->attachToTextBasedProtocolMessage( + this, curOtherField->getNextField()->m_NameOffsetInMessage); + curField->setNextField(newField); + curField = curField->getNextField(); + curOtherField = curOtherField->getNextField(); + } + + m_LastField = curField; + } else { + m_FieldList = nullptr; + m_LastField = nullptr; + } + + m_FieldsOffset = other.m_FieldsOffset; + + // copy map + for (HeaderField* field = m_FieldList; field != nullptr; + field = field->getNextField()) { + m_FieldNameToFieldMap.insert( + std::pair(field->getFieldName(), field)); + } +} + +void TextBasedProtocolMessage::parseFields() { + char nameValueSeparator = getHeaderFieldNameValueSeparator(); + bool spacesAllowedBetweenNameAndValue = + spacesAllowedBetweenHeaderFieldNameAndValue(); + + HeaderField* firstField = + new HeaderField(this, m_FieldsOffset, nameValueSeparator, + spacesAllowedBetweenNameAndValue); + PCPP_LOG_DEBUG("Added new field: name='" + << firstField->getFieldName() + << "'; offset in packet=" << firstField->m_NameOffsetInMessage + << "; length=" << firstField->getFieldSize()); + PCPP_LOG_DEBUG(" Field value = " << firstField->getFieldValue()); + + if (m_FieldList == nullptr) + m_FieldList = firstField; + else + m_FieldList->setNextField(firstField); + + std::string fieldName = firstField->getFieldName(); + std::transform(fieldName.begin(), fieldName.end(), fieldName.begin(), + ::tolower); + m_FieldNameToFieldMap.insert( + std::pair(fieldName, firstField)); + + // Last field will be empty and contain just "\n" or "\r\n". This field will + // mark the end of the header + HeaderField* curField = m_FieldList; + int curOffset = m_FieldsOffset; + // last field can be one of: + // a.) \r\n\r\n or \n\n marking the end of the header + // b.) the end of the packet + while (!curField->isEndOfHeader() && + curOffset + curField->getFieldSize() < m_DataLen) { + curOffset += curField->getFieldSize(); + HeaderField* newField = new HeaderField(this, curOffset, nameValueSeparator, + spacesAllowedBetweenNameAndValue); + if (newField->getFieldSize() > 0) { + PCPP_LOG_DEBUG("Added new field: name='" + << newField->getFieldName() << "'; offset in packet=" + << newField->m_NameOffsetInMessage + << "; length=" << newField->getFieldSize()); + PCPP_LOG_DEBUG(" Field value = " << newField->getFieldValue()); + curField->setNextField(newField); + curField = newField; + fieldName = newField->getFieldName(); + std::transform(fieldName.begin(), fieldName.end(), fieldName.begin(), + ::tolower); + m_FieldNameToFieldMap.insert( + std::pair(fieldName, newField)); + } else { + delete newField; + break; + } + } + + m_LastField = curField; +} + +TextBasedProtocolMessage::~TextBasedProtocolMessage() { + while (m_FieldList != nullptr) { + HeaderField* temp = m_FieldList; + m_FieldList = m_FieldList->getNextField(); + delete temp; + } +} + +HeaderField* TextBasedProtocolMessage::addField(const std::string& fieldName, + const std::string& fieldValue) { + HeaderField newField(fieldName, fieldValue, + getHeaderFieldNameValueSeparator(), + spacesAllowedBetweenHeaderFieldNameAndValue()); + return addField(newField); +} + +HeaderField* TextBasedProtocolMessage::addField(const HeaderField& newField) { + return insertField(m_LastField, newField); +} + +HeaderField* TextBasedProtocolMessage::addEndOfHeader() { + HeaderField endOfHeaderField(PCPP_END_OF_TEXT_BASED_PROTOCOL_HEADER, "", '\0', + false); + return insertField(m_LastField, endOfHeaderField); +} + +HeaderField* +TextBasedProtocolMessage::insertField(HeaderField* prevField, + const std::string& fieldName, + const std::string& fieldValue) { + HeaderField newField(fieldName, fieldValue, + getHeaderFieldNameValueSeparator(), + spacesAllowedBetweenHeaderFieldNameAndValue()); + return insertField(prevField, newField); +} + +HeaderField* +TextBasedProtocolMessage::insertField(std::string prevFieldName, + const std::string& fieldName, + const std::string& fieldValue) { + if (prevFieldName == "") { + return insertField(nullptr, fieldName, fieldValue); + } else { + HeaderField* prevField = getFieldByName(prevFieldName); + if (prevField == nullptr) + return nullptr; + + return insertField(prevField, fieldName, fieldValue); + } +} + +HeaderField* +TextBasedProtocolMessage::insertField(HeaderField* prevField, + const HeaderField& newField) { + if (newField.m_TextBasedProtocolMessage != nullptr) { + PCPP_LOG_ERROR("This field is already associated with another message"); + return nullptr; + } + + if (prevField != nullptr && + prevField->getFieldName() == PCPP_END_OF_TEXT_BASED_PROTOCOL_HEADER) { + PCPP_LOG_ERROR("Cannot add a field after end of header"); + return nullptr; + } + + HeaderField* newFieldToAdd = new HeaderField(newField); + + int newFieldOffset = m_FieldsOffset; + if (prevField != nullptr) + newFieldOffset = + prevField->m_NameOffsetInMessage + prevField->getFieldSize(); + + // extend layer to make room for the new field. Field will be added just + // before the last field + if (!extendLayer(newFieldOffset, newFieldToAdd->getFieldSize())) { + PCPP_LOG_ERROR("Cannot extend layer to insert the header"); + delete newFieldToAdd; + return nullptr; + } + + HeaderField* curField = m_FieldList; + if (prevField != nullptr) + curField = prevField->getNextField(); + + // go over all fields after prevField and update their offsets + shiftFieldsOffset(curField, newFieldToAdd->getFieldSize()); + + // copy new field data to message + memcpy(m_Data + newFieldOffset, newFieldToAdd->m_NewFieldData, + newFieldToAdd->getFieldSize()); + + // attach new field to message + newFieldToAdd->attachToTextBasedProtocolMessage(this, newFieldOffset); + + // insert field into fields link list + if (prevField == nullptr) { + newFieldToAdd->setNextField(m_FieldList); + m_FieldList = newFieldToAdd; + } else { + newFieldToAdd->setNextField(prevField->getNextField()); + prevField->setNextField(newFieldToAdd); + } + + // if newField is the last field, update m_LastField + if (newFieldToAdd->getNextField() == nullptr) + m_LastField = newFieldToAdd; + + // insert the new field into name to field map + std::string fieldName = newFieldToAdd->getFieldName(); + std::transform(fieldName.begin(), fieldName.end(), fieldName.begin(), + ::tolower); + m_FieldNameToFieldMap.insert( + std::pair(fieldName, newFieldToAdd)); + + return newFieldToAdd; +} + +bool TextBasedProtocolMessage::removeField(std::string fieldName, int index) { + std::transform(fieldName.begin(), fieldName.end(), fieldName.begin(), + ::tolower); + + HeaderField* fieldToRemove = nullptr; + + std::pair::iterator, + std::multimap::iterator> + range; + range = m_FieldNameToFieldMap.equal_range(fieldName); + int i = 0; + for (std::multimap::iterator iter = range.first; + iter != range.second; ++iter) { + if (i == index) { + fieldToRemove = iter->second; + break; + } + + i++; + } + + if (fieldToRemove != nullptr) + return removeField(fieldToRemove); + else { + PCPP_LOG_ERROR("Cannot find field '" << fieldName << "'"); + return false; + } +} + +bool TextBasedProtocolMessage::removeField(HeaderField* fieldToRemove) { + if (fieldToRemove == nullptr) + return true; + + if (fieldToRemove->m_TextBasedProtocolMessage != this) { + PCPP_LOG_ERROR("Field isn't associated with this message"); + return false; + } + + std::string fieldName = fieldToRemove->getFieldName(); + + // shorten layer and delete this field + if (!shortenLayer(fieldToRemove->m_NameOffsetInMessage, + fieldToRemove->getFieldSize())) { + PCPP_LOG_ERROR("Cannot shorten layer"); + return false; + } + + // update offsets of all fields after this field + HeaderField* curField = fieldToRemove->getNextField(); + shiftFieldsOffset(curField, 0 - fieldToRemove->getFieldSize()); + // while (curField != NULL) + // { + // curField->m_NameOffsetInMessage -= + //fieldToRemove->getFieldSize(); if (curField->m_ValueOffsetInMessage != -1) + // curField->m_ValueOffsetInMessage -= + //fieldToRemove->getFieldSize(); + // + // curField = curField->getNextField(); + // } + + // update fields link list + if (fieldToRemove == m_FieldList) + m_FieldList = m_FieldList->getNextField(); + else { + curField = m_FieldList; + while (curField->getNextField() != fieldToRemove) + curField = curField->getNextField(); + + curField->setNextField(fieldToRemove->getNextField()); + } + + // re-calculate m_LastField if needed + if (fieldToRemove == m_LastField) { + if (m_FieldList == nullptr) + m_LastField = nullptr; + else { + curField = m_FieldList; + while (curField->getNextField() != nullptr) + curField = curField->getNextField(); + m_LastField = curField; + } + } + + // remove the hash entry for this field + std::transform(fieldName.begin(), fieldName.end(), fieldName.begin(), + ::tolower); + std::pair::iterator, + std::multimap::iterator> + range; + range = m_FieldNameToFieldMap.equal_range(fieldName); + for (std::multimap::iterator iter = range.first; + iter != range.second; ++iter) { + if (iter->second == fieldToRemove) { + m_FieldNameToFieldMap.erase(iter); + break; + } + } + + // finally - delete this field + delete fieldToRemove; + + return true; +} + +bool TextBasedProtocolMessage::isHeaderComplete() const { + if (m_LastField == nullptr) + return false; + + return (m_LastField->getFieldName() == + PCPP_END_OF_TEXT_BASED_PROTOCOL_HEADER); +} + +void TextBasedProtocolMessage::shiftFieldsOffset(HeaderField* fromField, + int numOfBytesToShift) { + while (fromField != nullptr) { + fromField->m_NameOffsetInMessage += numOfBytesToShift; + if (fromField->m_ValueOffsetInMessage != -1) + fromField->m_ValueOffsetInMessage += numOfBytesToShift; + fromField = fromField->getNextField(); + } +} + +HeaderField* TextBasedProtocolMessage::getFieldByName(std::string fieldName, + int index) const { + std::transform(fieldName.begin(), fieldName.end(), fieldName.begin(), + ::tolower); + + std::pair::const_iterator, + std::multimap::const_iterator> + range; + range = m_FieldNameToFieldMap.equal_range(fieldName); + int i = 0; + for (std::multimap::const_iterator iter = + range.first; + iter != range.second; ++iter) { + if (i == index) + return iter->second; + + i++; + } + + return nullptr; +} + +int TextBasedProtocolMessage::getFieldCount() const { + int result = 0; + + HeaderField* curField = getFirstField(); + while (curField != nullptr) { + if (!curField->isEndOfHeader()) + result++; + curField = curField->getNextField(); + } + + return result; } -int TextBasedProtocolMessage::getFieldCount() const -{ - int result = 0; +void TextBasedProtocolMessage::parseNextLayer() { + size_t headerLen = getHeaderLen(); + if (m_DataLen <= headerLen) + return; - HeaderField* curField = getFirstField(); - while (curField != nullptr) - { - if (!curField->isEndOfHeader()) - result++; - curField = curField->getNextField(); - } - - return result; -} - -void TextBasedProtocolMessage::parseNextLayer() -{ - size_t headerLen = getHeaderLen(); - if (m_DataLen <= headerLen) - return; - - m_NextLayer = new PayloadLayer(m_Data + headerLen, m_DataLen - headerLen, this, m_Packet); + m_NextLayer = new PayloadLayer(m_Data + headerLen, m_DataLen - headerLen, + this, m_Packet); } -size_t TextBasedProtocolMessage::getHeaderLen() const -{ - return m_LastField->m_NameOffsetInMessage + m_LastField->m_FieldSize; +size_t TextBasedProtocolMessage::getHeaderLen() const { + return m_LastField->m_NameOffsetInMessage + m_LastField->m_FieldSize; } -void TextBasedProtocolMessage::computeCalculateFields() -{ - //nothing to do for now +void TextBasedProtocolMessage::computeCalculateFields() { + // nothing to do for now } - - - - // -------- Class HeaderField ----------------- +HeaderField::HeaderField(TextBasedProtocolMessage* TextBasedProtocolMessage, + int offsetInMessage, char nameValueSeparator, + bool spacesAllowedBetweenNameAndValue) + : m_NewFieldData(nullptr), + m_TextBasedProtocolMessage(TextBasedProtocolMessage), + m_NameOffsetInMessage(offsetInMessage), m_NextField(nullptr), + m_NameValueSeparator(nameValueSeparator), + m_SpacesAllowedBetweenNameAndValue(spacesAllowedBetweenNameAndValue) { + char* fieldData = + (char*)(m_TextBasedProtocolMessage->m_Data + m_NameOffsetInMessage); + char* fieldEndPtr = (char*)memchr(fieldData, '\n', + m_TextBasedProtocolMessage->m_DataLen - + (size_t)m_NameOffsetInMessage); + if (fieldEndPtr == nullptr) + m_FieldSize = + tbp_my_own_strnlen(fieldData, m_TextBasedProtocolMessage->m_DataLen - + (size_t)m_NameOffsetInMessage); + else + m_FieldSize = fieldEndPtr - fieldData + 1; + + if (m_FieldSize == 0 || (*fieldData) == '\r' || (*fieldData) == '\n') { + m_FieldNameSize = -1; + m_ValueOffsetInMessage = -1; + m_FieldValueSize = -1; + m_FieldNameSize = -1; + m_IsEndOfHeaderField = true; + return; + } else + m_IsEndOfHeaderField = false; + + char* fieldValuePtr = (char*)memchr(fieldData, nameValueSeparator, + m_TextBasedProtocolMessage->m_DataLen - + (size_t)m_NameOffsetInMessage); + // could not find the position of the separator, meaning field value position + // is unknown + if (fieldValuePtr == nullptr || + (fieldEndPtr != nullptr && fieldValuePtr >= fieldEndPtr)) { + m_ValueOffsetInMessage = -1; + m_FieldValueSize = -1; + m_FieldNameSize = m_FieldSize; + } else { + m_FieldNameSize = fieldValuePtr - fieldData; + // Header field looks like this: [separator] So fieldValuePtr give us the position of the + // separator. Value offset is the first non-space byte forward + fieldValuePtr++; + + // reached the end of the packet and value start offset wasn't found + if ((size_t)(fieldValuePtr - + (char*)(m_TextBasedProtocolMessage->m_Data)) >= + m_TextBasedProtocolMessage->getDataLen()) { + m_ValueOffsetInMessage = -1; + m_FieldValueSize = -1; + return; + } + + if (spacesAllowedBetweenNameAndValue) { + // advance fieldValuePtr 1 byte forward while didn't get to end of packet + // and fieldValuePtr points to a space char + while ( + (size_t)(fieldValuePtr - (char*)m_TextBasedProtocolMessage->m_Data) < + m_TextBasedProtocolMessage->getDataLen() && + (*fieldValuePtr) == ' ') { + fieldValuePtr++; + } + } + + // reached the end of the packet and value start offset wasn't found + if ((size_t)(fieldValuePtr - + (char*)(m_TextBasedProtocolMessage->m_Data)) >= + m_TextBasedProtocolMessage->getDataLen()) { + m_ValueOffsetInMessage = -1; + m_FieldValueSize = -1; + } else { + m_ValueOffsetInMessage = + fieldValuePtr - (char*)m_TextBasedProtocolMessage->m_Data; + // couldn't find the end of the field, so assuming the field value length + // is from m_ValueOffsetInMessage until the end of the packet + if (fieldEndPtr == nullptr) + m_FieldValueSize = (char*)(m_TextBasedProtocolMessage->m_Data + + m_TextBasedProtocolMessage->getDataLen()) - + fieldValuePtr; + else { + m_FieldValueSize = fieldEndPtr - fieldValuePtr; + // if field ends with \r\n, decrease the value length by 1 + if ((*(--fieldEndPtr)) == '\r') + m_FieldValueSize--; + } + } + } +} + +HeaderField::HeaderField(const std::string& name, const std::string& value, + char nameValueSeparator, + bool spacesAllowedBetweenNameAndValue) { + m_NameValueSeparator = nameValueSeparator; + m_SpacesAllowedBetweenNameAndValue = spacesAllowedBetweenNameAndValue; + initNewField(std::move(name), std::move(value)); +} + +void HeaderField::initNewField(const std::string& name, + const std::string& value) { + m_TextBasedProtocolMessage = nullptr; + m_NameOffsetInMessage = 0; + m_NextField = nullptr; + + // first building the name-value separator + std::string nameValueSeparation(1, m_NameValueSeparator); + if (m_SpacesAllowedBetweenNameAndValue) + nameValueSeparation += " "; + + // Field size is: name_length + separator_len + value_length + '\r\n' + if (name != PCPP_END_OF_TEXT_BASED_PROTOCOL_HEADER) + m_FieldSize = + name.length() + nameValueSeparation.length() + value.length() + 2; + else + // Field is \r\n (2B) + m_FieldSize = 2; + + m_NewFieldData = new uint8_t[m_FieldSize]; + std::string fieldData; + + if (name != PCPP_END_OF_TEXT_BASED_PROTOCOL_HEADER) + fieldData = name + nameValueSeparation + value + "\r\n"; + else + fieldData = "\r\n"; + + // copy field data to m_NewFieldData + memcpy(m_NewFieldData, fieldData.c_str(), m_FieldSize); + + // calculate value offset + if (name != PCPP_END_OF_TEXT_BASED_PROTOCOL_HEADER) + m_ValueOffsetInMessage = name.length() + nameValueSeparation.length(); + else + m_ValueOffsetInMessage = 0; + m_FieldNameSize = name.length(); + m_FieldValueSize = value.length(); + + if (name != PCPP_END_OF_TEXT_BASED_PROTOCOL_HEADER) + m_IsEndOfHeaderField = false; + else + m_IsEndOfHeaderField = true; +} + +HeaderField::~HeaderField() { + if (m_NewFieldData != nullptr) + delete[] m_NewFieldData; +} + +HeaderField::HeaderField(const HeaderField& other) + : m_NameValueSeparator('\0'), m_SpacesAllowedBetweenNameAndValue(false) { + m_NameValueSeparator = other.m_NameValueSeparator; + m_SpacesAllowedBetweenNameAndValue = other.m_SpacesAllowedBetweenNameAndValue; + initNewField(other.getFieldName(), other.getFieldValue()); +} + +HeaderField& HeaderField::operator=(const HeaderField& other) { + m_NameValueSeparator = other.m_NameValueSeparator; + m_SpacesAllowedBetweenNameAndValue = other.m_SpacesAllowedBetweenNameAndValue; + if (m_NewFieldData != nullptr) + delete[] m_NewFieldData; + initNewField(other.getFieldName(), other.getFieldValue()); + + return (*this); +} + +char* HeaderField::getData() const { + if (m_TextBasedProtocolMessage == nullptr) + return (char*)m_NewFieldData; + else + return (char*)(m_TextBasedProtocolMessage->m_Data); +} + +void HeaderField::setNextField(HeaderField* nextField) { + m_NextField = nextField; +} + +HeaderField* HeaderField::getNextField() const { return m_NextField; } + +std::string HeaderField::getFieldName() const { + std::string result; + + if (m_FieldNameSize != (size_t)-1) + result.assign((const char*)(((HeaderField*)this)->getData() + + m_NameOffsetInMessage), + m_FieldNameSize); + + return result; +} + +std::string HeaderField::getFieldValue() const { + std::string result; + if (m_ValueOffsetInMessage != -1) + result.assign((const char*)(((HeaderField*)this)->getData() + + m_ValueOffsetInMessage), + m_FieldValueSize); + return result; +} -HeaderField::HeaderField(TextBasedProtocolMessage* TextBasedProtocolMessage, int offsetInMessage, char nameValueSeparator, bool spacesAllowedBetweenNameAndValue) : - m_NewFieldData(nullptr), m_TextBasedProtocolMessage(TextBasedProtocolMessage), m_NameOffsetInMessage(offsetInMessage), m_NextField(nullptr), - m_NameValueSeparator(nameValueSeparator), m_SpacesAllowedBetweenNameAndValue(spacesAllowedBetweenNameAndValue) -{ - char* fieldData = (char*)(m_TextBasedProtocolMessage->m_Data + m_NameOffsetInMessage); - char* fieldEndPtr = (char*)memchr(fieldData, '\n', m_TextBasedProtocolMessage->m_DataLen - (size_t)m_NameOffsetInMessage); - if (fieldEndPtr == nullptr) - m_FieldSize = tbp_my_own_strnlen(fieldData, m_TextBasedProtocolMessage->m_DataLen - (size_t)m_NameOffsetInMessage); - else - m_FieldSize = fieldEndPtr - fieldData + 1; - - if (m_FieldSize == 0 || (*fieldData) == '\r' || (*fieldData) == '\n') - { - m_FieldNameSize = -1; - m_ValueOffsetInMessage = -1; - m_FieldValueSize = -1; - m_FieldNameSize = -1; - m_IsEndOfHeaderField = true; - return; - } - else - m_IsEndOfHeaderField = false; - - char* fieldValuePtr = (char*)memchr(fieldData, nameValueSeparator, m_TextBasedProtocolMessage->m_DataLen - (size_t)m_NameOffsetInMessage); - // could not find the position of the separator, meaning field value position is unknown - if (fieldValuePtr == nullptr || (fieldEndPtr != nullptr && fieldValuePtr >= fieldEndPtr)) - { - m_ValueOffsetInMessage = -1; - m_FieldValueSize = -1; - m_FieldNameSize = m_FieldSize; - } - else - { - m_FieldNameSize = fieldValuePtr - fieldData; - // Header field looks like this: [separator] - // So fieldValuePtr give us the position of the separator. Value offset is the first non-space byte forward - fieldValuePtr++; - - // reached the end of the packet and value start offset wasn't found - if ((size_t)(fieldValuePtr - (char*)(m_TextBasedProtocolMessage->m_Data)) >= m_TextBasedProtocolMessage->getDataLen()) - { - m_ValueOffsetInMessage = -1; - m_FieldValueSize = -1; - return; - } - - if (spacesAllowedBetweenNameAndValue) - { - // advance fieldValuePtr 1 byte forward while didn't get to end of packet and fieldValuePtr points to a space char - while ((size_t)(fieldValuePtr - (char*)m_TextBasedProtocolMessage->m_Data) < m_TextBasedProtocolMessage->getDataLen() && (*fieldValuePtr) == ' ') - { - fieldValuePtr++; - } - } - - // reached the end of the packet and value start offset wasn't found - if ((size_t)(fieldValuePtr - (char*)(m_TextBasedProtocolMessage->m_Data)) >= m_TextBasedProtocolMessage->getDataLen()) - { - m_ValueOffsetInMessage = -1; - m_FieldValueSize = -1; - } - else - { - m_ValueOffsetInMessage = fieldValuePtr - (char*)m_TextBasedProtocolMessage->m_Data; - // couldn't find the end of the field, so assuming the field value length is from m_ValueOffsetInMessage until the end of the packet - if (fieldEndPtr == nullptr) - m_FieldValueSize = (char*)(m_TextBasedProtocolMessage->m_Data + m_TextBasedProtocolMessage->getDataLen()) - fieldValuePtr; - else - { - m_FieldValueSize = fieldEndPtr - fieldValuePtr; - // if field ends with \r\n, decrease the value length by 1 - if ((*(--fieldEndPtr)) == '\r') - m_FieldValueSize--; - } - } - } -} - -HeaderField::HeaderField(const std::string& name, const std::string& value, char nameValueSeparator, bool spacesAllowedBetweenNameAndValue) -{ - m_NameValueSeparator = nameValueSeparator; - m_SpacesAllowedBetweenNameAndValue = spacesAllowedBetweenNameAndValue; - initNewField(std::move(name), std::move(value)); -} - -void HeaderField::initNewField(const std::string& name, const std::string& value) -{ - m_TextBasedProtocolMessage = nullptr; - m_NameOffsetInMessage = 0; - m_NextField = nullptr; - - // first building the name-value separator - std::string nameValueSeparation(1, m_NameValueSeparator); - if (m_SpacesAllowedBetweenNameAndValue) - nameValueSeparation += " "; - - // Field size is: name_length + separator_len + value_length + '\r\n' - if (name != PCPP_END_OF_TEXT_BASED_PROTOCOL_HEADER) - m_FieldSize = name.length() + nameValueSeparation.length() + value.length() + 2; - else - // Field is \r\n (2B) - m_FieldSize = 2; - - m_NewFieldData = new uint8_t[m_FieldSize]; - std::string fieldData; - - if (name != PCPP_END_OF_TEXT_BASED_PROTOCOL_HEADER) - fieldData = name + nameValueSeparation + value + "\r\n"; - else - fieldData = "\r\n"; - - // copy field data to m_NewFieldData - memcpy(m_NewFieldData, fieldData.c_str(), m_FieldSize); - - // calculate value offset - if (name != PCPP_END_OF_TEXT_BASED_PROTOCOL_HEADER) - m_ValueOffsetInMessage = name.length() + nameValueSeparation.length(); - else - m_ValueOffsetInMessage = 0; - m_FieldNameSize = name.length(); - m_FieldValueSize = value.length(); - - if (name != PCPP_END_OF_TEXT_BASED_PROTOCOL_HEADER) - m_IsEndOfHeaderField = false; - else - m_IsEndOfHeaderField = true; -} - -HeaderField::~HeaderField() -{ - if (m_NewFieldData != nullptr) - delete [] m_NewFieldData; -} - -HeaderField::HeaderField(const HeaderField& other) : m_NameValueSeparator('\0'), m_SpacesAllowedBetweenNameAndValue(false) -{ - m_NameValueSeparator = other.m_NameValueSeparator; - m_SpacesAllowedBetweenNameAndValue = other.m_SpacesAllowedBetweenNameAndValue; - initNewField(other.getFieldName(), other.getFieldValue()); -} - -HeaderField& HeaderField::operator=(const HeaderField& other) -{ - m_NameValueSeparator = other.m_NameValueSeparator; - m_SpacesAllowedBetweenNameAndValue = other.m_SpacesAllowedBetweenNameAndValue; - if (m_NewFieldData != nullptr) - delete [] m_NewFieldData; - initNewField(other.getFieldName(), other.getFieldValue()); - - return (*this); -} - -char* HeaderField::getData() const -{ - if (m_TextBasedProtocolMessage == nullptr) - return (char*)m_NewFieldData; - else - return (char*)(m_TextBasedProtocolMessage->m_Data); -} - -void HeaderField::setNextField(HeaderField* nextField) -{ - m_NextField = nextField; -} - -HeaderField* HeaderField::getNextField() const -{ - return m_NextField; -} - -std::string HeaderField::getFieldName() const -{ - std::string result; - - if (m_FieldNameSize != (size_t)-1) - result.assign((const char*)(((HeaderField*)this)->getData() + m_NameOffsetInMessage), m_FieldNameSize); - - return result; -} - -std::string HeaderField::getFieldValue() const -{ - std::string result; - if (m_ValueOffsetInMessage != -1) - result.assign((const char*)(((HeaderField*)this)->getData() + m_ValueOffsetInMessage), m_FieldValueSize); - return result; -} - -bool HeaderField::setFieldValue(const std::string& newValue) -{ - // Field isn't linked with any message yet - if (m_TextBasedProtocolMessage == nullptr) - { - std::string name = getFieldName(); - delete [] m_NewFieldData; - initNewField(name, newValue); - return true; - } +bool HeaderField::setFieldValue(const std::string& newValue) { + // Field isn't linked with any message yet + if (m_TextBasedProtocolMessage == nullptr) { + std::string name = getFieldName(); + delete[] m_NewFieldData; + initNewField(name, newValue); + return true; + } + + std::string curValue = getFieldValue(); + int lengthDifference = newValue.length() - curValue.length(); + // new value is longer than current value + if (lengthDifference > 0) { + if (!m_TextBasedProtocolMessage->extendLayer(m_ValueOffsetInMessage, + lengthDifference)) { + PCPP_LOG_ERROR("Could not extend layer"); + return false; + } + } + // new value is shorter than current value + else if (lengthDifference < 0) { + if (!m_TextBasedProtocolMessage->shortenLayer(m_ValueOffsetInMessage, + 0 - lengthDifference)) { + PCPP_LOG_ERROR("Could not shorten layer"); + return false; + } + } - std::string curValue = getFieldValue(); - int lengthDifference = newValue.length() - curValue.length(); - // new value is longer than current value - if (lengthDifference > 0) - { - if (!m_TextBasedProtocolMessage->extendLayer(m_ValueOffsetInMessage, lengthDifference)) - { - PCPP_LOG_ERROR("Could not extend layer"); - return false; - } - } - // new value is shorter than current value - else if (lengthDifference < 0) - { - if (!m_TextBasedProtocolMessage->shortenLayer(m_ValueOffsetInMessage, 0 - lengthDifference)) - { - PCPP_LOG_ERROR("Could not shorten layer"); - return false; - } - } + if (lengthDifference != 0) + m_TextBasedProtocolMessage->shiftFieldsOffset(getNextField(), + lengthDifference); - if (lengthDifference != 0) - m_TextBasedProtocolMessage->shiftFieldsOffset(getNextField(), lengthDifference); + // update sizes + m_FieldValueSize += lengthDifference; + m_FieldSize += lengthDifference; - // update sizes - m_FieldValueSize += lengthDifference; - m_FieldSize += lengthDifference; + // write new value to field data + memcpy(getData() + m_ValueOffsetInMessage, newValue.c_str(), + newValue.length()); - // write new value to field data - memcpy(getData() + m_ValueOffsetInMessage, newValue.c_str(), newValue.length()); - - return true; + return true; } -void HeaderField::attachToTextBasedProtocolMessage(TextBasedProtocolMessage* message, int fieldOffsetInMessage) -{ - if (m_TextBasedProtocolMessage != nullptr && m_TextBasedProtocolMessage != message) - { - PCPP_LOG_ERROR("Header field already associated with another message"); - return; - } - - if (m_NewFieldData == nullptr) - { - PCPP_LOG_ERROR("Header field doesn't have new field data"); - return; - } +void HeaderField::attachToTextBasedProtocolMessage( + TextBasedProtocolMessage* message, int fieldOffsetInMessage) { + if (m_TextBasedProtocolMessage != nullptr && + m_TextBasedProtocolMessage != message) { + PCPP_LOG_ERROR("Header field already associated with another message"); + return; + } + + if (m_NewFieldData == nullptr) { + PCPP_LOG_ERROR("Header field doesn't have new field data"); + return; + } - delete [] m_NewFieldData; - m_NewFieldData = nullptr; - m_TextBasedProtocolMessage = message; + delete[] m_NewFieldData; + m_NewFieldData = nullptr; + m_TextBasedProtocolMessage = message; - int valueAndNameDifference = m_ValueOffsetInMessage - m_NameOffsetInMessage; - m_NameOffsetInMessage = fieldOffsetInMessage; - m_ValueOffsetInMessage = m_NameOffsetInMessage + valueAndNameDifference; + int valueAndNameDifference = m_ValueOffsetInMessage - m_NameOffsetInMessage; + m_NameOffsetInMessage = fieldOffsetInMessage; + m_ValueOffsetInMessage = m_NameOffsetInMessage + valueAndNameDifference; } -} +} // namespace pcpp diff --git a/Packet++/src/TpktLayer.cpp b/Packet++/src/TpktLayer.cpp index 038791d18d..f6490da4f8 100644 --- a/Packet++/src/TpktLayer.cpp +++ b/Packet++/src/TpktLayer.cpp @@ -1,59 +1,62 @@ #include "TpktLayer.h" -#include "EndianPortable.h" -#include "TcpLayer.h" #include "CotpLayer.h" +#include "EndianPortable.h" #include "PayloadLayer.h" +#include "TcpLayer.h" #include #include #include -namespace pcpp -{ - TpktLayer::TpktLayer(uint8_t version, uint16_t length) - { - m_DataLen = sizeof(tpkthdr); - m_Data = new uint8_t[m_DataLen]; - memset(m_Data, 0, m_DataLen); - tpkthdr *tpktHdr = getTpktHeader(); - tpktHdr->version = version; - tpktHdr->reserved = 0; - tpktHdr->length = htobe16(length); - m_Protocol = TPKT; - } - - uint8_t TpktLayer::getReserved() const { return getTpktHeader()->reserved; } - - uint8_t TpktLayer::getVersion() const { return getTpktHeader()->version; } - - uint16_t TpktLayer::getLength() const { return htobe16(getTpktHeader()->length); } - - void TpktLayer::setLength(uint16_t length) const { getTpktHeader()->length = htobe16(length); } - - void TpktLayer::setVersion(uint8_t version) const { getTpktHeader()->version = version; } - - std::string TpktLayer::toString() const - { - std::ostringstream versionStream; - versionStream << std::to_string(getVersion()); - std::ostringstream lengthStream; - lengthStream << std::to_string(getLength()); - - return "TPKT Layer, version: " + versionStream.str() + ", length: " + lengthStream.str(); - } - - void TpktLayer::parseNextLayer() - { - size_t headerLen = getHeaderLen(); - if (m_DataLen <= headerLen) - return; - - uint8_t *payload = m_Data + headerLen; - size_t payloadLen = m_DataLen - headerLen; - - if (CotpLayer::isDataValid(payload, payloadLen)) { - m_NextLayer = new CotpLayer(payload, payloadLen, this, m_Packet); - } else - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - } +namespace pcpp { +TpktLayer::TpktLayer(uint8_t version, uint16_t length) { + m_DataLen = sizeof(tpkthdr); + m_Data = new uint8_t[m_DataLen]; + memset(m_Data, 0, m_DataLen); + tpkthdr* tpktHdr = getTpktHeader(); + tpktHdr->version = version; + tpktHdr->reserved = 0; + tpktHdr->length = htobe16(length); + m_Protocol = TPKT; +} + +uint8_t TpktLayer::getReserved() const { return getTpktHeader()->reserved; } + +uint8_t TpktLayer::getVersion() const { return getTpktHeader()->version; } + +uint16_t TpktLayer::getLength() const { + return htobe16(getTpktHeader()->length); +} + +void TpktLayer::setLength(uint16_t length) const { + getTpktHeader()->length = htobe16(length); +} + +void TpktLayer::setVersion(uint8_t version) const { + getTpktHeader()->version = version; +} + +std::string TpktLayer::toString() const { + std::ostringstream versionStream; + versionStream << std::to_string(getVersion()); + std::ostringstream lengthStream; + lengthStream << std::to_string(getLength()); + + return "TPKT Layer, version: " + versionStream.str() + + ", length: " + lengthStream.str(); +} + +void TpktLayer::parseNextLayer() { + size_t headerLen = getHeaderLen(); + if (m_DataLen <= headerLen) + return; + + uint8_t* payload = m_Data + headerLen; + size_t payloadLen = m_DataLen - headerLen; + + if (CotpLayer::isDataValid(payload, payloadLen)) { + m_NextLayer = new CotpLayer(payload, payloadLen, this, m_Packet); + } else + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); +} } // namespace pcpp diff --git a/Packet++/src/UdpLayer.cpp b/Packet++/src/UdpLayer.cpp index cc92e420ba..c7fb4ce9ef 100644 --- a/Packet++/src/UdpLayer.cpp +++ b/Packet++/src/UdpLayer.cpp @@ -1,152 +1,159 @@ #define LOG_MODULE PacketLogModuleUdpLayer -#include "EndianPortable.h" #include "UdpLayer.h" -#include "PayloadLayer.h" -#include "IPv4Layer.h" -#include "IPv6Layer.h" -#include "DnsLayer.h" #include "DhcpLayer.h" #include "DhcpV6Layer.h" -#include "VxlanLayer.h" -#include "SipLayer.h" -#include "RadiusLayer.h" +#include "DnsLayer.h" +#include "EndianPortable.h" #include "GtpLayer.h" +#include "IPv4Layer.h" +#include "IPv6Layer.h" +#include "Logger.h" #include "NtpLayer.h" +#include "PacketUtils.h" +#include "PayloadLayer.h" +#include "RadiusLayer.h" +#include "SipLayer.h" #include "SomeIpLayer.h" +#include "VxlanLayer.h" #include "WakeOnLanLayer.h" -#include "PacketUtils.h" -#include "Logger.h" -#include #include +#include -namespace pcpp -{ - -UdpLayer::UdpLayer(uint16_t portSrc, uint16_t portDst) -{ - const size_t headerLen = sizeof(udphdr); - m_DataLen = headerLen; - m_Data = new uint8_t[headerLen]; - memset(m_Data, 0, headerLen); - udphdr* udpHdr = (udphdr*)m_Data; - udpHdr->portDst = htobe16(portDst); - udpHdr->portSrc = htobe16(portSrc); - m_Protocol = UDP; +namespace pcpp { + +UdpLayer::UdpLayer(uint16_t portSrc, uint16_t portDst) { + const size_t headerLen = sizeof(udphdr); + m_DataLen = headerLen; + m_Data = new uint8_t[headerLen]; + memset(m_Data, 0, headerLen); + udphdr* udpHdr = (udphdr*)m_Data; + udpHdr->portDst = htobe16(portDst); + udpHdr->portSrc = htobe16(portSrc); + m_Protocol = UDP; } -uint16_t UdpLayer::getSrcPort() const -{ - return be16toh(getUdpHeader()->portSrc); +uint16_t UdpLayer::getSrcPort() const { + return be16toh(getUdpHeader()->portSrc); } -uint16_t UdpLayer::getDstPort() const -{ - return be16toh(getUdpHeader()->portDst); +uint16_t UdpLayer::getDstPort() const { + return be16toh(getUdpHeader()->portDst); } -uint16_t UdpLayer::calculateChecksum(bool writeResultToPacket) -{ - udphdr* udpHdr = (udphdr*)m_Data; - uint16_t checksumRes = 0; - uint16_t currChecksumValue = udpHdr->headerChecksum; +uint16_t UdpLayer::calculateChecksum(bool writeResultToPacket) { + udphdr* udpHdr = (udphdr*)m_Data; + uint16_t checksumRes = 0; + uint16_t currChecksumValue = udpHdr->headerChecksum; - if (m_PrevLayer != nullptr) - { - udpHdr->headerChecksum = 0; - PCPP_LOG_DEBUG("UDP data len = " << m_DataLen); + if (m_PrevLayer != nullptr) { + udpHdr->headerChecksum = 0; + PCPP_LOG_DEBUG("UDP data len = " << m_DataLen); - if (m_PrevLayer->getProtocol() == IPv4) - { - IPv4Address srcIP = ((IPv4Layer*)m_PrevLayer)->getSrcIPv4Address(); - IPv4Address dstIP = ((IPv4Layer*)m_PrevLayer)->getDstIPv4Address(); + if (m_PrevLayer->getProtocol() == IPv4) { + IPv4Address srcIP = ((IPv4Layer*)m_PrevLayer)->getSrcIPv4Address(); + IPv4Address dstIP = ((IPv4Layer*)m_PrevLayer)->getDstIPv4Address(); - checksumRes = pcpp::computePseudoHdrChecksum((uint8_t *) udpHdr, getDataLen(), IPAddress::IPv4AddressType, - PACKETPP_IPPROTO_UDP, srcIP, dstIP); + checksumRes = pcpp::computePseudoHdrChecksum( + (uint8_t*)udpHdr, getDataLen(), IPAddress::IPv4AddressType, + PACKETPP_IPPROTO_UDP, srcIP, dstIP); - PCPP_LOG_DEBUG("calculated IPv4 UDP checksum = 0x" << std::uppercase << std::hex << checksumRes); - } - else if (m_PrevLayer->getProtocol() == IPv6) - { - IPv6Address srcIP = ((IPv6Layer*)m_PrevLayer)->getSrcIPv6Address(); - IPv6Address dstIP = ((IPv6Layer*)m_PrevLayer)->getDstIPv6Address(); + PCPP_LOG_DEBUG("calculated IPv4 UDP checksum = 0x" + << std::uppercase << std::hex << checksumRes); + } else if (m_PrevLayer->getProtocol() == IPv6) { + IPv6Address srcIP = ((IPv6Layer*)m_PrevLayer)->getSrcIPv6Address(); + IPv6Address dstIP = ((IPv6Layer*)m_PrevLayer)->getDstIPv6Address(); - checksumRes = computePseudoHdrChecksum((uint8_t *) udpHdr, getDataLen(), IPAddress::IPv6AddressType, - PACKETPP_IPPROTO_UDP, srcIP, dstIP); + checksumRes = computePseudoHdrChecksum( + (uint8_t*)udpHdr, getDataLen(), IPAddress::IPv6AddressType, + PACKETPP_IPPROTO_UDP, srcIP, dstIP); - PCPP_LOG_DEBUG("calculated IPv6 UDP checksum = 0xX" << std::uppercase << std::hex << checksumRes); - } - } + PCPP_LOG_DEBUG("calculated IPv6 UDP checksum = 0xX" + << std::uppercase << std::hex << checksumRes); + } + } - if (checksumRes == 0) - checksumRes = 0xffff; + if (checksumRes == 0) + checksumRes = 0xffff; - if(writeResultToPacket) - udpHdr->headerChecksum = htobe16(checksumRes); - else - udpHdr->headerChecksum = currChecksumValue; + if (writeResultToPacket) + udpHdr->headerChecksum = htobe16(checksumRes); + else + udpHdr->headerChecksum = currChecksumValue; - return checksumRes; + return checksumRes; } -void UdpLayer::parseNextLayer() -{ - if (m_DataLen <= sizeof(udphdr)) - return; - - uint16_t portDst = getDstPort(); - uint16_t portSrc = getSrcPort(); - - uint8_t* udpData = m_Data + sizeof(udphdr); - size_t udpDataLen = m_DataLen - sizeof(udphdr); - - if ((portSrc == 68 && portDst == 67) || (portSrc == 67 && portDst == 68) || (portSrc == 67 && portDst == 67)) - m_NextLayer = new DhcpLayer(udpData, udpDataLen, this, m_Packet); - else if (VxlanLayer::isVxlanPort(portDst)) - m_NextLayer = new VxlanLayer(udpData, udpDataLen, this, m_Packet); - else if (DnsLayer::isDataValid(udpData, udpDataLen) && (DnsLayer::isDnsPort(portDst) || DnsLayer::isDnsPort(portSrc))) - m_NextLayer = new DnsLayer(udpData, udpDataLen, this, m_Packet); - else if(SipLayer::isSipPort(portDst) || SipLayer::isSipPort(portSrc)) - { - if (SipRequestFirstLine::parseMethod((char*)udpData, udpDataLen) != SipRequestLayer::SipMethodUnknown) - m_NextLayer = new SipRequestLayer(udpData, udpDataLen, this, m_Packet); - else if (SipResponseFirstLine::parseStatusCode((char*)udpData, udpDataLen) != SipResponseLayer::SipStatusCodeUnknown - && SipResponseFirstLine::parseVersion((char*)udpData, udpDataLen) != "") - m_NextLayer = new SipResponseLayer(udpData, udpDataLen, this, m_Packet); - else - m_NextLayer = new PayloadLayer(udpData, udpDataLen, this, m_Packet); - } - else if ((RadiusLayer::isRadiusPort(portDst) || RadiusLayer::isRadiusPort(portSrc)) && RadiusLayer::isDataValid(udpData, udpDataLen)) - m_NextLayer = new RadiusLayer(udpData, udpDataLen, this, m_Packet); - else if ((GtpV1Layer::isGTPv1Port(portDst) || GtpV1Layer::isGTPv1Port(portSrc)) && GtpV1Layer::isGTPv1(udpData, udpDataLen)) - m_NextLayer = new GtpV1Layer(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); - else if ((NtpLayer::isNTPPort(portSrc) || NtpLayer::isNTPPort(portDst)) && NtpLayer::isDataValid(udpData, udpDataLen)) - m_NextLayer = new NtpLayer(udpData, udpDataLen, this, m_Packet); - else if (SomeIpLayer::isSomeIpPort(portSrc) || SomeIpLayer::isSomeIpPort(portDst)) - m_NextLayer = SomeIpLayer::parseSomeIpLayer(udpData, udpDataLen, this, m_Packet); - else if ((WakeOnLanLayer::isWakeOnLanPort(portDst) && WakeOnLanLayer::isDataValid(udpData, udpDataLen))) - m_NextLayer = new WakeOnLanLayer(udpData, udpDataLen, this, m_Packet); - else - m_NextLayer = new PayloadLayer(udpData, udpDataLen, this, m_Packet); +void UdpLayer::parseNextLayer() { + if (m_DataLen <= sizeof(udphdr)) + return; + + uint16_t portDst = getDstPort(); + uint16_t portSrc = getSrcPort(); + + uint8_t* udpData = m_Data + sizeof(udphdr); + size_t udpDataLen = m_DataLen - sizeof(udphdr); + + if ((portSrc == 68 && portDst == 67) || (portSrc == 67 && portDst == 68) || + (portSrc == 67 && portDst == 67)) + m_NextLayer = new DhcpLayer(udpData, udpDataLen, this, m_Packet); + else if (VxlanLayer::isVxlanPort(portDst)) + m_NextLayer = new VxlanLayer(udpData, udpDataLen, this, m_Packet); + else if (DnsLayer::isDataValid(udpData, udpDataLen) && + (DnsLayer::isDnsPort(portDst) || DnsLayer::isDnsPort(portSrc))) + m_NextLayer = new DnsLayer(udpData, udpDataLen, this, m_Packet); + else if (SipLayer::isSipPort(portDst) || SipLayer::isSipPort(portSrc)) { + if (SipRequestFirstLine::parseMethod((char*)udpData, udpDataLen) != + SipRequestLayer::SipMethodUnknown) + m_NextLayer = new SipRequestLayer(udpData, udpDataLen, this, m_Packet); + else if (SipResponseFirstLine::parseStatusCode((char*)udpData, + udpDataLen) != + SipResponseLayer::SipStatusCodeUnknown && + SipResponseFirstLine::parseVersion((char*)udpData, udpDataLen) != + "") + m_NextLayer = new SipResponseLayer(udpData, udpDataLen, this, m_Packet); + else + m_NextLayer = new PayloadLayer(udpData, udpDataLen, this, m_Packet); + } else if ((RadiusLayer::isRadiusPort(portDst) || + RadiusLayer::isRadiusPort(portSrc)) && + RadiusLayer::isDataValid(udpData, udpDataLen)) + m_NextLayer = new RadiusLayer(udpData, udpDataLen, this, m_Packet); + else if ((GtpV1Layer::isGTPv1Port(portDst) || + GtpV1Layer::isGTPv1Port(portSrc)) && + GtpV1Layer::isGTPv1(udpData, udpDataLen)) + m_NextLayer = new GtpV1Layer(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); + else if ((NtpLayer::isNTPPort(portSrc) || NtpLayer::isNTPPort(portDst)) && + NtpLayer::isDataValid(udpData, udpDataLen)) + m_NextLayer = new NtpLayer(udpData, udpDataLen, this, m_Packet); + else if (SomeIpLayer::isSomeIpPort(portSrc) || + SomeIpLayer::isSomeIpPort(portDst)) + m_NextLayer = + SomeIpLayer::parseSomeIpLayer(udpData, udpDataLen, this, m_Packet); + else if ((WakeOnLanLayer::isWakeOnLanPort(portDst) && + WakeOnLanLayer::isDataValid(udpData, udpDataLen))) + m_NextLayer = new WakeOnLanLayer(udpData, udpDataLen, this, m_Packet); + else + m_NextLayer = new PayloadLayer(udpData, udpDataLen, this, m_Packet); } -void UdpLayer::computeCalculateFields() -{ - udphdr* udpHdr = (udphdr*)m_Data; - udpHdr->length = htobe16(m_DataLen); - calculateChecksum(true); +void UdpLayer::computeCalculateFields() { + udphdr* udpHdr = (udphdr*)m_Data; + udpHdr->length = htobe16(m_DataLen); + calculateChecksum(true); } -std::string UdpLayer::toString() const -{ - std::ostringstream srcPortStream; - srcPortStream << getSrcPort(); - std::ostringstream dstPortStream; - dstPortStream << getDstPort(); +std::string UdpLayer::toString() const { + std::ostringstream srcPortStream; + srcPortStream << getSrcPort(); + std::ostringstream dstPortStream; + dstPortStream << getDstPort(); - return "UDP Layer, Src port: " + srcPortStream.str() + ", Dst port: " + dstPortStream.str(); + return "UDP Layer, Src port: " + srcPortStream.str() + + ", Dst port: " + dstPortStream.str(); } } // namespace pcpp diff --git a/Packet++/src/VlanLayer.cpp b/Packet++/src/VlanLayer.cpp index 0556e5fc89..ef21c954c6 100644 --- a/Packet++/src/VlanLayer.cpp +++ b/Packet++/src/VlanLayer.cpp @@ -1,147 +1,152 @@ #define LOG_MODULE PacketLogModuleVlanLayer #include "VlanLayer.h" +#include "ArpLayer.h" +#include "EndianPortable.h" #include "IPv4Layer.h" #include "IPv6Layer.h" -#include "PayloadLayer.h" -#include "ArpLayer.h" -#include "PPPoELayer.h" -#include "MplsLayer.h" #include "LLCLayer.h" -#include +#include "MplsLayer.h" +#include "PPPoELayer.h" +#include "PayloadLayer.h" #include -#include "EndianPortable.h" +#include -namespace pcpp -{ - -VlanLayer::VlanLayer(const uint16_t vlanID, bool cfi, uint8_t priority, uint16_t etherType) -{ - const size_t headerLen = sizeof(vlan_header); - m_DataLen = headerLen; - m_Data = new uint8_t[headerLen]; - memset(m_Data, 0, headerLen); - m_Protocol = VLAN; - - vlan_header* vlanHeader = getVlanHeader(); - setVlanID(vlanID); - setCFI(cfi); - setPriority(priority); - vlanHeader->etherType = htobe16(etherType); +namespace pcpp { + +VlanLayer::VlanLayer(const uint16_t vlanID, bool cfi, uint8_t priority, + uint16_t etherType) { + const size_t headerLen = sizeof(vlan_header); + m_DataLen = headerLen; + m_Data = new uint8_t[headerLen]; + memset(m_Data, 0, headerLen); + m_Protocol = VLAN; + + vlan_header* vlanHeader = getVlanHeader(); + setVlanID(vlanID); + setCFI(cfi); + setPriority(priority); + vlanHeader->etherType = htobe16(etherType); } -uint16_t VlanLayer::getVlanID() const -{ - return be16toh(getVlanHeader()->vlan) & 0xFFF; +uint16_t VlanLayer::getVlanID() const { + return be16toh(getVlanHeader()->vlan) & 0xFFF; } -uint8_t VlanLayer::getCFI() const -{ - return ((be16toh(getVlanHeader()->vlan) >> 12) & 1); +uint8_t VlanLayer::getCFI() const { + return ((be16toh(getVlanHeader()->vlan) >> 12) & 1); } -uint8_t VlanLayer::getPriority() const -{ - return (be16toh(getVlanHeader()->vlan) >> 13) & 7; +uint8_t VlanLayer::getPriority() const { + return (be16toh(getVlanHeader()->vlan) >> 13) & 7; } -void VlanLayer::setVlanID(uint16_t id) -{ - getVlanHeader()->vlan = htobe16((be16toh(getVlanHeader()->vlan) & (~0xFFF)) | (id & 0xFFF)); +void VlanLayer::setVlanID(uint16_t id) { + getVlanHeader()->vlan = + htobe16((be16toh(getVlanHeader()->vlan) & (~0xFFF)) | (id & 0xFFF)); } -void VlanLayer::setCFI(bool cfi) -{ - getVlanHeader()->vlan = htobe16((be16toh(getVlanHeader()->vlan) & (~(1 << 12))) | ((cfi & 1) << 12)); +void VlanLayer::setCFI(bool cfi) { + getVlanHeader()->vlan = htobe16( + (be16toh(getVlanHeader()->vlan) & (~(1 << 12))) | ((cfi & 1) << 12)); } -void VlanLayer::setPriority(uint8_t priority) -{ - getVlanHeader()->vlan = htobe16((be16toh(getVlanHeader()->vlan) & (~(7 << 13))) | ((priority & 7) << 13)); +void VlanLayer::setPriority(uint8_t priority) { + getVlanHeader()->vlan = htobe16( + (be16toh(getVlanHeader()->vlan) & (~(7 << 13))) | ((priority & 7) << 13)); } -void VlanLayer::parseNextLayer() -{ - if (m_DataLen <= sizeof(vlan_header)) - return; - - uint8_t* payload = m_Data + sizeof(vlan_header); - size_t payloadLen = m_DataLen - sizeof(vlan_header); - - vlan_header* hdr = getVlanHeader(); - switch (be16toh(hdr->etherType)) - { - case PCPP_ETHERTYPE_IP: - m_NextLayer = IPv4Layer::isDataValid(payload, payloadLen) - ? static_cast(new IPv4Layer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case PCPP_ETHERTYPE_IPV6: - m_NextLayer = IPv6Layer::isDataValid(payload, payloadLen) - ? static_cast(new IPv6Layer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case PCPP_ETHERTYPE_ARP: - m_NextLayer = new ArpLayer(payload, payloadLen, this, m_Packet); - break; - case PCPP_ETHERTYPE_VLAN: - case PCPP_ETHERTYPE_IEEE_802_1AD: - m_NextLayer = new VlanLayer(payload, payloadLen, this, m_Packet); - break; - case PCPP_ETHERTYPE_PPPOES: - m_NextLayer = PPPoESessionLayer::isDataValid(payload, payloadLen) - ? static_cast(new PPPoESessionLayer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case PCPP_ETHERTYPE_PPPOED: - m_NextLayer = PPPoEDiscoveryLayer::isDataValid(payload, payloadLen) - ? static_cast(new PPPoEDiscoveryLayer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case PCPP_ETHERTYPE_MPLS: - m_NextLayer = new MplsLayer(payload, payloadLen, this, m_Packet); - break; - default: - m_NextLayer = (be16toh(hdr->etherType) < 1500 && LLCLayer::isDataValid(payload, payloadLen)) - ? static_cast(new LLCLayer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - } +void VlanLayer::parseNextLayer() { + if (m_DataLen <= sizeof(vlan_header)) + return; + + uint8_t* payload = m_Data + sizeof(vlan_header); + size_t payloadLen = m_DataLen - sizeof(vlan_header); + + vlan_header* hdr = getVlanHeader(); + switch (be16toh(hdr->etherType)) { + case PCPP_ETHERTYPE_IP: + m_NextLayer = IPv4Layer::isDataValid(payload, payloadLen) + ? static_cast( + new IPv4Layer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer( + payload, payloadLen, this, m_Packet)); + break; + case PCPP_ETHERTYPE_IPV6: + m_NextLayer = IPv6Layer::isDataValid(payload, payloadLen) + ? static_cast( + new IPv6Layer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer( + payload, payloadLen, this, m_Packet)); + break; + case PCPP_ETHERTYPE_ARP: + m_NextLayer = new ArpLayer(payload, payloadLen, this, m_Packet); + break; + case PCPP_ETHERTYPE_VLAN: + case PCPP_ETHERTYPE_IEEE_802_1AD: + m_NextLayer = new VlanLayer(payload, payloadLen, this, m_Packet); + break; + case PCPP_ETHERTYPE_PPPOES: + m_NextLayer = + PPPoESessionLayer::isDataValid(payload, payloadLen) + ? static_cast( + new PPPoESessionLayer(payload, payloadLen, this, m_Packet)) + : static_cast( + new PayloadLayer(payload, payloadLen, this, m_Packet)); + break; + case PCPP_ETHERTYPE_PPPOED: + m_NextLayer = + PPPoEDiscoveryLayer::isDataValid(payload, payloadLen) + ? static_cast( + new PPPoEDiscoveryLayer(payload, payloadLen, this, m_Packet)) + : static_cast( + new PayloadLayer(payload, payloadLen, this, m_Packet)); + break; + case PCPP_ETHERTYPE_MPLS: + m_NextLayer = new MplsLayer(payload, payloadLen, this, m_Packet); + break; + default: + m_NextLayer = (be16toh(hdr->etherType) < 1500 && + LLCLayer::isDataValid(payload, payloadLen)) + ? static_cast( + new LLCLayer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer( + payload, payloadLen, this, m_Packet)); + } } -void VlanLayer::computeCalculateFields() -{ - if (m_NextLayer == nullptr) - return; - - switch (m_NextLayer->getProtocol()) - { - case IPv4: - getVlanHeader()->etherType = htobe16(PCPP_ETHERTYPE_IP); - break; - case IPv6: - getVlanHeader()->etherType = htobe16(PCPP_ETHERTYPE_IPV6); - break; - case ARP: - getVlanHeader()->etherType = htobe16(PCPP_ETHERTYPE_ARP); - break; - case VLAN: - getVlanHeader()->etherType = htobe16(PCPP_ETHERTYPE_VLAN); - break; - default: - return; - } +void VlanLayer::computeCalculateFields() { + if (m_NextLayer == nullptr) + return; + + switch (m_NextLayer->getProtocol()) { + case IPv4: + getVlanHeader()->etherType = htobe16(PCPP_ETHERTYPE_IP); + break; + case IPv6: + getVlanHeader()->etherType = htobe16(PCPP_ETHERTYPE_IPV6); + break; + case ARP: + getVlanHeader()->etherType = htobe16(PCPP_ETHERTYPE_ARP); + break; + case VLAN: + getVlanHeader()->etherType = htobe16(PCPP_ETHERTYPE_VLAN); + break; + default: + return; + } } -std::string VlanLayer::toString() const -{ - std::ostringstream cfiStream; - cfiStream << (int)getCFI(); - std::ostringstream priStream; - priStream << (int)getPriority(); - std::ostringstream idStream; - idStream << getVlanID(); +std::string VlanLayer::toString() const { + std::ostringstream cfiStream; + cfiStream << (int)getCFI(); + std::ostringstream priStream; + priStream << (int)getPriority(); + std::ostringstream idStream; + idStream << getVlanID(); - return "VLAN Layer, Priority: " + priStream.str() + ", Vlan ID: " + idStream.str() + ", CFI: " + cfiStream.str(); + return "VLAN Layer, Priority: " + priStream.str() + + ", Vlan ID: " + idStream.str() + ", CFI: " + cfiStream.str(); } } // namespace pcpp diff --git a/Packet++/src/VrrpLayer.cpp b/Packet++/src/VrrpLayer.cpp old mode 100755 new mode 100644 index 88cb424252..4f1459dc74 --- a/Packet++/src/VrrpLayer.cpp +++ b/Packet++/src/VrrpLayer.cpp @@ -1,514 +1,448 @@ #define LOG_MODULE PacketLogModuleVrrpLayer -#include -#include "PacketUtils.h" -#include "Logger.h" +#include "VrrpLayer.h" #include "EndianPortable.h" #include "IPv4Layer.h" +#include "Logger.h" +#include "PacketUtils.h" #include "PayloadLayer.h" -#include "VrrpLayer.h" +#include namespace pcpp { -#define VRRP_PRIO_STOP 0 /* priority to stop */ -#define VRRP_PRIO_DEF 100 /* default priority */ -#define VRRP_PRIO_OWNER 255 /* priority of the ip owner */ +#define VRRP_PRIO_STOP 0 /* priority to stop */ +#define VRRP_PRIO_DEF 100 /* default priority */ +#define VRRP_PRIO_OWNER 255 /* priority of the ip owner */ #define VRRP_PACKET_FIX_LEN 8 #define VRRP_PACKET_MAX_IP_ADDRESS_NUM 255 -#define VRRP_V2_VERSION 2 -#define VRRP_V3_VERSION 3 - - /************* - * VrrpLayer - *************/ - - VrrpLayer::VrrpLayer(ProtocolType subProtocol, uint8_t virtualRouterId, uint8_t priority) - { - m_DataLen = VRRP_PACKET_FIX_LEN; - m_Data = new uint8_t[m_DataLen]; - memset(m_Data, 0, m_DataLen); - m_Protocol = subProtocol; - m_AddressType = IPAddress::IPv4AddressType; - auto vrrpHeader = getVrrpHeader(); - if (subProtocol == VRRPv2) - { - vrrpHeader->version = VRRP_V2_VERSION; - } - else if (subProtocol == VRRPv3) - { - vrrpHeader->version = VRRP_V3_VERSION; - } - vrrpHeader->type = static_cast(VrrpType::VrrpType_Advertisement); - setVirtualRouterID(virtualRouterId); - setPriority(priority); - } - - ProtocolType VrrpLayer::getVersionFromData(uint8_t *data, size_t dataLen) - { - if (!data || dataLen <= VRRP_PACKET_FIX_LEN) - { - return UnknownProtocol; - } - - auto *vrrpPacketCommon = (vrrp_header *) data; - uint8_t version = vrrpPacketCommon->version; - switch (version) - { - case VRRP_V2_VERSION: - return VRRPv2; - case VRRP_V3_VERSION: - return VRRPv3; - default: - return UnknownProtocol; - } - } - - void VrrpLayer::computeCalculateFields() - { - // calculate and fill the checksum to packet - calculateAndSetChecksum(); - } - - uint8_t VrrpLayer::getIPAddressLen() const - { - if (getAddressType() == IPAddress::IPv4AddressType) - { - return 4; - } - - return 16; - } - - bool VrrpLayer::isChecksumCorrect() const - { - auto vrrpHeader = getVrrpHeader(); - if (vrrpHeader == nullptr) - { - return false; - } - - return (calculateChecksum() == be16toh(vrrpHeader->checksum)); - } - - VrrpLayer::VrrpPriority VrrpLayer::getPriorityAsEnum() const - { - switch (getVrrpHeader()->priority) - { - case VRRP_PRIO_DEF: - return VrrpLayer::VrrpPriority::Default; - - case VRRP_PRIO_STOP: - return VrrpLayer::VrrpPriority::Stop; - - case VRRP_PRIO_OWNER: - return VrrpLayer::VrrpPriority::Owner; - - default: - return VrrpLayer::VrrpPriority::Other; - } - } - - std::string VrrpLayer::toString() const - { - return "VRRP v" + std::to_string(getVersion()) + " Layer, virtual router ID: " + std::to_string(getVirtualRouterID()) + ", IP address count: " + std::to_string(getIPAddressesCount()); - } - - uint8_t VrrpLayer::getVersion() const - { - return getVrrpHeader()->version; - } - - VrrpLayer::VrrpType VrrpLayer::getType() const - { - if (getVrrpHeader()->type == VrrpType_Advertisement) - { - return VrrpType_Advertisement; - } - - return VrrpType_Unknown; - } - - uint8_t VrrpLayer::getVirtualRouterID() const - { - return getVrrpHeader()->vrId; - } - - void VrrpLayer::setVirtualRouterID(uint8_t virtualRouterID) - { - getVrrpHeader()->vrId = virtualRouterID; - } - - uint8_t VrrpLayer::getPriority() const - { - return getVrrpHeader()->priority; - } - - void VrrpLayer::setPriority(uint8_t priority) - { - getVrrpHeader()->priority = priority; - } - - uint16_t VrrpLayer::getChecksum() const - { - return be16toh(getVrrpHeader()->checksum); - } - - void VrrpLayer::calculateAndSetChecksum() - { - getVrrpHeader()->checksum = htobe16(calculateChecksum()); - } - - uint8_t VrrpLayer::getIPAddressesCount() const - { - return getVrrpHeader()->ipAddrCount; - } - - std::vector VrrpLayer::getIPAddresses() const - { - std::vector ipAddressesVec; - auto ipAddressesPtr = getFirstIPAddressPtr(); - while (ipAddressesPtr != nullptr) - { - IPAddress ipAddress = getIPAddressFromData(ipAddressesPtr); - ipAddressesVec.push_back(ipAddress); - ipAddressesPtr = getNextIPAddressPtr(ipAddressesPtr); - } - - return ipAddressesVec; - } - - uint8_t* VrrpLayer::getFirstIPAddressPtr() const - { - size_t ipAddressLen = getIPAddressLen(); - - // check if there are virtual IP address at all - if (getHeaderLen() <= VRRP_PACKET_FIX_LEN + ipAddressLen) - { - return nullptr; - } - - return (m_Data + VRRP_PACKET_FIX_LEN); - } - - uint8_t *VrrpLayer::getNextIPAddressPtr(uint8_t* ipAddressPtr) const - { - if (ipAddressPtr == nullptr) - { - return nullptr; - } - - size_t ipAddressLen = getIPAddressLen(); - - // prev virtual IP address was the last virtual IP address - if (ipAddressPtr + ipAddressLen - m_Data >= (int) getHeaderLen()) - { - return nullptr; - } - - return (ipAddressPtr + ipAddressLen); - } - - bool VrrpLayer::addIPAddressesAt(const std::vector &ipAddresses, int offset) - { - if (offset > (int) getHeaderLen()) - { - PCPP_LOG_ERROR("Cannot add virtual IP address offset(" << offset << ") is out of layer bounds"); - return false; - } - - for (auto ipAddress: ipAddresses) - { - if (!isIPAddressValid(ipAddress)) - { - PCPP_LOG_ERROR("Cannot add virtual IP address, for IP address is invalid."); - return false; - } - } - - if (getIPAddressesCount() + ipAddresses.size() > VRRP_PACKET_MAX_IP_ADDRESS_NUM) - { - PCPP_LOG_ERROR("Cannot add virtual IP address, for virtual IP address has already exceed maximum."); - return false; - } - - size_t ipAddrLen = getIPAddressLen(); - size_t ipAddressesLen = ipAddrLen * ipAddresses.size(); - if (ipAddressesLen == 0) - { - return true; - } - - if (!extendLayer(offset, ipAddressesLen)) - { - PCPP_LOG_ERROR("Cannot add virtual IP address, cannot extend layer"); - return false; - } - - size_t ipAddrOffset = 0; - uint8_t *newIpAddresses = getData() + offset; - for (auto ipAddress: ipAddresses) - { - copyIPAddressToData(newIpAddresses + ipAddrOffset, ipAddress); - ipAddrOffset += ipAddrLen; - } - - getVrrpHeader()->ipAddrCount = getIPAddressesCount() + ipAddresses.size(); - - return true; - } - - bool VrrpLayer::addIPAddresses(const std::vector &ipAddresses) - { - return addIPAddressesAt(ipAddresses, (int) getHeaderLen()); - } - - bool VrrpLayer::addIPAddress(const IPAddress &ipAddress) - { - std::vector ipAddresses; - ipAddresses.push_back(ipAddress); - - return addIPAddressesAt(ipAddresses, (int) getHeaderLen()); - } - - bool VrrpLayer::removeIPAddressAtIndex(int index) - { - int ipAddressCount = (int) getIPAddressesCount(); - - if (index < 0 || index >= ipAddressCount) - { - PCPP_LOG_ERROR("Cannot remove virtual IP address, index " << index << " is out of bounds"); - return false; - } - - size_t ipAddressLen = getIPAddressLen(); - - size_t offset = VRRP_PACKET_FIX_LEN; - auto curIpAddressPtr = getFirstIPAddressPtr(); - for (int i = 0; i < index; i++) - { - if (curIpAddressPtr == nullptr) - { - PCPP_LOG_ERROR("Cannot remove virtual IP address at index " - << index << ", cannot find virtual IP address at index " << i); - return false; - } - - offset += ipAddressLen; - curIpAddressPtr = getNextIPAddressPtr(curIpAddressPtr); - } - - if (!shortenLayer((int) offset, ipAddressLen)) - { - PCPP_LOG_ERROR("Cannot remove virtual IP address at index " << index << ", cannot shorted layer"); - return false; - } - - getVrrpHeader()->ipAddrCount = ipAddressCount - 1; - - return true; - } - - bool VrrpLayer::removeAllIPAddresses() - { - size_t offset = VRRP_PACKET_FIX_LEN; - size_t packetLen = getHeaderLen(); - if (packetLen <= offset) - { - return false; - } - - if (!shortenLayer((int)offset, packetLen - offset)) - { - PCPP_LOG_ERROR("Cannot remove all virtual IP address(es), cannot shorted layer"); - return false; - } - - getVrrpHeader()->ipAddrCount = 0; - - return true; - } - - void VrrpLayer::copyIPAddressToData(uint8_t *data, const IPAddress &ipAddress) const - { - size_t ipAddressLen = getIPAddressLen(); - - if (ipAddress.isIPv4()) - { - memcpy(data, ipAddress.getIPv4().toBytes(), ipAddressLen); - } - else if (ipAddress.isIPv6()) - { - memcpy(data, ipAddress.getIPv6().toBytes(), ipAddressLen); - } - } - - IPAddress VrrpLayer::getIPAddressFromData(uint8_t *data) const - { - if (getAddressType() == IPAddress::IPv4AddressType) - { - return IPv4Address(*((uint32_t *) data)); - } - - return IPv6Address(data); - } - - bool VrrpLayer::isIPAddressValid(IPAddress &ipAddress) const - { - if (ipAddress.isIPv6() && (getProtocol() != VRRPv3)) - { - PCPP_LOG_ERROR("Only VRRPv3 support IPv6 virtual address"); - return false; - } - if (ipAddress.getType() != getAddressType()) - { - PCPP_LOG_ERROR("IP address version is not equal to layer's"); - return false; - } - if (!ipAddress.isValid()) - { - PCPP_LOG_ERROR("IP address is invalid."); - return false; - } - - return true; - } - - IPAddress::AddressType VrrpLayer::getAddressType() const - { - return m_AddressType; - } - - void VrrpLayer::setAddressType(IPAddress::AddressType addressType) - { - m_AddressType = addressType; - } - - /************* - * Vrrpv2Layer - *************/ - - VrrpV2Layer::VrrpV2Layer(uint8_t virtualRouterId, uint8_t priority, uint8_t advInt, uint8_t authType) : VrrpLayer(VRRPv2, virtualRouterId, priority) - { - setAdvInt(advInt); - setAuthType(authType); - } - - VrrpV2Layer::VrrpAuthType VrrpV2Layer::getAuthTypeAsEnum() const - { - auto authType = getAuthType(); - if (authType > 3) - { - return VrrpAuthType::Other; - } - - return static_cast(authType); - } - - uint8_t VrrpV2Layer::getAdvInt() const - { - uint16_t authAdvInt = getVrrpHeader()->authTypeAdvInt; - auto authAdvIntPtr = (vrrpv2_auth_adv *) (&authAdvInt); - return authAdvIntPtr->advInt; - } - - void VrrpV2Layer::setAdvInt(uint8_t advInt) - { - auto authAdvIntPtr = (vrrpv2_auth_adv *)&getVrrpHeader()->authTypeAdvInt; - authAdvIntPtr->advInt = advInt; - } - - uint8_t VrrpV2Layer::getAuthType() const - { - uint16_t authAdvInt = getVrrpHeader()->authTypeAdvInt; - auto *authAdvIntPtr = (vrrpv2_auth_adv *) (&authAdvInt); - return authAdvIntPtr->authType; - } - - void VrrpV2Layer::setAuthType(uint8_t authType) - { - auto authAdvIntPtr = (vrrpv2_auth_adv *)&getVrrpHeader()->authTypeAdvInt; - authAdvIntPtr->authType = authType; - } - - uint16_t VrrpV2Layer::calculateChecksum() const - { - if ((getData() == nullptr) || (getDataLen() == 0)) - { - return 0; - } - - auto vrrpHeader = getVrrpHeader(); - ScalarBuffer buffer = {}; - buffer.buffer = (uint16_t *) vrrpHeader; - buffer.len = getHeaderLen(); - - uint16_t currChecksumValue = vrrpHeader->checksum; - vrrpHeader->checksum = 0; - uint16_t checksum = computeChecksum(&buffer, 1); - vrrpHeader->checksum = currChecksumValue; - - return checksum; - } - - /************* - * Vrrpv3Layer - *************/ - - VrrpV3Layer::VrrpV3Layer(IPAddress::AddressType addressType, uint8_t virtualRouterId, uint8_t priority, uint16_t maxAdvInt) : VrrpLayer( - VRRPv3, virtualRouterId, priority) - { - setAddressType(addressType); - setMaxAdvInt(maxAdvInt); - } - - uint16_t VrrpV3Layer::getMaxAdvInt() const - { - uint16_t authAdvInt = getVrrpHeader()->authTypeAdvInt; - auto rsvdAdv = (vrrpv3_rsvd_adv *) (&authAdvInt); - return be16toh(rsvdAdv->maxAdvInt); - } - - void VrrpV3Layer::setMaxAdvInt(uint16_t maxAdvInt) - { - if (maxAdvInt > 0xfff) - { - throw std::invalid_argument("maxAdvInt must not exceed 12 bits length"); - } - auto rsvdAdv = (vrrpv3_rsvd_adv *)&getVrrpHeader()->authTypeAdvInt; - rsvdAdv->maxAdvInt = htobe16(maxAdvInt); - } - - uint16_t VrrpV3Layer::calculateChecksum() const - { - auto *ipLayer = m_Packet->getLayerOfType(); - if (ipLayer == nullptr) - { - PCPP_LOG_ERROR("Calculate checksum failed, for can not get IPLayer" << ""); - return 0; - } - - auto vrrpHeader = getVrrpHeader(); - uint16_t currChecksumValue = vrrpHeader->checksum; - vrrpHeader->checksum = 0; - - pcpp::IPAddress srcIPAddr = ipLayer->getSrcIPAddress(); - pcpp::IPAddress dstIPAddr = ipLayer->getDstIPAddress(); - uint16_t checksum; - if (getAddressType() == IPAddress::IPv4AddressType) - { - checksum = computePseudoHdrChecksum((uint8_t *) vrrpHeader, getDataLen(), IPAddress::IPv4AddressType, - PACKETPP_IPPROTO_VRRP, srcIPAddr, dstIPAddr); - } - else - { - checksum = computePseudoHdrChecksum((uint8_t *) vrrpHeader, getDataLen(), IPAddress::IPv6AddressType, - PACKETPP_IPPROTO_VRRP, srcIPAddr, dstIPAddr); - } - - vrrpHeader->checksum = currChecksumValue; - - return checksum; - } +#define VRRP_V2_VERSION 2 +#define VRRP_V3_VERSION 3 + +/************* + * VrrpLayer + *************/ + +VrrpLayer::VrrpLayer(ProtocolType subProtocol, uint8_t virtualRouterId, + uint8_t priority) { + m_DataLen = VRRP_PACKET_FIX_LEN; + m_Data = new uint8_t[m_DataLen]; + memset(m_Data, 0, m_DataLen); + m_Protocol = subProtocol; + m_AddressType = IPAddress::IPv4AddressType; + auto vrrpHeader = getVrrpHeader(); + if (subProtocol == VRRPv2) { + vrrpHeader->version = VRRP_V2_VERSION; + } else if (subProtocol == VRRPv3) { + vrrpHeader->version = VRRP_V3_VERSION; + } + vrrpHeader->type = static_cast(VrrpType::VrrpType_Advertisement); + setVirtualRouterID(virtualRouterId); + setPriority(priority); +} + +ProtocolType VrrpLayer::getVersionFromData(uint8_t* data, size_t dataLen) { + if (!data || dataLen <= VRRP_PACKET_FIX_LEN) { + return UnknownProtocol; + } + + auto* vrrpPacketCommon = (vrrp_header*)data; + uint8_t version = vrrpPacketCommon->version; + switch (version) { + case VRRP_V2_VERSION: + return VRRPv2; + case VRRP_V3_VERSION: + return VRRPv3; + default: + return UnknownProtocol; + } +} + +void VrrpLayer::computeCalculateFields() { + // calculate and fill the checksum to packet + calculateAndSetChecksum(); +} + +uint8_t VrrpLayer::getIPAddressLen() const { + if (getAddressType() == IPAddress::IPv4AddressType) { + return 4; + } + + return 16; +} + +bool VrrpLayer::isChecksumCorrect() const { + auto vrrpHeader = getVrrpHeader(); + if (vrrpHeader == nullptr) { + return false; + } + + return (calculateChecksum() == be16toh(vrrpHeader->checksum)); +} + +VrrpLayer::VrrpPriority VrrpLayer::getPriorityAsEnum() const { + switch (getVrrpHeader()->priority) { + case VRRP_PRIO_DEF: + return VrrpLayer::VrrpPriority::Default; + + case VRRP_PRIO_STOP: + return VrrpLayer::VrrpPriority::Stop; + + case VRRP_PRIO_OWNER: + return VrrpLayer::VrrpPriority::Owner; + + default: + return VrrpLayer::VrrpPriority::Other; + } +} + +std::string VrrpLayer::toString() const { + return "VRRP v" + std::to_string(getVersion()) + + " Layer, virtual router ID: " + std::to_string(getVirtualRouterID()) + + ", IP address count: " + std::to_string(getIPAddressesCount()); +} + +uint8_t VrrpLayer::getVersion() const { return getVrrpHeader()->version; } + +VrrpLayer::VrrpType VrrpLayer::getType() const { + if (getVrrpHeader()->type == VrrpType_Advertisement) { + return VrrpType_Advertisement; + } + + return VrrpType_Unknown; +} + +uint8_t VrrpLayer::getVirtualRouterID() const { return getVrrpHeader()->vrId; } + +void VrrpLayer::setVirtualRouterID(uint8_t virtualRouterID) { + getVrrpHeader()->vrId = virtualRouterID; +} + +uint8_t VrrpLayer::getPriority() const { return getVrrpHeader()->priority; } + +void VrrpLayer::setPriority(uint8_t priority) { + getVrrpHeader()->priority = priority; +} + +uint16_t VrrpLayer::getChecksum() const { + return be16toh(getVrrpHeader()->checksum); +} + +void VrrpLayer::calculateAndSetChecksum() { + getVrrpHeader()->checksum = htobe16(calculateChecksum()); +} + +uint8_t VrrpLayer::getIPAddressesCount() const { + return getVrrpHeader()->ipAddrCount; +} + +std::vector VrrpLayer::getIPAddresses() const { + std::vector ipAddressesVec; + auto ipAddressesPtr = getFirstIPAddressPtr(); + while (ipAddressesPtr != nullptr) { + IPAddress ipAddress = getIPAddressFromData(ipAddressesPtr); + ipAddressesVec.push_back(ipAddress); + ipAddressesPtr = getNextIPAddressPtr(ipAddressesPtr); + } + + return ipAddressesVec; +} + +uint8_t* VrrpLayer::getFirstIPAddressPtr() const { + size_t ipAddressLen = getIPAddressLen(); + + // check if there are virtual IP address at all + if (getHeaderLen() <= VRRP_PACKET_FIX_LEN + ipAddressLen) { + return nullptr; + } + + return (m_Data + VRRP_PACKET_FIX_LEN); +} + +uint8_t* VrrpLayer::getNextIPAddressPtr(uint8_t* ipAddressPtr) const { + if (ipAddressPtr == nullptr) { + return nullptr; + } + + size_t ipAddressLen = getIPAddressLen(); + + // prev virtual IP address was the last virtual IP address + if (ipAddressPtr + ipAddressLen - m_Data >= (int)getHeaderLen()) { + return nullptr; + } + + return (ipAddressPtr + ipAddressLen); +} + +bool VrrpLayer::addIPAddressesAt(const std::vector& ipAddresses, + int offset) { + if (offset > (int)getHeaderLen()) { + PCPP_LOG_ERROR("Cannot add virtual IP address offset(" + << offset << ") is out of layer bounds"); + return false; + } + + for (auto ipAddress : ipAddresses) { + if (!isIPAddressValid(ipAddress)) { + PCPP_LOG_ERROR( + "Cannot add virtual IP address, for IP address is invalid."); + return false; + } + } + + if (getIPAddressesCount() + ipAddresses.size() > + VRRP_PACKET_MAX_IP_ADDRESS_NUM) { + PCPP_LOG_ERROR("Cannot add virtual IP address, for virtual IP address has " + "already exceed maximum."); + return false; + } + + size_t ipAddrLen = getIPAddressLen(); + size_t ipAddressesLen = ipAddrLen * ipAddresses.size(); + if (ipAddressesLen == 0) { + return true; + } + + if (!extendLayer(offset, ipAddressesLen)) { + PCPP_LOG_ERROR("Cannot add virtual IP address, cannot extend layer"); + return false; + } + + size_t ipAddrOffset = 0; + uint8_t* newIpAddresses = getData() + offset; + for (auto ipAddress : ipAddresses) { + copyIPAddressToData(newIpAddresses + ipAddrOffset, ipAddress); + ipAddrOffset += ipAddrLen; + } + + getVrrpHeader()->ipAddrCount = getIPAddressesCount() + ipAddresses.size(); + + return true; +} + +bool VrrpLayer::addIPAddresses(const std::vector& ipAddresses) { + return addIPAddressesAt(ipAddresses, (int)getHeaderLen()); +} + +bool VrrpLayer::addIPAddress(const IPAddress& ipAddress) { + std::vector ipAddresses; + ipAddresses.push_back(ipAddress); + + return addIPAddressesAt(ipAddresses, (int)getHeaderLen()); +} + +bool VrrpLayer::removeIPAddressAtIndex(int index) { + int ipAddressCount = (int)getIPAddressesCount(); + + if (index < 0 || index >= ipAddressCount) { + PCPP_LOG_ERROR("Cannot remove virtual IP address, index " + << index << " is out of bounds"); + return false; + } + + size_t ipAddressLen = getIPAddressLen(); + + size_t offset = VRRP_PACKET_FIX_LEN; + auto curIpAddressPtr = getFirstIPAddressPtr(); + for (int i = 0; i < index; i++) { + if (curIpAddressPtr == nullptr) { + PCPP_LOG_ERROR("Cannot remove virtual IP address at index " + << index << ", cannot find virtual IP address at index " + << i); + return false; + } + + offset += ipAddressLen; + curIpAddressPtr = getNextIPAddressPtr(curIpAddressPtr); + } + + if (!shortenLayer((int)offset, ipAddressLen)) { + PCPP_LOG_ERROR("Cannot remove virtual IP address at index " + << index << ", cannot shorted layer"); + return false; + } + + getVrrpHeader()->ipAddrCount = ipAddressCount - 1; + + return true; +} + +bool VrrpLayer::removeAllIPAddresses() { + size_t offset = VRRP_PACKET_FIX_LEN; + size_t packetLen = getHeaderLen(); + if (packetLen <= offset) { + return false; + } + + if (!shortenLayer((int)offset, packetLen - offset)) { + PCPP_LOG_ERROR( + "Cannot remove all virtual IP address(es), cannot shorted layer"); + return false; + } + + getVrrpHeader()->ipAddrCount = 0; + + return true; +} + +void VrrpLayer::copyIPAddressToData(uint8_t* data, + const IPAddress& ipAddress) const { + size_t ipAddressLen = getIPAddressLen(); + + if (ipAddress.isIPv4()) { + memcpy(data, ipAddress.getIPv4().toBytes(), ipAddressLen); + } else if (ipAddress.isIPv6()) { + memcpy(data, ipAddress.getIPv6().toBytes(), ipAddressLen); + } +} + +IPAddress VrrpLayer::getIPAddressFromData(uint8_t* data) const { + if (getAddressType() == IPAddress::IPv4AddressType) { + return IPv4Address(*((uint32_t*)data)); + } + + return IPv6Address(data); +} + +bool VrrpLayer::isIPAddressValid(IPAddress& ipAddress) const { + if (ipAddress.isIPv6() && (getProtocol() != VRRPv3)) { + PCPP_LOG_ERROR("Only VRRPv3 support IPv6 virtual address"); + return false; + } + if (ipAddress.getType() != getAddressType()) { + PCPP_LOG_ERROR("IP address version is not equal to layer's"); + return false; + } + if (!ipAddress.isValid()) { + PCPP_LOG_ERROR("IP address is invalid."); + return false; + } + + return true; +} + +IPAddress::AddressType VrrpLayer::getAddressType() const { + return m_AddressType; +} + +void VrrpLayer::setAddressType(IPAddress::AddressType addressType) { + m_AddressType = addressType; +} + +/************* + * Vrrpv2Layer + *************/ + +VrrpV2Layer::VrrpV2Layer(uint8_t virtualRouterId, uint8_t priority, + uint8_t advInt, uint8_t authType) + : VrrpLayer(VRRPv2, virtualRouterId, priority) { + setAdvInt(advInt); + setAuthType(authType); +} + +VrrpV2Layer::VrrpAuthType VrrpV2Layer::getAuthTypeAsEnum() const { + auto authType = getAuthType(); + if (authType > 3) { + return VrrpAuthType::Other; + } + + return static_cast(authType); +} + +uint8_t VrrpV2Layer::getAdvInt() const { + uint16_t authAdvInt = getVrrpHeader()->authTypeAdvInt; + auto authAdvIntPtr = (vrrpv2_auth_adv*)(&authAdvInt); + return authAdvIntPtr->advInt; +} + +void VrrpV2Layer::setAdvInt(uint8_t advInt) { + auto authAdvIntPtr = (vrrpv2_auth_adv*)&getVrrpHeader()->authTypeAdvInt; + authAdvIntPtr->advInt = advInt; +} + +uint8_t VrrpV2Layer::getAuthType() const { + uint16_t authAdvInt = getVrrpHeader()->authTypeAdvInt; + auto* authAdvIntPtr = (vrrpv2_auth_adv*)(&authAdvInt); + return authAdvIntPtr->authType; +} + +void VrrpV2Layer::setAuthType(uint8_t authType) { + auto authAdvIntPtr = (vrrpv2_auth_adv*)&getVrrpHeader()->authTypeAdvInt; + authAdvIntPtr->authType = authType; +} + +uint16_t VrrpV2Layer::calculateChecksum() const { + if ((getData() == nullptr) || (getDataLen() == 0)) { + return 0; + } + + auto vrrpHeader = getVrrpHeader(); + ScalarBuffer buffer = {}; + buffer.buffer = (uint16_t*)vrrpHeader; + buffer.len = getHeaderLen(); + + uint16_t currChecksumValue = vrrpHeader->checksum; + vrrpHeader->checksum = 0; + uint16_t checksum = computeChecksum(&buffer, 1); + vrrpHeader->checksum = currChecksumValue; + + return checksum; +} + +/************* + * Vrrpv3Layer + *************/ + +VrrpV3Layer::VrrpV3Layer(IPAddress::AddressType addressType, + uint8_t virtualRouterId, uint8_t priority, + uint16_t maxAdvInt) + : VrrpLayer(VRRPv3, virtualRouterId, priority) { + setAddressType(addressType); + setMaxAdvInt(maxAdvInt); +} + +uint16_t VrrpV3Layer::getMaxAdvInt() const { + uint16_t authAdvInt = getVrrpHeader()->authTypeAdvInt; + auto rsvdAdv = (vrrpv3_rsvd_adv*)(&authAdvInt); + return be16toh(rsvdAdv->maxAdvInt); +} + +void VrrpV3Layer::setMaxAdvInt(uint16_t maxAdvInt) { + if (maxAdvInt > 0xfff) { + throw std::invalid_argument("maxAdvInt must not exceed 12 bits length"); + } + auto rsvdAdv = (vrrpv3_rsvd_adv*)&getVrrpHeader()->authTypeAdvInt; + rsvdAdv->maxAdvInt = htobe16(maxAdvInt); +} + +uint16_t VrrpV3Layer::calculateChecksum() const { + auto* ipLayer = m_Packet->getLayerOfType(); + if (ipLayer == nullptr) { + PCPP_LOG_ERROR("Calculate checksum failed, for can not get IPLayer" + << ""); + return 0; + } + + auto vrrpHeader = getVrrpHeader(); + uint16_t currChecksumValue = vrrpHeader->checksum; + vrrpHeader->checksum = 0; + + pcpp::IPAddress srcIPAddr = ipLayer->getSrcIPAddress(); + pcpp::IPAddress dstIPAddr = ipLayer->getDstIPAddress(); + uint16_t checksum; + if (getAddressType() == IPAddress::IPv4AddressType) { + checksum = computePseudoHdrChecksum( + (uint8_t*)vrrpHeader, getDataLen(), IPAddress::IPv4AddressType, + PACKETPP_IPPROTO_VRRP, srcIPAddr, dstIPAddr); + } else { + checksum = computePseudoHdrChecksum( + (uint8_t*)vrrpHeader, getDataLen(), IPAddress::IPv6AddressType, + PACKETPP_IPPROTO_VRRP, srcIPAddr, dstIPAddr); + } + + vrrpHeader->checksum = currChecksumValue; + + return checksum; } +} // namespace pcpp diff --git a/Packet++/src/VxlanLayer.cpp b/Packet++/src/VxlanLayer.cpp index b0c89537e7..77c25c237b 100644 --- a/Packet++/src/VxlanLayer.cpp +++ b/Packet++/src/VxlanLayer.cpp @@ -1,58 +1,52 @@ #include "VxlanLayer.h" +#include "EndianPortable.h" #include "EthLayer.h" #include -#include "EndianPortable.h" -namespace pcpp -{ +namespace pcpp { -VxlanLayer::VxlanLayer(uint32_t vni, uint16_t groupPolicyID, bool setGbpFlag, bool setPolicyAppliedFlag, bool setDontLearnFlag) -{ - const size_t headerLen = sizeof(vxlan_header); - m_DataLen = headerLen; - m_Data = new uint8_t[headerLen]; - memset(m_Data, 0, headerLen); - m_Protocol = VXLAN; +VxlanLayer::VxlanLayer(uint32_t vni, uint16_t groupPolicyID, bool setGbpFlag, + bool setPolicyAppliedFlag, bool setDontLearnFlag) { + const size_t headerLen = sizeof(vxlan_header); + m_DataLen = headerLen; + m_Data = new uint8_t[headerLen]; + memset(m_Data, 0, headerLen); + m_Protocol = VXLAN; - if (vni != 0) - setVNI(vni); + if (vni != 0) + setVNI(vni); - vxlan_header* vxlanHeader = getVxlanHeader(); + vxlan_header* vxlanHeader = getVxlanHeader(); - if (groupPolicyID != 0) - vxlanHeader->groupPolicyID = htobe16(groupPolicyID); + if (groupPolicyID != 0) + vxlanHeader->groupPolicyID = htobe16(groupPolicyID); - vxlanHeader->vniPresentFlag = 1; + vxlanHeader->vniPresentFlag = 1; - if (setGbpFlag) - vxlanHeader->gbpFlag = 1; - if (setPolicyAppliedFlag) - vxlanHeader->policyAppliedFlag = 1; - if (setDontLearnFlag) - vxlanHeader->dontLearnFlag = 1; + if (setGbpFlag) + vxlanHeader->gbpFlag = 1; + if (setPolicyAppliedFlag) + vxlanHeader->policyAppliedFlag = 1; + if (setDontLearnFlag) + vxlanHeader->dontLearnFlag = 1; } -uint32_t VxlanLayer::getVNI() const -{ - return (be32toh(getVxlanHeader()->vni) >> 8); +uint32_t VxlanLayer::getVNI() const { + return (be32toh(getVxlanHeader()->vni) >> 8); } -void VxlanLayer::setVNI(uint32_t vni) -{ - getVxlanHeader()->vni = htobe32(vni << 8); +void VxlanLayer::setVNI(uint32_t vni) { + getVxlanHeader()->vni = htobe32(vni << 8); } -std::string VxlanLayer::toString() const -{ - return "VXLAN Layer"; -} +std::string VxlanLayer::toString() const { return "VXLAN Layer"; } -void VxlanLayer::parseNextLayer() -{ - if (m_DataLen <= sizeof(vxlan_header)) - return; +void VxlanLayer::parseNextLayer() { + if (m_DataLen <= sizeof(vxlan_header)) + return; - m_NextLayer = new EthLayer(m_Data + sizeof(vxlan_header), m_DataLen - sizeof(vxlan_header), this, m_Packet); + m_NextLayer = new EthLayer(m_Data + sizeof(vxlan_header), + m_DataLen - sizeof(vxlan_header), this, m_Packet); } -} +} // namespace pcpp diff --git a/Packet++/src/WakeOnLanLayer.cpp b/Packet++/src/WakeOnLanLayer.cpp index c5c6996551..a5a0f5cf82 100644 --- a/Packet++/src/WakeOnLanLayer.cpp +++ b/Packet++/src/WakeOnLanLayer.cpp @@ -4,133 +4,114 @@ #include "GeneralUtils.h" #include "Logger.h" +namespace pcpp { -namespace pcpp -{ +void WakeOnLanLayer::init(uint16_t len) { + m_Data = new uint8_t[len]; + m_DataLen = len; + m_Protocol = WakeOnLan; -void WakeOnLanLayer::init(uint16_t len) -{ - m_Data = new uint8_t[len]; - m_DataLen = len; - m_Protocol = WakeOnLan; - - memset(getWakeOnLanHeader()->sync, 0xFF, 6); + memset(getWakeOnLanHeader()->sync, 0xFF, 6); } -WakeOnLanLayer::WakeOnLanLayer(const pcpp::MacAddress &targetAddr) -{ - init(sizeof(wol_header)); - setTargetAddr(targetAddr); +WakeOnLanLayer::WakeOnLanLayer(const pcpp::MacAddress& targetAddr) { + init(sizeof(wol_header)); + setTargetAddr(targetAddr); } -WakeOnLanLayer::WakeOnLanLayer(const pcpp::MacAddress &targetAddr, uint8_t *password, uint8_t len) -{ - init(sizeof(wol_header) + len); - setTargetAddr(targetAddr); - setPassword(password, len); +WakeOnLanLayer::WakeOnLanLayer(const pcpp::MacAddress& targetAddr, + uint8_t* password, uint8_t len) { + init(sizeof(wol_header) + len); + setTargetAddr(targetAddr); + setPassword(password, len); } -WakeOnLanLayer::WakeOnLanLayer(const pcpp::MacAddress &targetAddr, const pcpp::MacAddress &password) -{ - init(sizeof(wol_header) + 6); - setTargetAddr(targetAddr); - setPassword(password); +WakeOnLanLayer::WakeOnLanLayer(const pcpp::MacAddress& targetAddr, + const pcpp::MacAddress& password) { + init(sizeof(wol_header) + 6); + setTargetAddr(targetAddr); + setPassword(password); } -WakeOnLanLayer::WakeOnLanLayer(const pcpp::MacAddress &targetAddr, const IPv4Address &password) -{ - init(sizeof(wol_header) + 4); - setTargetAddr(targetAddr); - setPassword(password); +WakeOnLanLayer::WakeOnLanLayer(const pcpp::MacAddress& targetAddr, + const IPv4Address& password) { + init(sizeof(wol_header) + 4); + setTargetAddr(targetAddr); + setPassword(password); } -pcpp::MacAddress WakeOnLanLayer::getTargetAddr() const -{ - return pcpp::MacAddress(getWakeOnLanHeader()->addrBody); +pcpp::MacAddress WakeOnLanLayer::getTargetAddr() const { + return pcpp::MacAddress(getWakeOnLanHeader()->addrBody); } -void WakeOnLanLayer::setTargetAddr(const pcpp::MacAddress &targetAddr) -{ - for (size_t idx = 0; idx < 16; ++idx) - memcpy(&(getWakeOnLanHeader()->addrBody[idx * 6]), targetAddr.getRawData(), 6); +void WakeOnLanLayer::setTargetAddr(const pcpp::MacAddress& targetAddr) { + for (size_t idx = 0; idx < 16; ++idx) + memcpy(&(getWakeOnLanHeader()->addrBody[idx * 6]), targetAddr.getRawData(), + 6); } -std::string WakeOnLanLayer::getPassword() const -{ - size_t passSize = m_DataLen - sizeof(wol_header); - switch (passSize) - { - case 0: - return std::string(); - case 4: - return IPv4Address(&m_Data[sizeof(wol_header)]).toString(); - case 6: - return MacAddress(&m_Data[sizeof(wol_header)]).toString(); - default: - return byteArrayToHexString(&m_Data[sizeof(wol_header)], passSize); - } +std::string WakeOnLanLayer::getPassword() const { + size_t passSize = m_DataLen - sizeof(wol_header); + switch (passSize) { + case 0: + return std::string(); + case 4: + return IPv4Address(&m_Data[sizeof(wol_header)]).toString(); + case 6: + return MacAddress(&m_Data[sizeof(wol_header)]).toString(); + default: + return byteArrayToHexString(&m_Data[sizeof(wol_header)], passSize); + } } -bool WakeOnLanLayer::setPassword(const uint8_t *password, uint8_t len) -{ - if (len) - { - if (m_DataLen > sizeof(wol_header) + len) - { - if (!shortenLayer(sizeof(wol_header), m_DataLen - (sizeof(wol_header) + len))) - { - PCPP_LOG_ERROR("Can't shorten Wake on LAN layer"); - return false; - } - } - else if (m_DataLen < sizeof(wol_header) + len) - { - if (!extendLayer(m_DataLen, (sizeof(wol_header) + len) - m_DataLen)) - { - PCPP_LOG_ERROR("Can't extend Wake on LAN layer"); - return false; - } - } - memcpy(&m_Data[sizeof(wol_header)], password, len); - } - - return true; +bool WakeOnLanLayer::setPassword(const uint8_t* password, uint8_t len) { + if (len) { + if (m_DataLen > sizeof(wol_header) + len) { + if (!shortenLayer(sizeof(wol_header), + m_DataLen - (sizeof(wol_header) + len))) { + PCPP_LOG_ERROR("Can't shorten Wake on LAN layer"); + return false; + } + } else if (m_DataLen < sizeof(wol_header) + len) { + if (!extendLayer(m_DataLen, (sizeof(wol_header) + len) - m_DataLen)) { + PCPP_LOG_ERROR("Can't extend Wake on LAN layer"); + return false; + } + } + memcpy(&m_Data[sizeof(wol_header)], password, len); + } + + return true; } -bool WakeOnLanLayer::setPassword(const std::string &password) -{ - return setPassword(reinterpret_cast(password.c_str()), password.size()); +bool WakeOnLanLayer::setPassword(const std::string& password) { + return setPassword(reinterpret_cast(password.c_str()), + password.size()); } -bool WakeOnLanLayer::setPassword(const MacAddress &addr) -{ - return setPassword(addr.getRawData(), 6); +bool WakeOnLanLayer::setPassword(const MacAddress& addr) { + return setPassword(addr.getRawData(), 6); } -bool WakeOnLanLayer::setPassword(const IPv4Address &addr) -{ - return setPassword(addr.toBytes(), 4); +bool WakeOnLanLayer::setPassword(const IPv4Address& addr) { + return setPassword(addr.toBytes(), 4); } -bool WakeOnLanLayer::isDataValid(const uint8_t *data, size_t dataSize) -{ - if (data && dataSize >= sizeof(wol_header)) - { - // It should repeat same MAC address at the payload 16 times - pcpp::MacAddress bufAddr(&data[6]); - for (size_t idx = 1; idx < 16; ++idx) - { - if (bufAddr != pcpp::MacAddress(&data[6 + idx * 6])) - return false; - } - return true; - } - return false; +bool WakeOnLanLayer::isDataValid(const uint8_t* data, size_t dataSize) { + if (data && dataSize >= sizeof(wol_header)) { + // It should repeat same MAC address at the payload 16 times + pcpp::MacAddress bufAddr(&data[6]); + for (size_t idx = 1; idx < 16; ++idx) { + if (bufAddr != pcpp::MacAddress(&data[6 + idx * 6])) + return false; + } + return true; + } + return false; } -std::string WakeOnLanLayer::toString() const -{ - return "Wake On LAN Layer, target address: " + getTargetAddr().toString(); +std::string WakeOnLanLayer::toString() const { + return "Wake On LAN Layer, target address: " + getTargetAddr().toString(); } } // namespace pcpp diff --git a/Pcap++/header/Device.h b/Pcap++/header/Device.h index 44e945d7dc..662e37a242 100644 --- a/Pcap++/header/Device.h +++ b/Pcap++/header/Device.h @@ -3,96 +3,95 @@ /// @file +#include "PcapFilter.h" #include "PointerVector.h" #include "RawPacket.h" -#include "PcapFilter.h" /** -* \namespace pcpp -* \brief The main namespace for the PcapPlusPlus lib -*/ -namespace pcpp -{ - /** A vector of pointers to RawPacket */ - typedef PointerVector RawPacketVector; - - /** - * @class IDevice - * An abstract interface representing all packet processing devices. It stands as the root class for all devices. - * This is an abstract class that cannot be instantiated - */ - class IDevice - { - protected: - bool m_DeviceOpened; - - // c'tor should not be public - IDevice() : m_DeviceOpened(false) {} - - public: - - virtual ~IDevice() {} - - /** - * Open the device - * @return True if device was opened successfully, false otherwise - */ - virtual bool open() = 0; - - /** - * Close the device - */ - virtual void close() = 0; - - /** - * @return True if the file is opened, false otherwise - */ - inline bool isOpened() { return m_DeviceOpened; } - }; + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp { +/** A vector of pointers to RawPacket */ +typedef PointerVector RawPacketVector; +/** + * @class IDevice + * An abstract interface representing all packet processing devices. It stands + * as the root class for all devices. This is an abstract class that cannot be + * instantiated + */ +class IDevice { + protected: + bool m_DeviceOpened; + + // c'tor should not be public + IDevice() : m_DeviceOpened(false) {} + + public: + virtual ~IDevice() {} + + /** + * Open the device + * @return True if device was opened successfully, false otherwise + */ + virtual bool open() = 0; + + /** + * Close the device + */ + virtual void close() = 0; + + /** + * @return True if the file is opened, false otherwise + */ + inline bool isOpened() { return m_DeviceOpened; } +}; - /** - * @class IFilterableDevice - * An abstract interface representing all devices that have BPF (Berkeley Packet Filter) filtering capabilities, - * meaning devices that can filter packets based on the BPF filtering syntax. - * This is an abstract class that cannot be instantiated - */ - class IFilterableDevice - { - protected: - - // c'tor should not be public - IFilterableDevice() {} - - public: - - virtual ~IFilterableDevice() {} - - /** - * Set a filter for the device. When implemented by the device, only packets that match the filter will be received - * @param[in] filter The filter to be set in PcapPlusPlus' GeneralFilter format - * @return True if filter set successfully, false otherwise - */ - virtual bool setFilter(GeneralFilter& filter) - { - std::string filterAsString; - filter.parseToString(filterAsString); - return setFilter(filterAsString); - } - - /** - * Set a filter for the device. When implemented by the device, only packets that match the filter will be received - * @param[in] filterAsString The filter to be set in Berkeley Packet Filter (BPF) syntax (http://biot.com/capstats/bpf.html) - * @return True if filter set successfully, false otherwise - */ - virtual bool setFilter(std::string filterAsString) = 0; - - /** - * Clear the filter currently set on the device - * @return True if filter was removed successfully or if no filter was set, false otherwise - */ - virtual bool clearFilter() = 0; - }; -} +/** + * @class IFilterableDevice + * An abstract interface representing all devices that have BPF (Berkeley Packet + * Filter) filtering capabilities, meaning devices that can filter packets based + * on the BPF filtering syntax. This is an abstract class that cannot be + * instantiated + */ +class IFilterableDevice { + protected: + // c'tor should not be public + IFilterableDevice() {} + + public: + virtual ~IFilterableDevice() {} + + /** + * Set a filter for the device. When implemented by the device, only packets + * that match the filter will be received + * @param[in] filter The filter to be set in PcapPlusPlus' GeneralFilter + * format + * @return True if filter set successfully, false otherwise + */ + virtual bool setFilter(GeneralFilter& filter) { + std::string filterAsString; + filter.parseToString(filterAsString); + return setFilter(filterAsString); + } + + /** + * Set a filter for the device. When implemented by the device, only packets + * that match the filter will be received + * @param[in] filterAsString The filter to be set in Berkeley Packet Filter + * (BPF) syntax (http://biot.com/capstats/bpf.html) + * @return True if filter set successfully, false otherwise + */ + virtual bool setFilter(std::string filterAsString) = 0; + + /** + * Clear the filter currently set on the device + * @return True if filter was removed successfully or if no filter was set, + * false otherwise + */ + virtual bool clearFilter() = 0; +}; +} // namespace pcpp #endif // PCAPPP_DEVICE diff --git a/Pcap++/header/DpdkDevice.h b/Pcap++/header/DpdkDevice.h index f0cdc8ecac..aa97d97aad 100644 --- a/Pcap++/header/DpdkDevice.h +++ b/Pcap++/header/DpdkDevice.h @@ -3,55 +3,77 @@ // GCOVR_EXCL_START +#include "Device.h" +#include "MBufRawPacket.h" +#include "MacAddress.h" +#include "SystemUtils.h" #include #include #include -#include "MacAddress.h" -#include "SystemUtils.h" -#include "Device.h" -#include "MBufRawPacket.h" /** * @file - * This file and DpdkDeviceList.h provide PcapPlusPlus C++ wrapper for DPDK (stands for data-plan development kit). What is - * DPDK? as quoting from http://dpdk.org: "DPDK is a set of libraries and drivers for fast packet processing... These libraries can be used to: - * receive and send packets within the minimum number of CPU cycles (usually less than 80 cycles)... develop fast packet capture algorithms - * (tcpdump-like)... run third-party fast path stacks... Some packet processing functions have been benchmarked up to hundreds million - * frames per second, using 64-byte packets with a PCIe NIC"
- * As DPDK API is written in C, PcapPlusPlus wraps the main functionality in a C++ easy-to-use classes which should have minimum affect - * on performance and packet processing rate. In addition it brings DPDK to the PcapPlusPlus framework and API so you can use DPDK - * together with other PcapPlusPlus features such as packet parsing and editing, etc.
- * So how DPDK basically works? in order to boost packet processing performance on a commodity server DPDK is bypassing the Linux kernel. - * All the packet processing activity happens in the user space so basically packets are delivered from NIC hardware queues directly - * to user-space shared memory without going through the kernel. In addition DPDK uses polling instead of handling interrupts for each - * arrived packet (as interrupts create some delays). Other methods to boost packets processing implemented by DPDK are using Hugepages to - * decrease the size of TLB that results in a much faster virtual to physical page conversion, thread affinity to bind threads to a - * specific core, lock-free user-space multi-core synchronization using rings data structures and NUMA awareness to avoid expensive data - * transfers between sockets.
- * Not every NIC supports kernel-bypass capabilities so DPDK cannot work with any NIC. The list of supported NICs are in DPDK's web-site - * http://dpdk.org/doc/nics . For each such NIC the DPDK framework provides a module that called poll-mode-driver (PMD in short) that - * enables this NIC to the working with DPDK. PcapPlusPlus wasn't tested with most PMDs but all of them should theoretically work as - * PcapPlusPlus doesn't change the PMD behavior
- * DPDK has another basic data-structure called mbuf. An mbuf is DPDK wrapper struct for network packets. When working with packets - * in DPDK you actually work with mbufs. The mbuf contains the packet data (obviously) but also some metadata on the packet such - * as the DPDK port it was captured on, packet ref-count (which allows it to be referenced by several objects), etc. One important - * concept is that DPDK doesn't allocate mbufs on-the-fly but uses mbuf pools. These pools is allocated on application startup and - * used throughout the application. The goal of this, of course, is increasing packet processing performance as allocating memory has - * its cost. So pool size is important and varies between applications. For example: an application that stores packets in memory - * has to have a large pool of mbufs so mbufs doesn't run-out. PcapPlusPlus enables to choose the pool size at startup
- *
+ * This file and DpdkDeviceList.h provide PcapPlusPlus C++ wrapper for DPDK + * (stands for data-plan development kit). What is DPDK? as quoting from + * http://dpdk.org: "DPDK is a set of libraries and drivers for fast packet + * processing... These libraries can be used to: receive and send packets within + * the minimum number of CPU cycles (usually less than 80 cycles)... develop + * fast packet capture algorithms (tcpdump-like)... run third-party fast path + * stacks... Some packet processing functions have been benchmarked up to + * hundreds million frames per second, using 64-byte packets with a PCIe + * NIC"
As DPDK API is written in C, PcapPlusPlus wraps the main + * functionality in a C++ easy-to-use classes which should have minimum affect + * on performance and packet processing rate. In addition it brings DPDK to the + * PcapPlusPlus framework and API so you can use DPDK together with other + * PcapPlusPlus features such as packet parsing and editing, etc.
So how + * DPDK basically works? in order to boost packet processing performance on a + * commodity server DPDK is bypassing the Linux kernel. All the packet + * processing activity happens in the user space so basically packets are + * delivered from NIC hardware queues directly to user-space shared memory + * without going through the kernel. In addition DPDK uses polling instead of + * handling interrupts for each arrived packet (as interrupts create some + * delays). Other methods to boost packets processing implemented by DPDK are + * using Hugepages to decrease the size of TLB that results in a much faster + * virtual to physical page conversion, thread affinity to bind threads to a + * specific core, lock-free user-space multi-core synchronization using rings + * data structures and NUMA awareness to avoid expensive data transfers between + * sockets.
Not every NIC supports kernel-bypass capabilities so DPDK cannot + * work with any NIC. The list of supported NICs are in DPDK's web-site + * http://dpdk.org/doc/nics . For each such NIC the DPDK framework provides a + * module that called poll-mode-driver (PMD in short) that enables this NIC to + * the working with DPDK. PcapPlusPlus wasn't tested with most PMDs but all of + * them should theoretically work as PcapPlusPlus doesn't change the PMD + * behavior
DPDK has another basic data-structure called mbuf. An mbuf is + * DPDK wrapper struct for network packets. When working with packets in DPDK + * you actually work with mbufs. The mbuf contains the packet data (obviously) + * but also some metadata on the packet such as the DPDK port it was captured + * on, packet ref-count (which allows it to be referenced by several objects), + * etc. One important concept is that DPDK doesn't allocate mbufs on-the-fly but + * uses mbuf pools. These pools is allocated on application startup and used + * throughout the application. The goal of this, of course, is increasing packet + * processing performance as allocating memory has its cost. So pool size is + * important and varies between applications. For example: an application that + * stores packets in memory has to have a large pool of mbufs so mbufs doesn't + * run-out. PcapPlusPlus enables to choose the pool size at startup

* PcapPlusPlus main wrapper classes for DPDK are: - * - DpdkDevice - a class that wraps a DPDK port and provides all capabilities of receiving and sending packets to this port - * - DpdkDeviceList - a singleton class that initializes the DPDK infrastructure and creates DpdkDevice instances to all available ports. - * In addition it allows starting and stopping of worker threads - * - MBufRawPacket - a child class to RawPacket which customizes it for working with mbuf - * - In addition PcapPlusPlus provides a shell script to initialize DPDK prerequisites: setup_dpdk.py. This is an easy-to-use script - * that sets up huge-pages, loads DPDK kernel module and sets up the NICs that will be used by DPDK. This script must run before an - * application that uses DPDK runs. If you forgot to run it the application will fail with an appropriate error that will remind + * - DpdkDevice - a class that wraps a DPDK port and provides all + * capabilities of receiving and sending packets to this port + * - DpdkDeviceList - a singleton class that initializes the DPDK + * infrastructure and creates DpdkDevice instances to all available ports. In + * addition it allows starting and stopping of worker threads + * - MBufRawPacket - a child class to RawPacket which customizes it for + * working with mbuf + * - In addition PcapPlusPlus provides a shell script to initialize DPDK + * prerequisites: setup_dpdk.py. This is an easy-to-use script that sets up + * huge-pages, loads DPDK kernel module and sets up the NICs that will be used + * by DPDK. This script must run before an application that uses DPDK runs. If + * you forgot to run it the application will fail with an appropriate error that + * will remind * * DPDK initialization using PcapPlusPlus: * - Before application runs: run the setup_dpdk.py script - * - On application startup call DpdkDeviceList#initDpdk() static method to initialize DPDK infrastructure and DpdkDevice instances + * - On application startup call DpdkDeviceList#initDpdk() static method to + * initialize DPDK infrastructure and DpdkDevice instances * - Open the relevant DpdkDevice(s) * - Send & receive packets... */ @@ -62,769 +84,933 @@ struct rte_eth_conf; struct rte_eth_dev_tx_buffer; /** -* \namespace pcpp -* \brief The main namespace for the PcapPlusPlus lib -*/ -namespace pcpp -{ + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp { #define DPDK_MAX_RX_QUEUES 16 #define DPDK_MAX_TX_QUEUES 16 #define PCPP_RSS_HASH_MAGIC_NUMBER 0x123456 - class DpdkDeviceList; - class DpdkDevice; - - /** - * An enum describing all PMD (poll mode driver) types supported by DPDK. For more info about these PMDs please visit the DPDK web-site - */ - enum DpdkPMDType - { - /** Unknown PMD type */ - PMD_UNKNOWN, - /** Link Bonding for 1GbE and 10GbE ports to allow the aggregation of multiple (slave) NICs into a single logical interface*/ - PMD_BOND, - /** Intel E1000 PMD */ - PMD_E1000EM, - /** Intel 1GbE PMD */ - PMD_IGB, - /** Intel 1GbE virtual function PMD */ - PMD_IGBVF, - /** Cisco enic (UCS Virtual Interface Card) PMD */ - PMD_ENIC, - /** Intel fm10k PMD */ - PMD_FM10K, - /** Intel 40GbE PMD */ - PMD_I40E, - /** Intel 40GbE virtual function PMD */ - PMD_I40EVF, - /** Intel 10GbE PMD */ - PMD_IXGBE, - /** Intel 10GbE virtual function PMD */ - PMD_IXGBEVF, - /** Mellanox ConnectX-3, ConnectX-3 Pro PMD */ - PMD_MLX4, - /** Null PMD */ - PMD_NULL, - /** pcap file PMD */ - PMD_PCAP, - /** ring-based (memory) PMD */ - PMD_RING, - /** VirtIO PMD */ - PMD_VIRTIO, - /** VMWare VMXNET3 PMD */ - PMD_VMXNET3, - /** Xen Project PMD */ - PMD_XENVIRT, - /** AF_PACKET PMD */ - PMD_AF_PACKET - }; - - /** - * @typedef OnDpdkPacketsArriveCallback - * A callback that is called when a burst of packets are captured by DpdkDevice - * @param[in] packets A pointer to an array of MBufRawPacket - * @param[in] numOfPackets The length of the array - * @param[in] threadId The thread/core ID who captured the packets - * @param[in] device A pointer to the DpdkDevice who captured the packets - * @param[in] userCookie The user cookie assigned by the user in DpdkDevice#startCaptureSingleThread() or DpdkDevice#startCaptureMultiThreads - */ - typedef void (*OnDpdkPacketsArriveCallback)(MBufRawPacket* packets, uint32_t numOfPackets, uint8_t threadId, DpdkDevice* device, void* userCookie); - - /** - * @class DpdkDevice - * Encapsulates a DPDK port and enables receiving and sending packets using DPDK as well as getting interface info & status, packet - * statistics, etc. This class has no public c'tor as it's constructed by DpdkDeviceList during initialization.
- * - * __RX/TX queues__: modern NICs provide hardware load-balancing for packets. This means that each packet received by the NIC is hashed - * by one or more parameter (IP address, port, etc.) and goes into one of several RX queues provided by the NIC. This enables - * applications to work in a multi-core environment where each core can read packets from different RX queue(s). Same goes for TX - * queues: it's possible to write packets to different TX queues and the NIC is taking care of sending them to the network. - * Different NICs provide different number of RX and TX queues. DPDK supports this capability and enables the user to open the - * DPDK port (DpdkDevice) with a single or multiple RX and TX queues. When receiving packets the user can decide from which RX queue - * to read from, and when transmitting packets the user can decide to which TX queue to send them to. RX/TX queues are configured - * when opening the DpdkDevice (see openMultiQueues())
- * - * __Capturing packets__: there are two ways to capture packets using DpdkDevice: - * - using worker threads (see DpdkDeviceList#startDpdkWorkerThreads() ). When using this method the worker should use the - * DpdkDevice#receivePackets() methods to get packets from the DpdkDevice - * - by setting a callback which is invoked each time a burst of packets arrives. For more details see - * DpdkDevice#startCaptureSingleThread() - * - * __Sending packets:__ DpdkDevice has various methods for sending packets. They enable sending raw packets, parsed packets, etc. - * for all opened TX queues. Also, DPDK provides an option to buffer TX packets and send them only when reaching a certain threshold (you - * can read more about it here: http://dpdk.org/doc/api/rte__ethdev_8h.html#a0e941a74ae1b1b886764bc282458d946). DpdkDevice supports that - * option as well. See DpdkDevice#sendPackets()
- * - * __Get interface info__: DpdkDevice provides all kind of information on the interface/device such as MAC address, MTU, link status, - * PCI address, PMD (poll-mode-driver) used for this port, etc. In addition it provides RX/TX statistics when receiving or sending - * packets
- * - * __Known limitations:__ - * - BPF filters are currently not supported by this device (as opposed to other PcapPlusPlus device types. This means that the - * device cannot filter packets before they get to the user - * - It's not possible to set or change NIC load-balancing method. DPDK provides this capability but it's still not - * supported by DpdkDevice - */ - class DpdkDevice : public IDevice - { - friend class DpdkDeviceList; - friend class MBufRawPacket; - public: - - /** - * An enum describing all RSS (Receive Side Scaling) hash functions supported in DPDK. Notice not all - * PMDs support all types of hash functions - */ - enum DpdkRssHashFunction - { - /** No RSS */ - RSS_NONE = 0, - /** IPv4 based flow */ - RSS_IPV4 = 0x1, - /** Fragmented IPv4 based flow */ - RSS_FRAG_IPV4 = 0x2, - /** Non-fragmented IPv4 + TCP flow */ - RSS_NONFRAG_IPV4_TCP = 0x4, - /** Non-fragmented IPv4 + UDP flow */ - RSS_NONFRAG_IPV4_UDP = 0x8, - /** Non-fragmented IPv4 + SCTP flow */ - RSS_NONFRAG_IPV4_SCTP = 0x10, - /** Non-fragmented IPv4 + non TCP/UDP/SCTP flow */ - RSS_NONFRAG_IPV4_OTHER = 0x20, - /** IPv6 based flow */ - RSS_IPV6 = 0x40, - /** Fragmented IPv6 based flow */ - RSS_FRAG_IPV6 = 0x80, - /** Non-fragmented IPv6 + TCP flow */ - RSS_NONFRAG_IPV6_TCP = 0x100, - /** Non-fragmented IPv6 + UDP flow */ - RSS_NONFRAG_IPV6_UDP = 0x200, - /** Non-fragmented IPv6 + SCTP flow */ - RSS_NONFRAG_IPV6_SCTP = 0x400, - /** Non-fragmented IPv6 + non TCP/UDP/SCTP flow */ - RSS_NONFRAG_IPV6_OTHER = 0x800, - /** L2 payload based flow */ - RSS_L2_PAYLOAD = 0x1000, - /** IPv6 Ex based flow */ - RSS_IPV6_EX = 0x2000, - /** IPv6 + TCP Ex based flow */ - RSS_IPV6_TCP_EX = 0x4000, - /** IPv6 + UDP Ex based flow */ - RSS_IPV6_UDP_EX = 0x8000, - /** Consider device port number as a flow differentiator */ - RSS_PORT = 0x10000, - /** VXLAN protocol based flow */ - RSS_VXLAN = 0x20000, - /** GENEVE protocol based flow */ - RSS_GENEVE = 0x40000, - /** NVGRE protocol based flow */ - RSS_NVGRE = 0x80000, - /** All RSS functions supported by the device */ - RSS_ALL_SUPPORTED = -1, - /** A default set of RSS functions supported by the device */ - RSS_DEFAULT = PCPP_RSS_HASH_MAGIC_NUMBER - }; - - /** - * @struct DpdkDeviceConfiguration - * A struct that contains user configurable parameters for opening a DpdkDevice. All of these parameters have default values so - * the user doesn't have to use these parameters or understand exactly what is their effect - */ - struct DpdkDeviceConfiguration - { - /** - * When configuring a DPDK RX queue, DPDK creates descriptors it will use for receiving packets from the network to this RX queue. - * This parameter enables to configure the number of descriptors that will be created for each RX queue - */ - uint16_t receiveDescriptorsNumber; - - /** - * When configuring a DPDK TX queue, DPDK creates descriptors it will use for transmitting packets to the network through this TX queue. - * This parameter enables to configure the number of descriptors that will be created for each TX queue - */ - uint16_t transmitDescriptorsNumber; - - /** - * Set the TX buffer flush timeout in millisecond (only relevant if sending packets using DPDK TX buffer mechanism). - * A value of zero means no timeout - */ - uint16_t flushTxBufferTimeout; - - /** - * When configuring a DPDK device, DPDK supports to activate the Receive Side Scaling (RSS) feature to distribute traffic between the RX queues - * This parameter points to an array holding the RSS key to use for hashing specific header fields of received packets. - * The length of this array should be indicated by rssKeyLength below. - * Supplying a NULL value causes a default random hash key to be used by the device driver - */ - uint8_t* rssKey; - - /** - * This parameter indicates the length in bytes of the array pointed by rssKey. - * This length will be checked in i40e only. Others assume 40 bytes to be used. - */ - uint8_t rssKeyLength; - - /** - * This parameter enables to configure the types of packets to which the RSS hashing must be applied. The value - * is a mask composed of hash functions described in DpdkRssHashFunction enum. Supplying a value equal to zero - * disables the RSS feature. Supplying a value equal to -1 enables all hash functions supported by this PMD - */ - uint64_t rssHashFunction; - - /** - * A c'tor for this struct - * @param[in] receiveDescriptorsNumber An optional parameter for defining the number of RX descriptors that will be allocated for each RX queue. - * Default value is 128 - * @param[in] transmitDescriptorsNumber An optional parameter for defining the number of TX descriptors that will be allocated for each TX queue. - * Default value is 512 - * @param[in] flushTxBufferTimeout An optional parameter for setting TX buffer timeout in usec. Default value is 100 usec - * @param[in] rssHashFunction This parameter enable to configure the types of packets to which the RSS hashing must be applied. - * The value provided here should be a mask composed of hash functions described in DpdkRssHashFunction enum. - * The default value is RSS_DEFAULT. - * @param[in] rssKey A pointer to an array holding the RSS key to use for hashing specific header of received packets. If not - * specified, there is a default key defined inside DpdkDevice - * @param[in] rssKeyLength The length in bytes of the array pointed by rssKey. Default value is the length of default rssKey - */ - explicit DpdkDeviceConfiguration(uint16_t receiveDescriptorsNumber = 128, - uint16_t transmitDescriptorsNumber = 512, - uint16_t flushTxBufferTimeout = 100, - uint64_t rssHashFunction = RSS_DEFAULT, - uint8_t* rssKey = DpdkDevice::m_RSSKey, - uint8_t rssKeyLength = 40) - { - this->receiveDescriptorsNumber = receiveDescriptorsNumber; - this->transmitDescriptorsNumber = transmitDescriptorsNumber; - this->flushTxBufferTimeout = flushTxBufferTimeout; - this->rssKey = rssKey; - this->rssKeyLength = rssKeyLength; - this->rssHashFunction = rssHashFunction; - } - }; - - /** - * @struct LinkStatus - * A struct that contains the link status of a DpdkDevice (DPDK port). Returned from DpdkDevice#getLinkStatus() - */ - struct LinkStatus - { - /** Enum for describing link duplex */ - enum LinkDuplex - { - /** Full duplex */ - FULL_DUPLEX, - /** Half duplex */ - HALF_DUPLEX - }; - - /** True if link is up, false if it's down */ - bool linkUp; - /** Link speed in Mbps (for example: 10Gbe will show 10000) */ - int linkSpeedMbps; - /** Link duplex (half/full duplex) */ - LinkDuplex linkDuplex; - }; - - /** - * @struct RxTxStats - * A container for RX/TX statistics - */ - struct RxTxStats - { - /** Total number of packets */ - uint64_t packets; - /** Total number of successfully received bytes */ - uint64_t bytes; - /** Packets per second */ - uint64_t packetsPerSec; - /** Bytes per second */ - uint64_t bytesPerSec; - }; - - /** - * @struct DpdkDeviceStats - * A container for DpdkDevice statistics - */ - struct DpdkDeviceStats - { - /** DpdkDevice ID */ - uint8_t devId; - /** The timestamp of when the stats were written */ - timespec timestamp; - /** RX statistics per RX queue */ - RxTxStats rxStats[DPDK_MAX_RX_QUEUES]; - /** TX statistics per TX queue */ - RxTxStats txStats[DPDK_MAX_RX_QUEUES]; - /** RX statistics, aggregated for all RX queues */ - RxTxStats aggregatedRxStats; - /** TX statistics, aggregated for all TX queues */ - RxTxStats aggregatedTxStats; - /** Total number of RX packets dropped by H/W because there are no available buffers (i.e RX queues are full) */ - uint64_t rxPacketsDroppedByHW; - /** Total number of erroneous packets */ - uint64_t rxErroneousPackets; - /** Total number of RX mbuf allocation failures */ - uint64_t rxMbufAlocFailed; - }; - - virtual ~DpdkDevice(); - - /** - * @return The device ID (DPDK port ID) - */ - int getDeviceId() const { return m_Id; } - /** - * @return The device name which is in the format of 'DPDK_[PORT-ID]' - */ - std::string getDeviceName() const { return m_DeviceName; } - - /** - * @return The MAC address of the device (DPDK port) - */ - MacAddress getMacAddress() const { return m_MacAddress; } - - /** - * @return The name of the PMD (poll mode driver) DPDK is using for this device. You can read about PMDs in the DPDK documentation: - * http://dpdk.org/doc/guides/prog_guide/poll_mode_drv.html - */ - std::string getPMDName() const { return m_PMDName; } - - /** - * @return The enum type of the PMD (poll mode driver) DPDK is using for this device. You can read about PMDs in the DPDK documentation: - * http://dpdk.org/doc/guides/prog_guide/poll_mode_drv.html - */ - DpdkPMDType getPMDType() const { return m_PMDType; } - - /** - * @return The PCI address of the device - */ - std::string getPciAddress() const { return m_PciAddress; } - - /** - * @return The device's maximum transmission unit (MTU) in bytes - */ - uint16_t getMtu() const { return m_DeviceMtu; } - - /** - * Set a new maximum transmission unit (MTU) for this device - * @param[in] newMtu The new MTU in bytes - * @return True if MTU was set successfully, false if operation failed or if PMD doesn't support changing the MTU - */ - bool setMtu(uint16_t newMtu); - - /** - * @return True if this device is a virtual interface (such as VMXNET3, 1G/10G virtual function, etc.), false otherwise - */ - bool isVirtual() const; - - /** - * Get the link status (link up/down, link speed and link duplex) - * @param[out] linkStatus A reference to object the result shall be written to - */ - void getLinkStatus(LinkStatus& linkStatus) const; - - /** - * @return The core ID used in this context - */ - uint32_t getCurrentCoreId() const; - - /** - * @return The number of RX queues currently opened for this device (as configured in openMultiQueues() ) - */ - uint16_t getNumOfOpenedRxQueues() const { return m_NumOfRxQueuesOpened; } - - /** - * @return The number of TX queues currently opened for this device (as configured in openMultiQueues() ) - */ - uint16_t getNumOfOpenedTxQueues() const { return m_NumOfTxQueuesOpened; } - - /** - * @return The total number of RX queues available on this device - */ - uint16_t getTotalNumOfRxQueues() const { return m_TotalAvailableRxQueues; } - - /** - * @return The total number of TX queues available on this device - */ - uint16_t getTotalNumOfTxQueues() const { return m_TotalAvailableTxQueues; } - - - /** - * Receive raw packets from the network - * @param[out] rawPacketsArr A vector where all received packets will be written into - * @param[in] rxQueueId The RX queue to receive packets from - * @return The number of packets received. If an error occurred 0 will be returned and the error will be printed to log - */ - uint16_t receivePackets(MBufRawPacketVector& rawPacketsArr, uint16_t rxQueueId) const; - - /** - * Receive raw packets from the network. Please notice that in terms of performance, this is the best method to use - * for receiving packets because out of all receivePackets overloads this method requires the least overhead and is - * almost as efficient as receiving packets directly through DPDK. So if performance is a critical factor in your - * application, please use this method - * @param[out] rawPacketsArr A pointer to an array of MBufRawPacket pointers where all received packets will be written into. The array is expected to - * be allocated by the user and its length should be provided in rawPacketArrLength. Number of packets received will be returned. - * Notice it's the user responsibility to free the array and its content when done using it - * @param[out] rawPacketArrLength The length of MBufRawPacket pointers array - * @param[in] rxQueueId The RX queue to receive packets from - * @return The number of packets received. If an error occurred 0 will be returned and the error will be printed to log - */ - uint16_t receivePackets(MBufRawPacket** rawPacketsArr, uint16_t rawPacketArrLength, uint16_t rxQueueId) const; - - /** - * Receive parsed packets from the network - * @param[out] packetsArr A pointer to an allocated array of Packet pointers where all received packets will be written into. The array is expected to - * be allocated by the user and its length should be provided in packetsArrLength. Number of packets received will be returned. - * Notice it's the user responsibility to free the array and its content when done using it - * @param[out] packetsArrLength The length of Packet pointers array - * @param[in] rxQueueId The RX queue to receive packets from - * @return The number of packets received. If an error occurred 0 will be returned and the error will be printed to log - */ - uint16_t receivePackets(Packet** packetsArr, uint16_t packetsArrLength, uint16_t rxQueueId) const; - - /** - * Send an array of MBufRawPacket to the network. Please notice the following:
- * - In terms of performance, this is the best method to use for sending packets because out of all sendPackets overloads - * this method requires the least overhead and is almost as efficient as sending the packets directly through DPDK. So if performance - * is a critical factor in your application, please use this method - * - If the number of packets to send is higher than 64 this method will run multiple iterations of sending packets to DPDK, each - * iteration of 64 packets - * - If the number of packets to send is higher than a threshold of 80% of total TX descriptors (which is typically around 400 packets), - * then after reaching this threshold there is a built-in 0.2 sec sleep to let the TX descriptors clean - * - The mbufs used in this method aren't freed by this method, they will be transparently freed by DPDK - *

- * @param[in] rawPacketsArr A pointer to an array of MBufRawPacket - * @param[in] arrLength The length of the array - * @param[in] txQueueId An optional parameter which indicates to which TX queue the packets will be sent to. The default is - * TX queue 0 - * @param[in] useTxBuffer A flag which indicates whether to use TX buffer mechanism or not. To read more about DPDK's - * TX buffer mechanism please refer to DpdkDevice class description. Default value is false (don't use this mechanism) - * @return The number of packets actually and successfully sent. If device is not opened or TX queue isn't open, 0 will be returned. - * Also, if TX buffer is being used and packets are buffered, some or all may not be actually sent - */ - uint16_t sendPackets(MBufRawPacket** rawPacketsArr, uint16_t arrLength, uint16_t txQueueId = 0, bool useTxBuffer = false); - - /** - * Send an array of parsed packets to the network. Please notice the following:
- * - If some or all of the packets contain raw packets which aren't of type MBufRawPacket, a new temp MBufRawPacket instances - * will be created and packet data will be copied to them. This is necessary to allocate mbufs which will store the data to be sent. - * If performance is a critical factor please make sure you send parsed packets that contain only raw packets of type MBufRawPacket - * - If the number of packets to send is higher than 64 this method will run multiple iterations of sending packets to DPDK, each - * iteration of 64 packets - * - If the number of packets to send is higher than a threshold of 80% of total TX descriptors (which is typically around 400 packets), - * then after reaching this threshold there is a built-in 0.2 sec sleep to let the TX descriptors clean - * - The mbufs used or allocated in this method aren't freed by this method, they will be transparently freed by DPDK - *

- * @param[in] packetsArr A pointer to an array of parsed packet pointers - * @param[in] arrLength The length of the array - * @param[in] txQueueId An optional parameter which indicates to which TX queue the packets will be sent to. The default is - * TX queue 0 - * @param[in] useTxBuffer A flag which indicates whether to use TX buffer mechanism or not. To read more about DPDK's - * TX buffer mechanism please refer to DpdkDevice class description. Default value is false (don't use this mechanism) - * @return The number of packets actually and successfully sent. If device is not opened or TX queue isn't open, 0 will be returned. - * Also, if TX buffer is being used and packets are buffered, some or all may not be actually sent - */ - uint16_t sendPackets(Packet** packetsArr, uint16_t arrLength, uint16_t txQueueId = 0, bool useTxBuffer = false); - - /** - * Send a vector of MBufRawPacket pointers to the network. Please notice the following:
- * - If the number of packets to send is higher than 64 this method will run multiple iterations of sending packets to DPDK, each - * iteration of 64 packets - * - If the number of packets to send is higher than a threshold of 80% of total TX descriptors (which is typically around 400 packets), - * then after reaching this threshold there is a built-in 0.2 sec sleep to let the TX descriptors clean - * - The mbufs used in this method aren't freed by this method, they will be transparently freed by DPDK - *

- * @param[in] rawPacketsVec The vector of raw packet - * @param[in] txQueueId An optional parameter which indicates to which TX queue the packets will be sent to. The default is - * TX queue 0 - * @param[in] useTxBuffer A flag which indicates whether to use TX buffer mechanism or not. To read more about DPDK's - * TX buffer mechanism please refer to DpdkDevice class description. Default value is false (don't use this mechanism) - * @return The number of packets actually and successfully sent. If device is not opened or TX queue isn't open, 0 will be returned. - * Also, if TX buffer is being used and packets are buffered, some or all may not be actually sent - */ - uint16_t sendPackets(MBufRawPacketVector& rawPacketsVec, uint16_t txQueueId = 0, bool useTxBuffer = false); - - /** - * Send a vector of RawPacket pointers to the network. Please notice the following:
- * - If some or all of the raw packets aren't of type MBufRawPacket, a new temp MBufRawPacket instances will be created - * and packet data will be copied to them. This is necessary to allocate mbufs which will store the data to be sent. If - * performance is a critical factor please make sure you send only raw packets of type MBufRawPacket (or use the sendPackets overload - * that sends MBufRawPacketVector) - * - If the number of packets to send is higher than 64 this method will run multiple iterations of sending packets to DPDK, each - * iteration of 64 packets - * - If the number of packets to send is higher than a threshold of 80% of total TX descriptors (which is typically around 400 packets), - * then after reaching this threshold there is a built-in 0.2 sec sleep to let the TX descriptors clean - * - The mbufs used or allocated in this method aren't freed by this method, they will be transparently freed by DPDK - *

- * @param[in] rawPacketsVec The vector of raw packet - * @param[in] txQueueId An optional parameter which indicates to which TX queue the packets will be sent to. The default is - * TX queue 0 - * @param[in] useTxBuffer A flag which indicates whether to use TX buffer mechanism or not. To read more about DPDK's - * TX buffer mechanism please refer to DpdkDevice class description. Default value is false (don't use this mechanism) - * @return The number of packets actually and successfully sent. If device is not opened or TX queue isn't open, 0 will be returned. - * Also, if TX buffer is being used and packets are buffered, some or all may not be actually sent - */ - uint16_t sendPackets(RawPacketVector& rawPacketsVec, uint16_t txQueueId = 0, bool useTxBuffer = false); - - /** - * Send a raw packet to the network. Please notice that if the raw packet isn't of type MBufRawPacket, a new temp MBufRawPacket - * will be created and the data will be copied to it. This is necessary to allocate an mbuf which will store the data to be sent. - * If performance is a critical factor please make sure you send a raw packet of type MBufRawPacket. Please also notice that the - * mbuf used or allocated in this method isn't freed by this method, it will be transparently freed by DPDK - * @param[in] rawPacket The raw packet to send - * @param[in] txQueueId An optional parameter which indicates to which TX queue the packet will be sent to. The default is - * TX queue 0 - * @param[in] useTxBuffer A flag which indicates whether to use TX buffer mechanism or not. To read more about DPDK's - * TX buffer mechanism please refer to DpdkDevice class description. Default value is false (don't use this mechanism) - * @return True if packet was sent successfully or false if device is not opened, TX queue isn't opened, or if the packet wasn't - * sent for any other reason. Please notice that when using TX buffers the packet may be buffered and not sent immediately, which - * may also result in returning false - */ - bool sendPacket(RawPacket& rawPacket, uint16_t txQueueId = 0, bool useTxBuffer = false); - - /** - * Send a MBufRawPacket to the network. Please notice that the mbuf used in this method isn't freed by this method, it will be - * transparently freed by DPDK - * @param[in] rawPacket The MBufRawPacket to send - * @param[in] txQueueId An optional parameter which indicates to which TX queue the packet will be sent to. The default is - * TX queue 0 - * @param[in] useTxBuffer A flag which indicates whether to use TX buffer mechanism or not. To read more about DPDK's - * TX buffer mechanism please refer to DpdkDevice class description. Default value is false (don't use this mechanism) - * @return True if packet was sent successfully or false if device is not opened, TX queue isn't opened, or if the packet wasn't - * sent for any other reason. Please notice that when using TX buffers the packet may be buffered and not sent immediately, which - * may also result in returning false - */ - bool sendPacket(MBufRawPacket& rawPacket, uint16_t txQueueId = 0, bool useTxBuffer = false); - - /** - * Send a parsed packet to the network. Please notice that the mbuf used or allocated in this method isn't freed by this method, - * it will be transparently freed by DPDK - * @param[in] packet The parsed packet to send. Please notice that if the packet contains a raw packet which isn't of type - * MBufRawPacket, a new temp MBufRawPacket will be created and the data will be copied to it. This is necessary to - * allocate an mbuf which will store the data to be sent. If performance is a critical factor please make sure you send a - * parsed packet that contains a raw packet of type MBufRawPacket - * @param[in] txQueueId An optional parameter which indicates to which TX queue the packet will be sent on. The default is - * TX queue 0 - * @param[in] useTxBuffer A flag which indicates whether to use TX buffer mechanism or not. To read more about DPDK's - * TX buffer mechanism please refer to DpdkDevice class description. Default value is false (don't use this mechanism) - * @return True if packet was sent successfully or false if device is not opened, TX queue isn't opened, or if the packet wasn't - * sent for any other reason. Please notice that when using TX buffers the packet may be buffered and not sent immediately, which - * may also result in returning false - */ - bool sendPacket(Packet& packet, uint16_t txQueueId = 0, bool useTxBuffer = false); - - /** - * Overridden method from IPcapDevice. __BPF filters are currently not implemented for DpdkDevice__ - * @param[in] filter Not used in this method - * @return Always false with a "Filters aren't supported in DPDK device" error message - */ - bool setFilter(GeneralFilter& filter); - - /** - * Overridden method from IPcapDevice. __BPF filters are currently not implemented for DpdkDevice__ - * @param[in] filterAsString Not used in this method - * @return Always false with a "Filters aren't supported in DPDK device" error message - */ - bool setFilter(std::string filterAsString); - - /** - * Open the DPDK device. Notice opening the device only makes it ready to use, it doesn't start packet capturing. This method initializes RX and TX queues, - * configures the DPDK port and starts it. Call close() to close the device. The device is opened in promiscuous mode - * @param[in] numOfRxQueuesToOpen Number of RX queues to setup. This number must be smaller or equal to the return value of getTotalNumOfRxQueues() - * @param[in] numOfTxQueuesToOpen Number of TX queues to setup. This number must be smaller or equal to the return value of getTotalNumOfTxQueues() - * @param[in] config Optional parameter for defining special port configuration parameters such as number of receive/transmit descriptors. If not set the default - * parameters will be set (see DpdkDeviceConfiguration) - * @return True if the device was opened successfully, false if device is already opened, if RX/TX queues configuration failed or of DPDK port - * configuration and startup failed - */ - bool openMultiQueues(uint16_t numOfRxQueuesToOpen, uint16_t numOfTxQueuesToOpen, const DpdkDeviceConfiguration& config = DpdkDeviceConfiguration()); - - /** - * There are two ways to capture packets using DpdkDevice: one of them is using worker threads (see DpdkDeviceList#startDpdkWorkerThreads() ) and - * the other way is setting a callback which is invoked each time a burst of packets is captured. This method implements the second way. - * After invoking this method the DpdkDevice enters capture mode and starts capturing packets. - * This method assumes there is only 1 RX queue opened for this device, otherwise an error is returned. It then allocates a core and creates 1 thread - * that runs in an endless loop and tries to capture packets using DPDK. Each time a burst of packets is captured the user callback is invoked with the user - * cookie as a parameter. This loop continues until stopCapture() is called. Notice: since the callback is invoked for every packet burst - * using this method can be slower than using worker threads. On the other hand, it's a simpler way comparing to worker threads - * @param[in] onPacketsArrive The user callback which will be invoked each time a packet burst is captured by the device - * @param[in] onPacketsArriveUserCookie The user callback is invoked with this cookie as a parameter. It can be used to pass - * information from the user application to the callback - * @return True if capture thread started successfully or false if device is already in capture mode, number of opened RX queues isn't equal - * to 1, if the method couldn't find an available core to allocate for the capture thread, or if thread invocation failed. In - * all of these cases an appropriate error message will be printed - */ - bool startCaptureSingleThread(OnDpdkPacketsArriveCallback onPacketsArrive, void* onPacketsArriveUserCookie); - - /** - * This method does exactly what startCaptureSingleThread() does, but with more than one RX queue / capturing thread. It's called - * with a core mask as a parameter and creates a packet capture thread on every core. Each capturing thread is assigned with a specific - * RX queue. This method assumes all cores in the core-mask are available and there are enough opened RX queues to match for each thread. - * If these assumptions are not true an error is returned. After invoking all threads, all of them run in an endless loop - * and try to capture packets from their designated RX queues. Each time a burst of packets is captured the callback is invoked with the user - * cookie and the thread ID that captured the packets - * @param[in] onPacketsArrive The user callback which will be invoked each time a burst of packets is captured by the device - * @param[in] onPacketsArriveUserCookie The user callback is invoked with this cookie as a parameter. It can be used to pass - * information from the user application to the callback - * @param coreMask The core-mask for creating the capture threads - * @return True if all capture threads started successfully or false if device is already in capture mode, not all cores in the core-mask are - * available to DPDK, there are not enough opened RX queues to match all cores in the core-mask, or if thread invocation failed. In - * all of these cases an appropriate error message will be printed - */ - bool startCaptureMultiThreads(OnDpdkPacketsArriveCallback onPacketsArrive, void* onPacketsArriveUserCookie, CoreMask coreMask); - - /** - * If device is in capture mode started by invoking startCaptureSingleThread() or startCaptureMultiThreads(), this method - * will stop all capturing threads and set the device to non-capturing mode - */ - void stopCapture(); - - /** - * @return The number of free mbufs in device's mbufs pool - */ - int getAmountOfFreeMbufs() const; - - /** - * @return The number of mbufs currently in use in device's mbufs pool - */ - int getAmountOfMbufsInUse() const; - - /** - * Retrieve RX/TX statistics from device - * @param[out] stats A reference to a DpdkDeviceStats object where stats will be written into - */ - void getStatistics(DpdkDeviceStats& stats) const; - - /** - * Clear device statistics - */ - void clearStatistics(); - - /** - * DPDK supports an option to buffer TX packets and send them only when reaching a certain threshold. This method enables - * the user to flush a TX buffer for certain TX queue and send the packets stored in it (you can read about it here: - * http://dpdk.org/doc/api/rte__ethdev_8h.html#a0e941a74ae1b1b886764bc282458d946). It has the option to flush only - * when timeout that was set in DpdkDeviceConfiguration#flushTxBufferTimeout expired or flush immediately regardless - * of the timeout. The usage of this method can be in the main loop where you can call this method once every a couple - * of iterations to make sure TX buffers are flushed - * @param[in] flushOnlyIfTimeoutExpired When set to true, flush will happen only if the timeout defined in - * DpdkDeviceConfiguration#flushTxBufferTimeout expired. If set to false flush will happen immediately. Default value - * is false - * @param[in] txQueueId The TX queue ID to flush its buffer. Default is 0 - * @return The number of packets sent after buffer was flushed - */ - uint16_t flushTxBuffer(bool flushOnlyIfTimeoutExpired = false, uint16_t txQueueId = 0); - - /** - * Check whether a specific RSS hash function is supported by this device (PMD) - * @param[in] rssHF RSS hash function to check - * @return True if this hash function is supported, false otherwise - */ - bool isDeviceSupportRssHashFunction(DpdkRssHashFunction rssHF) const; - - /** - * Check whether a mask of RSS hash functions is supported by this device (PMD) - * @param[in] rssHFMask RSS hash functions mask to check. This mask should be built from values in DpdkRssHashFunction enum - * @return True if all hash functions in this mask are supported, false otherwise - */ - bool isDeviceSupportRssHashFunction(uint64_t rssHFMask) const; - - /** - * @return A mask of all RSS hash functions supported by this device (PMD). This mask is built from values in DpdkRssHashFunction enum. - * Value of zero means RSS is not supported by this device - */ - uint64_t getSupportedRssHashFunctions() const; - - /** - * @return The RSS hash function mask configured for this device (PMD) - */ - uint64_t getConfiguredRssHashFunction() const; - - /** - * Translate RSS hash function mask to a list of their string representation - * @param rssHFMask RSS hash function mask - * @return RSS hash functions as strings - */ - std::vector rssHashFunctionMaskToString(uint64_t rssHFMask) const; - - //overridden methods - - /** - * Overridden method from IPcapDevice. It calls openMultiQueues() with 1 RX queue and 1 TX queue. - * Notice opening the device only makes it ready to use, it doesn't start packet capturing. The device is opened in promiscuous mode - * @return True if the device was opened successfully, false if device is already opened, if RX/TX queues configuration failed or of DPDK port - * configuration and startup failed - */ - bool open() { return openMultiQueues(1, 1); }; - - /** - * Close the DpdkDevice. When device is closed it's not possible work with it - */ - void close(); - - private: - - struct DpdkCoreConfiguration - { - int RxQueueId; - bool IsCoreInUse; - - void clear() { RxQueueId = -1; IsCoreInUse = false; } - - DpdkCoreConfiguration() : RxQueueId(-1), IsCoreInUse(false) {} - }; - - DpdkDevice(int port, uint32_t mBufPoolSize); - bool initMemPool(struct rte_mempool*& memPool, const char* mempoolName, uint32_t mBufPoolSize); - - bool configurePort(uint8_t numOfRxQueues, uint8_t numOfTxQueues); - bool initQueues(uint8_t numOfRxQueuesToInit, uint8_t numOfTxQueuesToInit); - bool startDevice(); - - static int dpdkCaptureThreadMain(void* ptr); - - void clearCoreConfiguration(); - bool initCoreConfigurationByCoreMask(CoreMask coreMask); - int getCoresInUseCount() const; - - void setDeviceInfo(); - - typedef rte_mbuf* (*PacketIterator)(void* packetStorage, int index); - uint16_t sendPacketsInner(uint16_t txQueueId, void* packetStorage, PacketIterator iter, int arrLength, bool useTxBuffer); - - uint64_t convertRssHfToDpdkRssHf(uint64_t rssHF) const; - uint64_t convertDpdkRssHfToRssHf(uint64_t dpdkRssHF) const; - - std::string m_DeviceName; - DpdkPMDType m_PMDType; - std::string m_PMDName; - std::string m_PciAddress; - - DpdkDeviceConfiguration m_Config; - - int m_Id; - MacAddress m_MacAddress; - uint16_t m_DeviceMtu; - struct rte_mempool* m_MBufMempool; - struct rte_eth_dev_tx_buffer** m_TxBuffers; - uint64_t m_TxBufferDrainTsc; - uint64_t* m_TxBufferLastDrainTsc; - DpdkCoreConfiguration m_CoreConfiguration[MAX_NUM_OF_CORES]; - uint16_t m_TotalAvailableRxQueues; - uint16_t m_TotalAvailableTxQueues; - uint16_t m_NumOfRxQueuesOpened; - uint16_t m_NumOfTxQueuesOpened; - OnDpdkPacketsArriveCallback m_OnPacketsArriveCallback; - void* m_OnPacketsArriveUserCookie; - bool m_StopThread; - - bool m_WasOpened; - - // RSS key used by the NIC for load balancing the packets between cores - static uint8_t m_RSSKey[40]; - - mutable DpdkDeviceStats m_PrevStats; - }; +class DpdkDeviceList; +class DpdkDevice; + +/** + * An enum describing all PMD (poll mode driver) types supported by DPDK. For + * more info about these PMDs please visit the DPDK web-site + */ +enum DpdkPMDType { + /** Unknown PMD type */ + PMD_UNKNOWN, + /** Link Bonding for 1GbE and 10GbE ports to allow the aggregation of multiple + (slave) NICs into a single logical interface*/ + PMD_BOND, + /** Intel E1000 PMD */ + PMD_E1000EM, + /** Intel 1GbE PMD */ + PMD_IGB, + /** Intel 1GbE virtual function PMD */ + PMD_IGBVF, + /** Cisco enic (UCS Virtual Interface Card) PMD */ + PMD_ENIC, + /** Intel fm10k PMD */ + PMD_FM10K, + /** Intel 40GbE PMD */ + PMD_I40E, + /** Intel 40GbE virtual function PMD */ + PMD_I40EVF, + /** Intel 10GbE PMD */ + PMD_IXGBE, + /** Intel 10GbE virtual function PMD */ + PMD_IXGBEVF, + /** Mellanox ConnectX-3, ConnectX-3 Pro PMD */ + PMD_MLX4, + /** Null PMD */ + PMD_NULL, + /** pcap file PMD */ + PMD_PCAP, + /** ring-based (memory) PMD */ + PMD_RING, + /** VirtIO PMD */ + PMD_VIRTIO, + /** VMWare VMXNET3 PMD */ + PMD_VMXNET3, + /** Xen Project PMD */ + PMD_XENVIRT, + /** AF_PACKET PMD */ + PMD_AF_PACKET +}; + +/** + * @typedef OnDpdkPacketsArriveCallback + * A callback that is called when a burst of packets are captured by DpdkDevice + * @param[in] packets A pointer to an array of MBufRawPacket + * @param[in] numOfPackets The length of the array + * @param[in] threadId The thread/core ID who captured the packets + * @param[in] device A pointer to the DpdkDevice who captured the packets + * @param[in] userCookie The user cookie assigned by the user in + * DpdkDevice#startCaptureSingleThread() or DpdkDevice#startCaptureMultiThreads + */ +typedef void (*OnDpdkPacketsArriveCallback)(MBufRawPacket* packets, + uint32_t numOfPackets, + uint8_t threadId, + DpdkDevice* device, + void* userCookie); + +/** + * @class DpdkDevice + * Encapsulates a DPDK port and enables receiving and sending packets using DPDK + * as well as getting interface info & status, packet statistics, etc. This + * class has no public c'tor as it's constructed by DpdkDeviceList during + * initialization.
+ * + * __RX/TX queues__: modern NICs provide hardware load-balancing for packets. + * This means that each packet received by the NIC is hashed by one or more + * parameter (IP address, port, etc.) and goes into one of several RX queues + * provided by the NIC. This enables applications to work in a multi-core + * environment where each core can read packets from different RX queue(s). Same + * goes for TX queues: it's possible to write packets to different TX queues and + * the NIC is taking care of sending them to the network. Different NICs provide + * different number of RX and TX queues. DPDK supports this capability and + * enables the user to open the DPDK port (DpdkDevice) with a single or multiple + * RX and TX queues. When receiving packets the user can decide from which RX + * queue to read from, and when transmitting packets the user can decide to + * which TX queue to send them to. RX/TX queues are configured when opening the + * DpdkDevice (see openMultiQueues())
+ * + * __Capturing packets__: there are two ways to capture packets using + * DpdkDevice: + * - using worker threads (see DpdkDeviceList#startDpdkWorkerThreads() ). + * When using this method the worker should use the DpdkDevice#receivePackets() + * methods to get packets from the DpdkDevice + * - by setting a callback which is invoked each time a burst of packets + * arrives. For more details see DpdkDevice#startCaptureSingleThread() + * + * __Sending packets:__ DpdkDevice has various methods for sending packets. They + * enable sending raw packets, parsed packets, etc. for all opened TX queues. + * Also, DPDK provides an option to buffer TX packets and send them only when + * reaching a certain threshold (you can read more about it here: + * http://dpdk.org/doc/api/rte__ethdev_8h.html#a0e941a74ae1b1b886764bc282458d946). + * DpdkDevice supports that option as well. See DpdkDevice#sendPackets()
+ * + * __Get interface info__: DpdkDevice provides all kind of information on the + * interface/device such as MAC address, MTU, link status, PCI address, PMD + * (poll-mode-driver) used for this port, etc. In addition it provides RX/TX + * statistics when receiving or sending packets
+ * + * __Known limitations:__ + * - BPF filters are currently not supported by this device (as opposed to + * other PcapPlusPlus device types. This means that the device cannot filter + * packets before they get to the user + * - It's not possible to set or change NIC load-balancing method. DPDK + * provides this capability but it's still not supported by DpdkDevice + */ +class DpdkDevice : public IDevice { + friend class DpdkDeviceList; + friend class MBufRawPacket; + + public: + /** + * An enum describing all RSS (Receive Side Scaling) hash functions supported + * in DPDK. Notice not all PMDs support all types of hash functions + */ + enum DpdkRssHashFunction { + /** No RSS */ + RSS_NONE = 0, + /** IPv4 based flow */ + RSS_IPV4 = 0x1, + /** Fragmented IPv4 based flow */ + RSS_FRAG_IPV4 = 0x2, + /** Non-fragmented IPv4 + TCP flow */ + RSS_NONFRAG_IPV4_TCP = 0x4, + /** Non-fragmented IPv4 + UDP flow */ + RSS_NONFRAG_IPV4_UDP = 0x8, + /** Non-fragmented IPv4 + SCTP flow */ + RSS_NONFRAG_IPV4_SCTP = 0x10, + /** Non-fragmented IPv4 + non TCP/UDP/SCTP flow */ + RSS_NONFRAG_IPV4_OTHER = 0x20, + /** IPv6 based flow */ + RSS_IPV6 = 0x40, + /** Fragmented IPv6 based flow */ + RSS_FRAG_IPV6 = 0x80, + /** Non-fragmented IPv6 + TCP flow */ + RSS_NONFRAG_IPV6_TCP = 0x100, + /** Non-fragmented IPv6 + UDP flow */ + RSS_NONFRAG_IPV6_UDP = 0x200, + /** Non-fragmented IPv6 + SCTP flow */ + RSS_NONFRAG_IPV6_SCTP = 0x400, + /** Non-fragmented IPv6 + non TCP/UDP/SCTP flow */ + RSS_NONFRAG_IPV6_OTHER = 0x800, + /** L2 payload based flow */ + RSS_L2_PAYLOAD = 0x1000, + /** IPv6 Ex based flow */ + RSS_IPV6_EX = 0x2000, + /** IPv6 + TCP Ex based flow */ + RSS_IPV6_TCP_EX = 0x4000, + /** IPv6 + UDP Ex based flow */ + RSS_IPV6_UDP_EX = 0x8000, + /** Consider device port number as a flow differentiator */ + RSS_PORT = 0x10000, + /** VXLAN protocol based flow */ + RSS_VXLAN = 0x20000, + /** GENEVE protocol based flow */ + RSS_GENEVE = 0x40000, + /** NVGRE protocol based flow */ + RSS_NVGRE = 0x80000, + /** All RSS functions supported by the device */ + RSS_ALL_SUPPORTED = -1, + /** A default set of RSS functions supported by the device */ + RSS_DEFAULT = PCPP_RSS_HASH_MAGIC_NUMBER + }; + + /** + * @struct DpdkDeviceConfiguration + * A struct that contains user configurable parameters for opening a + * DpdkDevice. All of these parameters have default values so the user doesn't + * have to use these parameters or understand exactly what is their effect + */ + struct DpdkDeviceConfiguration { + /** + * When configuring a DPDK RX queue, DPDK creates descriptors it will use + * for receiving packets from the network to this RX queue. This parameter + * enables to configure the number of descriptors that will be created for + * each RX queue + */ + uint16_t receiveDescriptorsNumber; + + /** + * When configuring a DPDK TX queue, DPDK creates descriptors it will use + * for transmitting packets to the network through this TX queue. This + * parameter enables to configure the number of descriptors that will be + * created for each TX queue + */ + uint16_t transmitDescriptorsNumber; + + /** + * Set the TX buffer flush timeout in millisecond (only relevant if sending + * packets using DPDK TX buffer mechanism). A value of zero means no timeout + */ + uint16_t flushTxBufferTimeout; + + /** + * When configuring a DPDK device, DPDK supports to activate the Receive + * Side Scaling (RSS) feature to distribute traffic between the RX queues + * This parameter points to an array holding the RSS key to use for hashing + * specific header fields of received packets. The length of this array + * should be indicated by rssKeyLength below. Supplying a NULL value causes + * a default random hash key to be used by the device driver + */ + uint8_t* rssKey; + + /** + * This parameter indicates the length in bytes of the array pointed by + * rssKey. This length will be checked in i40e only. Others assume 40 bytes + * to be used. + */ + uint8_t rssKeyLength; + + /** + * This parameter enables to configure the types of packets to which the RSS + * hashing must be applied. The value is a mask composed of hash functions + * described in DpdkRssHashFunction enum. Supplying a value equal to zero + * disables the RSS feature. Supplying a value equal to -1 enables all hash + * functions supported by this PMD + */ + uint64_t rssHashFunction; + + /** + * A c'tor for this struct + * @param[in] receiveDescriptorsNumber An optional parameter for defining + * the number of RX descriptors that will be allocated for each RX queue. + * Default value is 128 + * @param[in] transmitDescriptorsNumber An optional parameter for defining + * the number of TX descriptors that will be allocated for each TX queue. + * Default value is 512 + * @param[in] flushTxBufferTimeout An optional parameter for setting TX + * buffer timeout in usec. Default value is 100 usec + * @param[in] rssHashFunction This parameter enable to configure the types + * of packets to which the RSS hashing must be applied. The value provided + * here should be a mask composed of hash functions described in + * DpdkRssHashFunction enum. The default value is RSS_DEFAULT. + * @param[in] rssKey A pointer to an array holding the RSS key to use for + * hashing specific header of received packets. If not specified, there is a + * default key defined inside DpdkDevice + * @param[in] rssKeyLength The length in bytes of the array pointed by + * rssKey. Default value is the length of default rssKey + */ + explicit DpdkDeviceConfiguration(uint16_t receiveDescriptorsNumber = 128, + uint16_t transmitDescriptorsNumber = 512, + uint16_t flushTxBufferTimeout = 100, + uint64_t rssHashFunction = RSS_DEFAULT, + uint8_t* rssKey = DpdkDevice::m_RSSKey, + uint8_t rssKeyLength = 40) { + this->receiveDescriptorsNumber = receiveDescriptorsNumber; + this->transmitDescriptorsNumber = transmitDescriptorsNumber; + this->flushTxBufferTimeout = flushTxBufferTimeout; + this->rssKey = rssKey; + this->rssKeyLength = rssKeyLength; + this->rssHashFunction = rssHashFunction; + } + }; + + /** + * @struct LinkStatus + * A struct that contains the link status of a DpdkDevice (DPDK port). + * Returned from DpdkDevice#getLinkStatus() + */ + struct LinkStatus { + /** Enum for describing link duplex */ + enum LinkDuplex { + /** Full duplex */ + FULL_DUPLEX, + /** Half duplex */ + HALF_DUPLEX + }; + + /** True if link is up, false if it's down */ + bool linkUp; + /** Link speed in Mbps (for example: 10Gbe will show 10000) */ + int linkSpeedMbps; + /** Link duplex (half/full duplex) */ + LinkDuplex linkDuplex; + }; + + /** + * @struct RxTxStats + * A container for RX/TX statistics + */ + struct RxTxStats { + /** Total number of packets */ + uint64_t packets; + /** Total number of successfully received bytes */ + uint64_t bytes; + /** Packets per second */ + uint64_t packetsPerSec; + /** Bytes per second */ + uint64_t bytesPerSec; + }; + + /** + * @struct DpdkDeviceStats + * A container for DpdkDevice statistics + */ + struct DpdkDeviceStats { + /** DpdkDevice ID */ + uint8_t devId; + /** The timestamp of when the stats were written */ + timespec timestamp; + /** RX statistics per RX queue */ + RxTxStats rxStats[DPDK_MAX_RX_QUEUES]; + /** TX statistics per TX queue */ + RxTxStats txStats[DPDK_MAX_RX_QUEUES]; + /** RX statistics, aggregated for all RX queues */ + RxTxStats aggregatedRxStats; + /** TX statistics, aggregated for all TX queues */ + RxTxStats aggregatedTxStats; + /** Total number of RX packets dropped by H/W because there are no available + * buffers (i.e RX queues are full) */ + uint64_t rxPacketsDroppedByHW; + /** Total number of erroneous packets */ + uint64_t rxErroneousPackets; + /** Total number of RX mbuf allocation failures */ + uint64_t rxMbufAlocFailed; + }; + + virtual ~DpdkDevice(); + + /** + * @return The device ID (DPDK port ID) + */ + int getDeviceId() const { return m_Id; } + /** + * @return The device name which is in the format of 'DPDK_[PORT-ID]' + */ + std::string getDeviceName() const { return m_DeviceName; } + + /** + * @return The MAC address of the device (DPDK port) + */ + MacAddress getMacAddress() const { return m_MacAddress; } + + /** + * @return The name of the PMD (poll mode driver) DPDK is using for this + * device. You can read about PMDs in the DPDK documentation: + * http://dpdk.org/doc/guides/prog_guide/poll_mode_drv.html + */ + std::string getPMDName() const { return m_PMDName; } + + /** + * @return The enum type of the PMD (poll mode driver) DPDK is using for this + * device. You can read about PMDs in the DPDK documentation: + * http://dpdk.org/doc/guides/prog_guide/poll_mode_drv.html + */ + DpdkPMDType getPMDType() const { return m_PMDType; } + + /** + * @return The PCI address of the device + */ + std::string getPciAddress() const { return m_PciAddress; } + + /** + * @return The device's maximum transmission unit (MTU) in bytes + */ + uint16_t getMtu() const { return m_DeviceMtu; } + + /** + * Set a new maximum transmission unit (MTU) for this device + * @param[in] newMtu The new MTU in bytes + * @return True if MTU was set successfully, false if operation failed or if + * PMD doesn't support changing the MTU + */ + bool setMtu(uint16_t newMtu); + + /** + * @return True if this device is a virtual interface (such as VMXNET3, 1G/10G + * virtual function, etc.), false otherwise + */ + bool isVirtual() const; + + /** + * Get the link status (link up/down, link speed and link duplex) + * @param[out] linkStatus A reference to object the result shall be written to + */ + void getLinkStatus(LinkStatus& linkStatus) const; + + /** + * @return The core ID used in this context + */ + uint32_t getCurrentCoreId() const; + + /** + * @return The number of RX queues currently opened for this device (as + * configured in openMultiQueues() ) + */ + uint16_t getNumOfOpenedRxQueues() const { return m_NumOfRxQueuesOpened; } + + /** + * @return The number of TX queues currently opened for this device (as + * configured in openMultiQueues() ) + */ + uint16_t getNumOfOpenedTxQueues() const { return m_NumOfTxQueuesOpened; } + + /** + * @return The total number of RX queues available on this device + */ + uint16_t getTotalNumOfRxQueues() const { return m_TotalAvailableRxQueues; } + + /** + * @return The total number of TX queues available on this device + */ + uint16_t getTotalNumOfTxQueues() const { return m_TotalAvailableTxQueues; } + + /** + * Receive raw packets from the network + * @param[out] rawPacketsArr A vector where all received packets will be + * written into + * @param[in] rxQueueId The RX queue to receive packets from + * @return The number of packets received. If an error occurred 0 will be + * returned and the error will be printed to log + */ + uint16_t receivePackets(MBufRawPacketVector& rawPacketsArr, + uint16_t rxQueueId) const; + + /** + * Receive raw packets from the network. Please notice that in terms of + * performance, this is the best method to use for receiving packets because + * out of all receivePackets overloads this method requires the least overhead + * and is almost as efficient as receiving packets directly through DPDK. So + * if performance is a critical factor in your application, please use this + * method + * @param[out] rawPacketsArr A pointer to an array of MBufRawPacket pointers + * where all received packets will be written into. The array is expected to + * be allocated by the user and its length should be provided in + * rawPacketArrLength. Number of packets received will be returned. Notice + * it's the user responsibility to free the array and its content when done + * using it + * @param[out] rawPacketArrLength The length of MBufRawPacket pointers array + * @param[in] rxQueueId The RX queue to receive packets from + * @return The number of packets received. If an error occurred 0 will be + * returned and the error will be printed to log + */ + uint16_t receivePackets(MBufRawPacket** rawPacketsArr, + uint16_t rawPacketArrLength, + uint16_t rxQueueId) const; + + /** + * Receive parsed packets from the network + * @param[out] packetsArr A pointer to an allocated array of Packet pointers + * where all received packets will be written into. The array is expected to + * be allocated by the user and its length should be provided in + * packetsArrLength. Number of packets received will be returned. Notice it's + * the user responsibility to free the array and its content when done using + * it + * @param[out] packetsArrLength The length of Packet pointers array + * @param[in] rxQueueId The RX queue to receive packets from + * @return The number of packets received. If an error occurred 0 will be + * returned and the error will be printed to log + */ + uint16_t receivePackets(Packet** packetsArr, uint16_t packetsArrLength, + uint16_t rxQueueId) const; + + /** + * Send an array of MBufRawPacket to the network. Please notice the + * following:
+ * - In terms of performance, this is the best method to use for sending + * packets because out of all sendPackets overloads this method requires the + * least overhead and is almost as efficient as sending the packets directly + * through DPDK. So if performance is a critical factor in your application, + * please use this method + * - If the number of packets to send is higher than 64 this method will run + * multiple iterations of sending packets to DPDK, each iteration of 64 + * packets + * - If the number of packets to send is higher than a threshold of 80% of + * total TX descriptors (which is typically around 400 packets), then after + * reaching this threshold there is a built-in 0.2 sec sleep to let the TX + * descriptors clean + * - The mbufs used in this method aren't freed by this method, they will be + * transparently freed by DPDK

+ * @param[in] rawPacketsArr A pointer to an array of MBufRawPacket + * @param[in] arrLength The length of the array + * @param[in] txQueueId An optional parameter which indicates to which TX + * queue the packets will be sent to. The default is TX queue 0 + * @param[in] useTxBuffer A flag which indicates whether to use TX buffer + * mechanism or not. To read more about DPDK's TX buffer mechanism please + * refer to DpdkDevice class description. Default value is false (don't use + * this mechanism) + * @return The number of packets actually and successfully sent. If device is + * not opened or TX queue isn't open, 0 will be returned. Also, if TX buffer + * is being used and packets are buffered, some or all may not be actually + * sent + */ + uint16_t sendPackets(MBufRawPacket** rawPacketsArr, uint16_t arrLength, + uint16_t txQueueId = 0, bool useTxBuffer = false); + + /** + * Send an array of parsed packets to the network. Please notice the + * following:
+ * - If some or all of the packets contain raw packets which aren't of type + * MBufRawPacket, a new temp MBufRawPacket instances will be created and + * packet data will be copied to them. This is necessary to allocate mbufs + * which will store the data to be sent. If performance is a critical factor + * please make sure you send parsed packets that contain only raw packets of + * type MBufRawPacket + * - If the number of packets to send is higher than 64 this method will run + * multiple iterations of sending packets to DPDK, each iteration of 64 + * packets + * - If the number of packets to send is higher than a threshold of 80% of + * total TX descriptors (which is typically around 400 packets), then after + * reaching this threshold there is a built-in 0.2 sec sleep to let the TX + * descriptors clean + * - The mbufs used or allocated in this method aren't freed by this method, + * they will be transparently freed by DPDK

+ * @param[in] packetsArr A pointer to an array of parsed packet pointers + * @param[in] arrLength The length of the array + * @param[in] txQueueId An optional parameter which indicates to which TX + * queue the packets will be sent to. The default is TX queue 0 + * @param[in] useTxBuffer A flag which indicates whether to use TX buffer + * mechanism or not. To read more about DPDK's TX buffer mechanism please + * refer to DpdkDevice class description. Default value is false (don't use + * this mechanism) + * @return The number of packets actually and successfully sent. If device is + * not opened or TX queue isn't open, 0 will be returned. Also, if TX buffer + * is being used and packets are buffered, some or all may not be actually + * sent + */ + uint16_t sendPackets(Packet** packetsArr, uint16_t arrLength, + uint16_t txQueueId = 0, bool useTxBuffer = false); + + /** + * Send a vector of MBufRawPacket pointers to the network. Please notice the + * following:
+ * - If the number of packets to send is higher than 64 this method will run + * multiple iterations of sending packets to DPDK, each iteration of 64 + * packets + * - If the number of packets to send is higher than a threshold of 80% of + * total TX descriptors (which is typically around 400 packets), then after + * reaching this threshold there is a built-in 0.2 sec sleep to let the TX + * descriptors clean + * - The mbufs used in this method aren't freed by this method, they will be + * transparently freed by DPDK

+ * @param[in] rawPacketsVec The vector of raw packet + * @param[in] txQueueId An optional parameter which indicates to which TX + * queue the packets will be sent to. The default is TX queue 0 + * @param[in] useTxBuffer A flag which indicates whether to use TX buffer + * mechanism or not. To read more about DPDK's TX buffer mechanism please + * refer to DpdkDevice class description. Default value is false (don't use + * this mechanism) + * @return The number of packets actually and successfully sent. If device is + * not opened or TX queue isn't open, 0 will be returned. Also, if TX buffer + * is being used and packets are buffered, some or all may not be actually + * sent + */ + uint16_t sendPackets(MBufRawPacketVector& rawPacketsVec, + uint16_t txQueueId = 0, bool useTxBuffer = false); + + /** + * Send a vector of RawPacket pointers to the network. Please notice the + * following:
+ * - If some or all of the raw packets aren't of type MBufRawPacket, a new + * temp MBufRawPacket instances will be created and packet data will be copied + * to them. This is necessary to allocate mbufs which will store the data to + * be sent. If performance is a critical factor please make sure you send only + * raw packets of type MBufRawPacket (or use the sendPackets overload that + * sends MBufRawPacketVector) + * - If the number of packets to send is higher than 64 this method will run + * multiple iterations of sending packets to DPDK, each iteration of 64 + * packets + * - If the number of packets to send is higher than a threshold of 80% of + * total TX descriptors (which is typically around 400 packets), then after + * reaching this threshold there is a built-in 0.2 sec sleep to let the TX + * descriptors clean + * - The mbufs used or allocated in this method aren't freed by this method, + * they will be transparently freed by DPDK

+ * @param[in] rawPacketsVec The vector of raw packet + * @param[in] txQueueId An optional parameter which indicates to which TX + * queue the packets will be sent to. The default is TX queue 0 + * @param[in] useTxBuffer A flag which indicates whether to use TX buffer + * mechanism or not. To read more about DPDK's TX buffer mechanism please + * refer to DpdkDevice class description. Default value is false (don't use + * this mechanism) + * @return The number of packets actually and successfully sent. If device is + * not opened or TX queue isn't open, 0 will be returned. Also, if TX buffer + * is being used and packets are buffered, some or all may not be actually + * sent + */ + uint16_t sendPackets(RawPacketVector& rawPacketsVec, uint16_t txQueueId = 0, + bool useTxBuffer = false); + + /** + * Send a raw packet to the network. Please notice that if the raw packet + * isn't of type MBufRawPacket, a new temp MBufRawPacket will be created and + * the data will be copied to it. This is necessary to allocate an mbuf which + * will store the data to be sent. If performance is a critical factor please + * make sure you send a raw packet of type MBufRawPacket. Please also notice + * that the mbuf used or allocated in this method isn't freed by this method, + * it will be transparently freed by DPDK + * @param[in] rawPacket The raw packet to send + * @param[in] txQueueId An optional parameter which indicates to which TX + * queue the packet will be sent to. The default is TX queue 0 + * @param[in] useTxBuffer A flag which indicates whether to use TX buffer + * mechanism or not. To read more about DPDK's TX buffer mechanism please + * refer to DpdkDevice class description. Default value is false (don't use + * this mechanism) + * @return True if packet was sent successfully or false if device is not + * opened, TX queue isn't opened, or if the packet wasn't sent for any other + * reason. Please notice that when using TX buffers the packet may be buffered + * and not sent immediately, which may also result in returning false + */ + bool sendPacket(RawPacket& rawPacket, uint16_t txQueueId = 0, + bool useTxBuffer = false); + + /** + * Send a MBufRawPacket to the network. Please notice that the mbuf used in + * this method isn't freed by this method, it will be transparently freed by + * DPDK + * @param[in] rawPacket The MBufRawPacket to send + * @param[in] txQueueId An optional parameter which indicates to which TX + * queue the packet will be sent to. The default is TX queue 0 + * @param[in] useTxBuffer A flag which indicates whether to use TX buffer + * mechanism or not. To read more about DPDK's TX buffer mechanism please + * refer to DpdkDevice class description. Default value is false (don't use + * this mechanism) + * @return True if packet was sent successfully or false if device is not + * opened, TX queue isn't opened, or if the packet wasn't sent for any other + * reason. Please notice that when using TX buffers the packet may be buffered + * and not sent immediately, which may also result in returning false + */ + bool sendPacket(MBufRawPacket& rawPacket, uint16_t txQueueId = 0, + bool useTxBuffer = false); + + /** + * Send a parsed packet to the network. Please notice that the mbuf used or + * allocated in this method isn't freed by this method, it will be + * transparently freed by DPDK + * @param[in] packet The parsed packet to send. Please notice that if the + * packet contains a raw packet which isn't of type MBufRawPacket, a new temp + * MBufRawPacket will be created and the data will be copied to it. This is + * necessary to allocate an mbuf which will store the data to be sent. If + * performance is a critical factor please make sure you send a parsed packet + * that contains a raw packet of type MBufRawPacket + * @param[in] txQueueId An optional parameter which indicates to which TX + * queue the packet will be sent on. The default is TX queue 0 + * @param[in] useTxBuffer A flag which indicates whether to use TX buffer + * mechanism or not. To read more about DPDK's TX buffer mechanism please + * refer to DpdkDevice class description. Default value is false (don't use + * this mechanism) + * @return True if packet was sent successfully or false if device is not + * opened, TX queue isn't opened, or if the packet wasn't sent for any other + * reason. Please notice that when using TX buffers the packet may be buffered + * and not sent immediately, which may also result in returning false + */ + bool sendPacket(Packet& packet, uint16_t txQueueId = 0, + bool useTxBuffer = false); + + /** + * Overridden method from IPcapDevice. __BPF filters are currently not + * implemented for DpdkDevice__ + * @param[in] filter Not used in this method + * @return Always false with a "Filters aren't supported in DPDK device" error + * message + */ + bool setFilter(GeneralFilter& filter); + + /** + * Overridden method from IPcapDevice. __BPF filters are currently not + * implemented for DpdkDevice__ + * @param[in] filterAsString Not used in this method + * @return Always false with a "Filters aren't supported in DPDK device" error + * message + */ + bool setFilter(std::string filterAsString); + + /** + * Open the DPDK device. Notice opening the device only makes it ready to use, + * it doesn't start packet capturing. This method initializes RX and TX + * queues, configures the DPDK port and starts it. Call close() to close the + * device. The device is opened in promiscuous mode + * @param[in] numOfRxQueuesToOpen Number of RX queues to setup. This number + * must be smaller or equal to the return value of getTotalNumOfRxQueues() + * @param[in] numOfTxQueuesToOpen Number of TX queues to setup. This number + * must be smaller or equal to the return value of getTotalNumOfTxQueues() + * @param[in] config Optional parameter for defining special port + * configuration parameters such as number of receive/transmit descriptors. If + * not set the default parameters will be set (see DpdkDeviceConfiguration) + * @return True if the device was opened successfully, false if device is + * already opened, if RX/TX queues configuration failed or of DPDK port + * configuration and startup failed + */ + bool openMultiQueues( + uint16_t numOfRxQueuesToOpen, uint16_t numOfTxQueuesToOpen, + const DpdkDeviceConfiguration& config = DpdkDeviceConfiguration()); + + /** + * There are two ways to capture packets using DpdkDevice: one of them is + * using worker threads (see DpdkDeviceList#startDpdkWorkerThreads() ) and the + * other way is setting a callback which is invoked each time a burst of + * packets is captured. This method implements the second way. After invoking + * this method the DpdkDevice enters capture mode and starts capturing + * packets. This method assumes there is only 1 RX queue opened for this + * device, otherwise an error is returned. It then allocates a core and + * creates 1 thread that runs in an endless loop and tries to capture packets + * using DPDK. Each time a burst of packets is captured the user callback is + * invoked with the user cookie as a parameter. This loop continues until + * stopCapture() is called. Notice: since the callback is invoked for every + * packet burst using this method can be slower than using worker threads. On + * the other hand, it's a simpler way comparing to worker threads + * @param[in] onPacketsArrive The user callback which will be invoked each + * time a packet burst is captured by the device + * @param[in] onPacketsArriveUserCookie The user callback is invoked with this + * cookie as a parameter. It can be used to pass information from the user + * application to the callback + * @return True if capture thread started successfully or false if device is + * already in capture mode, number of opened RX queues isn't equal to 1, if + * the method couldn't find an available core to allocate for the capture + * thread, or if thread invocation failed. In all of these cases an + * appropriate error message will be printed + */ + bool startCaptureSingleThread(OnDpdkPacketsArriveCallback onPacketsArrive, + void* onPacketsArriveUserCookie); + + /** + * This method does exactly what startCaptureSingleThread() does, but with + * more than one RX queue / capturing thread. It's called with a core mask as + * a parameter and creates a packet capture thread on every core. Each + * capturing thread is assigned with a specific RX queue. This method assumes + * all cores in the core-mask are available and there are enough opened RX + * queues to match for each thread. If these assumptions are not true an error + * is returned. After invoking all threads, all of them run in an endless loop + * and try to capture packets from their designated RX queues. Each time a + * burst of packets is captured the callback is invoked with the user cookie + * and the thread ID that captured the packets + * @param[in] onPacketsArrive The user callback which will be invoked each + * time a burst of packets is captured by the device + * @param[in] onPacketsArriveUserCookie The user callback is invoked with this + * cookie as a parameter. It can be used to pass information from the user + * application to the callback + * @param coreMask The core-mask for creating the capture threads + * @return True if all capture threads started successfully or false if device + * is already in capture mode, not all cores in the core-mask are available to + * DPDK, there are not enough opened RX queues to match all cores in the + * core-mask, or if thread invocation failed. In all of these cases an + * appropriate error message will be printed + */ + bool startCaptureMultiThreads(OnDpdkPacketsArriveCallback onPacketsArrive, + void* onPacketsArriveUserCookie, + CoreMask coreMask); + + /** + * If device is in capture mode started by invoking startCaptureSingleThread() + * or startCaptureMultiThreads(), this method will stop all capturing threads + * and set the device to non-capturing mode + */ + void stopCapture(); + + /** + * @return The number of free mbufs in device's mbufs pool + */ + int getAmountOfFreeMbufs() const; + + /** + * @return The number of mbufs currently in use in device's mbufs pool + */ + int getAmountOfMbufsInUse() const; + + /** + * Retrieve RX/TX statistics from device + * @param[out] stats A reference to a DpdkDeviceStats object where stats will + * be written into + */ + void getStatistics(DpdkDeviceStats& stats) const; + + /** + * Clear device statistics + */ + void clearStatistics(); + + /** + * DPDK supports an option to buffer TX packets and send them only when + * reaching a certain threshold. This method enables the user to flush a TX + * buffer for certain TX queue and send the packets stored in it (you can read + * about it here: + * http://dpdk.org/doc/api/rte__ethdev_8h.html#a0e941a74ae1b1b886764bc282458d946). + * It has the option to flush only when timeout that was set in + * DpdkDeviceConfiguration#flushTxBufferTimeout expired or flush immediately + * regardless of the timeout. The usage of this method can be in the main loop + * where you can call this method once every a couple of iterations to make + * sure TX buffers are flushed + * @param[in] flushOnlyIfTimeoutExpired When set to true, flush will happen + * only if the timeout defined in DpdkDeviceConfiguration#flushTxBufferTimeout + * expired. If set to false flush will happen immediately. Default value is + * false + * @param[in] txQueueId The TX queue ID to flush its buffer. Default is 0 + * @return The number of packets sent after buffer was flushed + */ + uint16_t flushTxBuffer(bool flushOnlyIfTimeoutExpired = false, + uint16_t txQueueId = 0); + + /** + * Check whether a specific RSS hash function is supported by this device + * (PMD) + * @param[in] rssHF RSS hash function to check + * @return True if this hash function is supported, false otherwise + */ + bool isDeviceSupportRssHashFunction(DpdkRssHashFunction rssHF) const; + + /** + * Check whether a mask of RSS hash functions is supported by this device + * (PMD) + * @param[in] rssHFMask RSS hash functions mask to check. This mask should be + * built from values in DpdkRssHashFunction enum + * @return True if all hash functions in this mask are supported, false + * otherwise + */ + bool isDeviceSupportRssHashFunction(uint64_t rssHFMask) const; + + /** + * @return A mask of all RSS hash functions supported by this device (PMD). + * This mask is built from values in DpdkRssHashFunction enum. Value of zero + * means RSS is not supported by this device + */ + uint64_t getSupportedRssHashFunctions() const; + + /** + * @return The RSS hash function mask configured for this device (PMD) + */ + uint64_t getConfiguredRssHashFunction() const; + + /** + * Translate RSS hash function mask to a list of their string representation + * @param rssHFMask RSS hash function mask + * @return RSS hash functions as strings + */ + std::vector + rssHashFunctionMaskToString(uint64_t rssHFMask) const; + + // overridden methods + + /** + * Overridden method from IPcapDevice. It calls openMultiQueues() with 1 RX + * queue and 1 TX queue. Notice opening the device only makes it ready to use, + * it doesn't start packet capturing. The device is opened in promiscuous mode + * @return True if the device was opened successfully, false if device is + * already opened, if RX/TX queues configuration failed or of DPDK port + * configuration and startup failed + */ + bool open() { return openMultiQueues(1, 1); }; + + /** + * Close the DpdkDevice. When device is closed it's not possible work with it + */ + void close(); + + private: + struct DpdkCoreConfiguration { + int RxQueueId; + bool IsCoreInUse; + + void clear() { + RxQueueId = -1; + IsCoreInUse = false; + } + + DpdkCoreConfiguration() : RxQueueId(-1), IsCoreInUse(false) {} + }; + + DpdkDevice(int port, uint32_t mBufPoolSize); + bool initMemPool(struct rte_mempool*& memPool, const char* mempoolName, + uint32_t mBufPoolSize); + + bool configurePort(uint8_t numOfRxQueues, uint8_t numOfTxQueues); + bool initQueues(uint8_t numOfRxQueuesToInit, uint8_t numOfTxQueuesToInit); + bool startDevice(); + + static int dpdkCaptureThreadMain(void* ptr); + + void clearCoreConfiguration(); + bool initCoreConfigurationByCoreMask(CoreMask coreMask); + int getCoresInUseCount() const; + + void setDeviceInfo(); + + typedef rte_mbuf* (*PacketIterator)(void* packetStorage, int index); + uint16_t sendPacketsInner(uint16_t txQueueId, void* packetStorage, + PacketIterator iter, int arrLength, + bool useTxBuffer); + + uint64_t convertRssHfToDpdkRssHf(uint64_t rssHF) const; + uint64_t convertDpdkRssHfToRssHf(uint64_t dpdkRssHF) const; + + std::string m_DeviceName; + DpdkPMDType m_PMDType; + std::string m_PMDName; + std::string m_PciAddress; + + DpdkDeviceConfiguration m_Config; + + int m_Id; + MacAddress m_MacAddress; + uint16_t m_DeviceMtu; + struct rte_mempool* m_MBufMempool; + struct rte_eth_dev_tx_buffer** m_TxBuffers; + uint64_t m_TxBufferDrainTsc; + uint64_t* m_TxBufferLastDrainTsc; + DpdkCoreConfiguration m_CoreConfiguration[MAX_NUM_OF_CORES]; + uint16_t m_TotalAvailableRxQueues; + uint16_t m_TotalAvailableTxQueues; + uint16_t m_NumOfRxQueuesOpened; + uint16_t m_NumOfTxQueuesOpened; + OnDpdkPacketsArriveCallback m_OnPacketsArriveCallback; + void* m_OnPacketsArriveUserCookie; + bool m_StopThread; + + bool m_WasOpened; + + // RSS key used by the NIC for load balancing the packets between cores + static uint8_t m_RSSKey[40]; + + mutable DpdkDeviceStats m_PrevStats; +}; } // namespace pcpp diff --git a/Pcap++/header/DpdkDeviceList.h b/Pcap++/header/DpdkDeviceList.h index e7e441cd0a..3aa549930f 100644 --- a/Pcap++/header/DpdkDeviceList.h +++ b/Pcap++/header/DpdkDeviceList.h @@ -3,203 +3,242 @@ // GCOVR_EXCL_START -#include "SystemUtils.h" #include "DpdkDevice.h" #include "Logger.h" +#include "SystemUtils.h" #include /** * @file - * For details about PcapPlusPlus support for DPDK see DpdkDevice.h file description + * For details about PcapPlusPlus support for DPDK see DpdkDevice.h file + * description + */ + +/** + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib */ +namespace pcpp { /** -* \namespace pcpp -* \brief The main namespace for the PcapPlusPlus lib -*/ -namespace pcpp -{ - - /** - * @class DpdkWorkerThread - * There are two ways to capture packets using DpdkDevice: one of them is using worker threads and the other way is using - * a callback which is invoked on each a burst of packets are captured (see DpdkDevice#startCaptureSingleThread() ). This class - * is a base class for implementing workers. A worker is basically a class that is activated by DpdkDeviceList#startDpdkWorkerThreads() - * and runs on a designated core. When it runs it can do whatever the user wants it to do. The most common use it running in an - * endless loop and receive, analyze and send packets using one or more DpdkDevice instances. It can do all kinds of processing for - * these packets. The only restriction for a worker class is that it must implement the 3 abstract methods stated in this class-interface - * for start running, stop running and get the core ID the worker is running on. - */ - class DpdkWorkerThread - { - public: - /** - * A virtual d'tor. Can be overridden by child class if needed - */ - virtual ~DpdkWorkerThread() {} - - /** - * An abstract method that must be implemented by child class. It's the indication for the worker to start running - * @param[in] coreId The core ID the worker is running on (should be returned in getCoreId() ) - * @return True if all went well or false otherwise - */ - virtual bool run(uint32_t coreId) = 0; - - /** - * An abstract method that must be implemented by child class. It's the indication for the worker to stop running. After - * this method is called the caller expects the worker to stop running as fast as possible - */ - virtual void stop() = 0; - - /** - * An abstract method that must be implemented by child class. Get the core ID the worker is running on (as sent to the run() method - * as a parameter) - * @return The core ID the worker is running on - */ - virtual uint32_t getCoreId() const = 0; - }; - - class KniDeviceList; - - /** - * @class DpdkDeviceList - * A singleton class that encapsulates DPDK initialization and holds the list of DpdkDevice instances. As it's a singleton, it has only - * one active instance doesn't have a public c'tor. This class has several main uses: - * - it contains the initDpdk() static method which initializes the DPDK infrastructure. It should be called once in every application at - * its startup process - * - it contains the list of DpdkDevice instances and enables access to them - * - it has methods to start and stop worker threads. See more details in startDpdkWorkerThreads() - */ - class DpdkDeviceList - { - friend class KniDeviceList; - private: - bool m_IsInitialized; - static bool m_IsDpdkInitialized; - static uint32_t m_MBufPoolSizePerDevice; - static CoreMask m_CoreMask; - std::vector m_DpdkDeviceList; - std::vector m_WorkerThreads; - - DpdkDeviceList(); - - bool isInitialized() const { return (m_IsInitialized && m_IsDpdkInitialized); } - bool initDpdkDevices(uint32_t mBufPoolSizePerDevice); - static bool verifyHugePagesAndDpdkDriver(); - - static int dpdkWorkerThreadStart(void* ptr); - public: - - ~DpdkDeviceList(); - - /** - * As DpdkDeviceList is a singleton, this is the static getter to retrieve its instance. Note that if the static method - * initDpdk() was not called or returned false this instance won't be initialized and DpdkDevices won't be initialized either - * @return The singleton instance of DpdkDeviceList - */ - static DpdkDeviceList& getInstance() - { - static DpdkDeviceList instance; - if (!instance.isInitialized()) - instance.initDpdkDevices(DpdkDeviceList::m_MBufPoolSizePerDevice); - - return instance; - } - - /** - * A static method that has to be called once at the startup of every application that uses DPDK. It does several things: - * - verifies huge-pages are set and DPDK kernel module is loaded (these are set by the setup_dpdk.py external script that - * has to be run before application is started) - * - initializes the DPDK infrastructure - * - creates DpdkDevice instances for all ports available for DPDK - * - * @param[in] coreMask The cores to initialize DPDK with. After initialization, DPDK will only be able to use these cores - * for its work. The core mask should have a bit set for every core to use. For example: if the user want to use cores 1,2 - * the core mask should be 6 (binary: 110) - * @param[in] mBufPoolSizePerDevice The mbuf pool size each DpdkDevice will have. This has to be a number which is a power of 2 - * minus 1, for example: 1023 (= 2^10-1) or 4,294,967,295 (= 2^32-1), etc. This is a DPDK limitation, not PcapPlusPlus. - * The size of the mbuf pool size dictates how many packets can be handled by the application at the same time. For example: if - * pool size is 1023 it means that no more than 1023 packets can be handled or stored in application memory at every point in time - * @param[in] masterCore The core DPDK will use as master to control all worker thread. The default, unless set otherwise, is 0 - * @param[in] initDpdkArgc Number of optional arguments - * @param[in] initDpdkArgv Optional arguments - * @param[in] appName program name to be provided for the DPDK - * @return True if initialization succeeded or false if huge-pages or DPDK kernel driver are not loaded, if mBufPoolSizePerDevice - * isn't power of 2 minus 1, if DPDK infra initialization failed or if DpdkDevice initialization failed. Anyway, if this method - * returned false it's impossible to use DPDK with PcapPlusPlus. You can get some more details about mbufs and pools in - * DpdkDevice.h file description or in DPDK web site - */ - static bool initDpdk(CoreMask coreMask, uint32_t mBufPoolSizePerDevice, uint8_t masterCore = 0, uint32_t initDpdkArgc = 0, char **initDpdkArgv = NULL, const std::string& appName = "pcapplusplusapp"); - - /** - * Get a DpdkDevice by port ID - * @param[in] portId The port ID - * @return A pointer to the DpdkDevice or NULL if no such device is found - */ - DpdkDevice* getDeviceByPort(int portId) const; - - /** - * Get a DpdkDevice by port PCI address - * @param[in] pciAddr The port PCI address - * @return A pointer to the DpdkDevice or NULL if no such device is found - */ - DpdkDevice* getDeviceByPciAddress(const std::string& pciAddr) const; - - /** - * @return A vector of all DpdkDevice instances - */ - const std::vector& getDpdkDeviceList() const { return m_DpdkDeviceList; } - - /** - * @return DPDK master core which is the core that initializes the application - */ - SystemCore getDpdkMasterCore() const; - - /** - * Change the log level of all modules of DPDK - * @param[in] logLevel The log level to set. Logger#Info is RTE_LOG_NOTICE and Logger#Debug is RTE_LOG_DEBUG - */ - void setDpdkLogLevel(Logger::LogLevel logLevel); - - /** - * @return The current DPDK log level. RTE_LOG_NOTICE and lower are considered as Logger#Info. RTE_LOG_INFO or RTE_LOG_DEBUG - * are considered as Logger#Debug - */ - Logger::LogLevel getDpdkLogLevel() const; - - /** - * Order DPDK to write all its logs to a file - * @param[in] logFile The file to write to - * @return True if action succeeded, false otherwise - */ - bool writeDpdkLogToFile(FILE* logFile); - - /** - * There are two ways to capture packets using DpdkDevice: one of them is using worker threads and the other way is setting - * a callback which is invoked each time a burst of packets is captured (see DpdkDevice#startCaptureSingleThread() ). This - * method implements the first way. See a detailed description of workers in DpdkWorkerThread class description. This method - * gets a vector of workers (classes that implement the DpdkWorkerThread interface) and a core mask and starts a worker thread - * on each core (meaning - call the worker's DpdkWorkerThread#run() method). Workers usually run in an endless loop and will - * be ordered to stop by calling stopDpdkWorkerThreads().
- * Note that number of cores in the core mask must be equal to the number of workers. In addition it's impossible to run a - * worker thread on DPDK master core, so the core mask shouldn't include the master core (you can find the master core by - * calling getDpdkMasterCore() ). - * @param[in] coreMask The bitmask of cores to run worker threads on. This list shouldn't include DPDK master core - * @param[in] workerThreadsVec A vector of worker instances to run (classes who implement the DpdkWorkerThread interface). - * Number of workers in this vector must be equal to the number of cores in the core mask. Notice that the instances of - * DpdkWorkerThread shouldn't be freed until calling stopDpdkWorkerThreads() as these instances are running - * @return True if all worker threads started successfully or false if: DPDK isn't initialized (initDpdk() wasn't called or - * returned false), number of cores differs from number of workers, core mask includes DPDK master core or if one of the - * worker threads couldn't be run - */ - bool startDpdkWorkerThreads(CoreMask coreMask, std::vector& workerThreadsVec); - - /** - * Assuming worker threads are running, this method orders them to stop by calling DpdkWorkerThread#stop(). Then it waits until - * they stop running - */ - void stopDpdkWorkerThreads(); - }; + * @class DpdkWorkerThread + * There are two ways to capture packets using DpdkDevice: one of them is using + * worker threads and the other way is using a callback which is invoked on each + * a burst of packets are captured (see DpdkDevice#startCaptureSingleThread() ). + * This class is a base class for implementing workers. A worker is basically a + * class that is activated by DpdkDeviceList#startDpdkWorkerThreads() and runs + * on a designated core. When it runs it can do whatever the user wants it to + * do. The most common use it running in an endless loop and receive, analyze + * and send packets using one or more DpdkDevice instances. It can do all kinds + * of processing for these packets. The only restriction for a worker class is + * that it must implement the 3 abstract methods stated in this class-interface + * for start running, stop running and get the core ID the worker is running on. + */ +class DpdkWorkerThread { + public: + /** + * A virtual d'tor. Can be overridden by child class if needed + */ + virtual ~DpdkWorkerThread() {} + + /** + * An abstract method that must be implemented by child class. It's the + * indication for the worker to start running + * @param[in] coreId The core ID the worker is running on (should be returned + * in getCoreId() ) + * @return True if all went well or false otherwise + */ + virtual bool run(uint32_t coreId) = 0; + + /** + * An abstract method that must be implemented by child class. It's the + * indication for the worker to stop running. After this method is called the + * caller expects the worker to stop running as fast as possible + */ + virtual void stop() = 0; + + /** + * An abstract method that must be implemented by child class. Get the core ID + * the worker is running on (as sent to the run() method as a parameter) + * @return The core ID the worker is running on + */ + virtual uint32_t getCoreId() const = 0; +}; + +class KniDeviceList; + +/** + * @class DpdkDeviceList + * A singleton class that encapsulates DPDK initialization and holds the list of + * DpdkDevice instances. As it's a singleton, it has only one active instance + * doesn't have a public c'tor. This class has several main uses: + * - it contains the initDpdk() static method which initializes the DPDK + * infrastructure. It should be called once in every application at its startup + * process + * - it contains the list of DpdkDevice instances and enables access to them + * - it has methods to start and stop worker threads. See more details in + * startDpdkWorkerThreads() + */ +class DpdkDeviceList { + friend class KniDeviceList; + + private: + bool m_IsInitialized; + static bool m_IsDpdkInitialized; + static uint32_t m_MBufPoolSizePerDevice; + static CoreMask m_CoreMask; + std::vector m_DpdkDeviceList; + std::vector m_WorkerThreads; + + DpdkDeviceList(); + + bool isInitialized() const { + return (m_IsInitialized && m_IsDpdkInitialized); + } + bool initDpdkDevices(uint32_t mBufPoolSizePerDevice); + static bool verifyHugePagesAndDpdkDriver(); + + static int dpdkWorkerThreadStart(void* ptr); + + public: + ~DpdkDeviceList(); + + /** + * As DpdkDeviceList is a singleton, this is the static getter to retrieve its + * instance. Note that if the static method initDpdk() was not called or + * returned false this instance won't be initialized and DpdkDevices won't be + * initialized either + * @return The singleton instance of DpdkDeviceList + */ + static DpdkDeviceList& getInstance() { + static DpdkDeviceList instance; + if (!instance.isInitialized()) + instance.initDpdkDevices(DpdkDeviceList::m_MBufPoolSizePerDevice); + + return instance; + } + + /** + * A static method that has to be called once at the startup of every + * application that uses DPDK. It does several things: + * - verifies huge-pages are set and DPDK kernel module is loaded (these + * are set by the setup_dpdk.py external script that has to be run before + * application is started) + * - initializes the DPDK infrastructure + * - creates DpdkDevice instances for all ports available for DPDK + * + * @param[in] coreMask The cores to initialize DPDK with. After + * initialization, DPDK will only be able to use these cores for its work. The + * core mask should have a bit set for every core to use. For example: if the + * user want to use cores 1,2 the core mask should be 6 (binary: 110) + * @param[in] mBufPoolSizePerDevice The mbuf pool size each DpdkDevice will + * have. This has to be a number which is a power of 2 minus 1, for example: + * 1023 (= 2^10-1) or 4,294,967,295 (= 2^32-1), etc. This is a DPDK + * limitation, not PcapPlusPlus. The size of the mbuf pool size dictates how + * many packets can be handled by the application at the same time. For + * example: if pool size is 1023 it means that no more than 1023 packets can + * be handled or stored in application memory at every point in time + * @param[in] masterCore The core DPDK will use as master to control all + * worker thread. The default, unless set otherwise, is 0 + * @param[in] initDpdkArgc Number of optional arguments + * @param[in] initDpdkArgv Optional arguments + * @param[in] appName program name to be provided for the DPDK + * @return True if initialization succeeded or false if huge-pages or DPDK + * kernel driver are not loaded, if mBufPoolSizePerDevice isn't power of 2 + * minus 1, if DPDK infra initialization failed or if DpdkDevice + * initialization failed. Anyway, if this method returned false it's + * impossible to use DPDK with PcapPlusPlus. You can get some more details + * about mbufs and pools in DpdkDevice.h file description or in DPDK web site + */ + static bool initDpdk(CoreMask coreMask, uint32_t mBufPoolSizePerDevice, + uint8_t masterCore = 0, uint32_t initDpdkArgc = 0, + char** initDpdkArgv = NULL, + const std::string& appName = "pcapplusplusapp"); + + /** + * Get a DpdkDevice by port ID + * @param[in] portId The port ID + * @return A pointer to the DpdkDevice or NULL if no such device is found + */ + DpdkDevice* getDeviceByPort(int portId) const; + + /** + * Get a DpdkDevice by port PCI address + * @param[in] pciAddr The port PCI address + * @return A pointer to the DpdkDevice or NULL if no such device is found + */ + DpdkDevice* getDeviceByPciAddress(const std::string& pciAddr) const; + + /** + * @return A vector of all DpdkDevice instances + */ + const std::vector& getDpdkDeviceList() const { + return m_DpdkDeviceList; + } + + /** + * @return DPDK master core which is the core that initializes the application + */ + SystemCore getDpdkMasterCore() const; + + /** + * Change the log level of all modules of DPDK + * @param[in] logLevel The log level to set. Logger#Info is RTE_LOG_NOTICE and + * Logger#Debug is RTE_LOG_DEBUG + */ + void setDpdkLogLevel(Logger::LogLevel logLevel); + + /** + * @return The current DPDK log level. RTE_LOG_NOTICE and lower are considered + * as Logger#Info. RTE_LOG_INFO or RTE_LOG_DEBUG are considered as + * Logger#Debug + */ + Logger::LogLevel getDpdkLogLevel() const; + + /** + * Order DPDK to write all its logs to a file + * @param[in] logFile The file to write to + * @return True if action succeeded, false otherwise + */ + bool writeDpdkLogToFile(FILE* logFile); + + /** + * There are two ways to capture packets using DpdkDevice: one of them is + * using worker threads and the other way is setting a callback which is + * invoked each time a burst of packets is captured (see + * DpdkDevice#startCaptureSingleThread() ). This method implements the first + * way. See a detailed description of workers in DpdkWorkerThread class + * description. This method gets a vector of workers (classes that implement + * the DpdkWorkerThread interface) and a core mask and starts a worker thread + * on each core (meaning - call the worker's DpdkWorkerThread#run() method). + * Workers usually run in an endless loop and will be ordered to stop by + * calling stopDpdkWorkerThreads().
Note that number of cores in the core + * mask must be equal to the number of workers. In addition it's impossible to + * run a worker thread on DPDK master core, so the core mask shouldn't include + * the master core (you can find the master core by calling + * getDpdkMasterCore() ). + * @param[in] coreMask The bitmask of cores to run worker threads on. This + * list shouldn't include DPDK master core + * @param[in] workerThreadsVec A vector of worker instances to run (classes + * who implement the DpdkWorkerThread interface). Number of workers in this + * vector must be equal to the number of cores in the core mask. Notice that + * the instances of DpdkWorkerThread shouldn't be freed until calling + * stopDpdkWorkerThreads() as these instances are running + * @return True if all worker threads started successfully or false if: DPDK + * isn't initialized (initDpdk() wasn't called or returned false), number of + * cores differs from number of workers, core mask includes DPDK master core + * or if one of the worker threads couldn't be run + */ + bool + startDpdkWorkerThreads(CoreMask coreMask, + std::vector& workerThreadsVec); + + /** + * Assuming worker threads are running, this method orders them to stop by + * calling DpdkWorkerThread#stop(). Then it waits until they stop running + */ + void stopDpdkWorkerThreads(); +}; } // namespace pcpp diff --git a/Pcap++/header/KniDevice.h b/Pcap++/header/KniDevice.h index f3d01947cf..2f85f82e2d 100644 --- a/Pcap++/header/KniDevice.h +++ b/Pcap++/header/KniDevice.h @@ -3,13 +3,13 @@ // GCOVR_EXCL_START -#include #include +#include #include "Device.h" -#include "MacAddress.h" -#include "MBufRawPacket.h" #include "LinuxNicInformationSocket.h" +#include "MBufRawPacket.h" +#include "MacAddress.h" /** * @file @@ -73,10 +73,15 @@ * suitable access rights (must have CAP_NET_ADMIN). * * Useful links: - * - KNI interface concept DPDK documentation + * - KNI + * interface concept DPDK documentation * - KNI PMD - * - KNI DPDK sample application - * - KNI DPDK test plan + * - KNI + * DPDK sample application + * - KNI DPDK + * test plan */ struct rte_kni; @@ -85,570 +90,641 @@ struct rte_kni; * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - class KniDevice; - class KniDeviceList; - - /** - * Defines the signature callback used by capturing API on KNI device - */ - typedef bool (*OnKniPacketArriveCallback)(MBufRawPacket* packets, uint32_t numOfPackets, KniDevice* device, void* userCookie); - - /** - * @class KniDevice - * This class represents special kind of DPDK devices called KNI - Kernel Network Interface - * that are used to exchange DPDK mbuf packets with Linux kernel network stack. - * This devices have only one RX and one TX queue so MT receiving or MT sending is not - * safe but simultaneous receiving and sending packets is MT safe. - * The RX queue of KNI device is pointed from kernel to application and TX queue is - * pointed in opposite direction - from application to kernel. So receive* methods will - * obtain packets from kernel and send* methods will send them to kernel.
- * The lifecycle of the KNI device is as follows: - * - KniDeviceConfiguration structure is created by user and filled with device settings; - * - The KniDeviceList#getInstance method is called to initialize the KNI module and - * obtain the KniDeviceList singleton; - * - KniDeviceList#createDevice method of the singleton is called to allocate new KNI device; - * - Device then must be opened by calling KniDevice#open; - * - During lifetime the application must handle the requests from kernel either by calling - * KniDevice#handleRequests to handle them synchronously or by starting separate thread - * to do this by calling KniDevice#startRequestHandlerThread; - * - During lifetime the packets may be send to/received from KNI device via - * calls to synchronous API (send/receive methods) or asynchronously by - * running capturing thread using KniDevice#startCapture; - * - KNI device will be destroyed or implicitly on application exit. User must assure - * that NO OTHER linux application is using KNI device when and after it is being - * destroyed otherwise Linux kernel may crush dramatically. - */ - class KniDevice : public IDevice - { - friend class KniDeviceList; - friend class MBufRawPacket; - public: - /** - * Various link related constants for KNI device - */ - enum KniLinkState - { - /** Returned by KNI functions if DPDK version used don't support link setup capability */ - LINK_NOT_SUPPORTED = -2, - /** Returned by KNI functions if link changing function meets an error */ - LINK_ERROR = -1, - /** Used to put link status on KNI device DOWN */ - LINK_DOWN = 0, - /** Used to put link status on KNI device UP */ - LINK_UP = 1 - }; - /** - * Various information related constants for KNI device - */ - enum KniInfoState - { - /** Used to identify intent to obtain cached version of KNI device information */ - INFO_CACHED = 0, - /** Used to identify intent to renew/update KNI device information */ - INFO_RENEW = 1 - }; - /** - * Promiscuous mode related constants for KNI device - */ - enum KniPromiscuousMode - { - /** Used to DISABLE promiscuous mode on KNI device */ - PROMISC_DISABLE = 0, - /** Used to ENABLE promiscuous mode on KNI device */ - PROMISC_ENABLE = 1 - }; - - /** - * @brief New callbacks for KNI device events. - * This structure MUST be used ONLY when KniDeviceList#callbackVersion returns - * KniDeviceList#KniCallbackVersion#CALLBACKS_NEW. - * Or if You are sure that DPDK version used is 17.11 or higher. - * If some callback is not provided (NULL) the request will always succeeds - * if other is not specified in callback description. - * @note This callbacks are direct copy of one defined in rte_kni_ops. Future - * maintainers of KNI device feature MUST refer to rte_kni_ops structure in - * rte_kni.h header file of DPDK to track the difference in signatures - */ - struct KniIoctlCallbacks - { - /** - * Pointer to function of changing MTU. - * Must return 0 in case of success or negative error code - */ - int (*change_mtu)(uint16_t port_id, unsigned int new_mtu); - /** - * Pointer to function of configuring network interface. - * Must return 0 in case of success or negative error code - */ - int (*config_network_if)(uint16_t port_id, uint8_t if_up); - /** - * Pointer to function of configuring mac address. - * If callback is not provided and port_id of KNI device is not UINT16_MAX - * then DPDK will use predefined callback witch sets MAC address of - * DPDK port port_id via rte_eth_dev_default_mac_addr_set. - * Must return 0 in case of success or negative error code - */ - int (*config_mac_address)(uint16_t port_id, uint8_t mac_addr[]); - /** - * Pointer to function of configuring promiscuous mode. - * If callback is not provided and port_id of KNI device is not UINT16_MAX - * then DPDK will use predefined callback witch sets promiscuous mode of - * DPDK port port_id via rte_eth_promiscuous_enable/rte_eth_promiscuous_disable. - * Must return 0 in case of success or negative error code - */ - int (*config_promiscusity)(uint16_t port_id, uint8_t to_on); - }; - - /** - * @brief Old callbacks for KNI device events. - * This structure MUST be used ONLY when KniDeviceList#callbackVersion returns - * KniDeviceList#KniCallbackVersion#CALLBACKS_OLD. - * Or if You are sure that DPDK version used is lower than 17.11. - * If some callback is not provided (NULL) the request will always succeeds. - */ - struct KniOldIoctlCallbacks - { - /** - * Pointer to function of changing MTU. - * Must return 0 in case of success or negative error code - */ - int (*change_mtu)(uint8_t port_id, unsigned int new_mtu); - /** - * Pointer to function of configuring network interface. - * Must return 0 in case of success or negative error code - */ - int (*config_network_if)(uint8_t port_id, uint8_t if_up); - }; - - /** - * @brief KNI device initialization data. - * Used to create new KNI device. - * Usage of callbacks member or oldCallbacks member is defined by - * result of KniDeviceList#callbackVersion - */ - struct KniDeviceConfiguration - { - /** - * Name used to display device in system. - * Must not interfere with already existing network interfaces. - * Must be less than or equal to IFNAMSIZ (16 chars including \0 on most systems) - */ - std::string name; - union - { - KniIoctlCallbacks* callbacks; - KniOldIoctlCallbacks* oldCallbacks; - }; - /** - * MAC (ETHERNET) address of new KNI device. - * If MacAddress::Zero is provided then - * some valid address will be automatically generated. - * If provided (not MacAddress::Zero) will be cached by new KNI device info structure. - */ - MacAddress mac; - /** - * Used in same way as DPDK port id. - * If some new callbacks are omitted then have separate meaning - * (see KniIoctlCallbacks#config_mac_address and KniIoctlCallbacks#config_promiscusity). - * Can be set to UINT16_MAX. - */ - uint16_t portId; - /** MTU of new KNI device. Will be cached by new KNI device info structure */ - uint16_t mtu; - /** - * If set forces to bind KNI Linux kernel thread (NOT userspace thread) to specific core. - * If rte_kni kernel module is loaded with "kthread_mode=single" then - - * rebinds kernel thread used for all KNI devices to specified core. - * If rte_kni kernel module is loaded with "kthread_mode=multiple" then - - * binds new kernel thread for this device to specified core. - */ - bool bindKthread; - /** ID of core to bind Linux kernel thread to (same as DPDK cores IDs) */ - uint32_t kthreadCoreId; - }; - - private: - /** All instances of this class MUST be produced by KniDeviceList class */ - KniDevice(const KniDeviceConfiguration& conf, size_t mempoolSize, int unique); - /** This class is not copyable */ - KniDevice(const KniDevice&); - /** This class is not copyable */ - KniDevice& operator=(const KniDevice&); - /** All instances of this class MUST be destroyed by KniDeviceList class */ - ~KniDevice(); - - public: - /* Information getters */ - - /** - * Indicates whether the KNI device was initialized successfully - */ - inline bool isInitialized() const { return !(m_Device == NULL || m_MBufMempool == NULL); } - /** - * Obtains name of KNI device in form of C++ string - */ - inline std::string getName() const { return std::string(m_DeviceInfo.name); } - /** - * Obtains port ID of KNI device - */ - inline uint16_t getPort() const { return m_DeviceInfo.portId; } - /** - * @brief Obtains link status of KNI device. - * If called with INFO_CACHED - returns cached data about link state (SUPER FAST may be INACCURATE). - * If called with INFO_RENEW - makes system call to Linux to obtain - * link information and caches it (VERY SLOW but ACCURATE). - * @param[in] state Defines information relevance level - * @return LINK_UP, LINK_DOWN, LINK_NOT_SUPPORTED if device is not initialized, some times LINK_ERROR - */ - KniLinkState getLinkState(KniInfoState state = INFO_CACHED); - /** - * @brief Obtains MAC address of KNI device. - * If called with INFO_CACHED - returns cached data about MAC address (SUPER FAST may be INACCURATE). - * If called with INFO_RENEW - makes system call to Linux to obtain - * MAC address and caches it (VERY SLOW but ACCURATE). - * @param[in] state Defines information relevance level - * @return Known MAC address of KNI interface - */ - MacAddress getMacAddress(KniInfoState state = INFO_CACHED); - /** - * @brief Obtains MTU of KNI device. - * If called with INFO_CACHED - returns cached data about MTU (SUPER FAST may be INACCURATE). - * If called with INFO_RENEW - makes system call to Linux to obtain - * MTU and caches it (VERY SLOW but ACCURATE). - * @param[in] state Defines information relevance level - * @return Known MTU of KNI interface - */ - uint16_t getMtu(KniInfoState state = INFO_CACHED); - /** - * @brief Obtains information about promiscuous mode of KNI device. - * If called with INFO_CACHED - returns cached data about promiscuous mode (SUPER FAST may be INACCURATE). - * If called with INFO_RENEW - makes system call to Linux to obtain - * promiscuous mode and caches it (VERY SLOW but ACCURATE). - * @param[in] state Defines information relevance level - * @return Known promiscuous mode of KNI interface - */ - KniPromiscuousMode getPromiscuous(KniInfoState state = INFO_CACHED); - - /* Information setters */ - - /** - * @brief Sets link state of KNI device. - * Firstly the link information is updated as by call to getLinkState(INFO_RENEW). - * Then link state is set only if obtained state differs from provided. - * New link state of KNI device is cached. - * @note You must be using sudo or be root or have CAP_NET_ADMIN capability to use this function - * @note Generates change link state request - * @param[in] state Must be LINK_UP or LINK_DOWN - * @return true if desired link state of KNI device is set (was as provided or set successfully), - * false if some error occurred (debug info is printed) - */ - bool setLinkState(KniLinkState state); - /** - * @brief Sets MAC address of KNI device. - * Unconditionally changes MAC of KNI device. - * If MAC is updated successfully then it is cached. - * @note You must be using sudo or be root or have CAP_NET_ADMIN capability to use this function - * @note Generates change mac request - * @param[in] mac New MAC address of KNI device - * @return true if desired MAC address is set, false if not and some error occurred (debug info is printed) - */ - bool setMacAddress(MacAddress mac); - /** - * @brief Sets MTU of KNI device. - * Unconditionally changes MTU of KNI device. - * If MTU is updated successfully then it is cached. - * @note You must be using sudo or be root or have CAP_NET_ADMIN capability to use this function - * @note Generates change mtu request - * @warning Low MTU values may crush Linux kernel. Follow Your kernel version documentation for details - * @param[in] mtu New MTU address of KNI device - * @return true if desired MTU is set, false if not and some error occurred (debug info is printed) - */ - bool setMtu(uint16_t mtu); - /** - * @brief Sets promiscuous mode of KNI device. - * Firstly the promiscuous mode information is updated as by call to getPromiscuous(INFO_RENEW). - * Then promiscuous mode is set only if obtained mode differs from provided. - * New promiscuous mode of KNI device is cached. - * @note You must be using sudo or be root or have CAP_NET_ADMIN capability to use this function - * @note Generates promiscuous mode request - * @param[in] mode Must be PROMISC_DISABLE or PROMISC_ENABLE - * @return true if desired promiscuous mode of KNI device is set (was as provided or set successfully), - * false if some error occurred (debug info is printed) - */ - bool setPromiscuous(KniPromiscuousMode mode); - /** - * @brief Updates link state of KNI device. - * Unconditionally updates link state of KNI device via call to DPDK librte_kni API. - * FASTER THAN setLinkState(state) but may not be supported or may fail. - * If link state is updated successfully then it is cached. - * @param[in] state New link state of KNI device - * @return LINK_NOT_SUPPORTED if this capability is not supported by DPDK version used (DPDK ver < 18.11), - * LINK_ERROR if attempt to set link state failed (may always fail on some systems see class description) - * LINK_DOWN - previous link state was DOWN, state is successfully updated to provided one - * LINK_UP - previous link state was UP, state is successfully updated to provided one - */ - KniLinkState updateLinkState(KniLinkState state); - - /* Requests */ - - /** - * @brief Handle requests from Linux kernel synchronously in calling thread. - * When one of events which is needed application attention occurres it must be handled by calling this - * function (or by running RequestHandlerThread for this device). - * Until the request is handled the Linux kernel thread that manages this KNI is blocked. - * If it is not handled by application in 3 seconds the request is reported to kernel as failed one. - * Current known requests are: - * - change link state: ip l set [interface] up/down - * - change mtu: ip l set dev [interface] mtu [mtu_count] - * - change mac: ip l set [interface] address [new_mac] - * - change promiscuous mode: ip l set [interface] promisc on/off - * @warning Functions setLinkState, setMacAddress, setMtu and setPromiscuous will generate this requests. - * @note Callbacks provided for this KNI device will be called synchronously in calling thread during execution of this function - * @return true if no error happened during request handling false otherwise - */ - bool handleRequests(); - /** - * @brief Starts new thread (using pthread) to asynchronously handle KNI device requests. - * See description of handleRequests() about requests. - * New thread is detached using pthread_detach. - * This thread can be stopped explicitly by calling stopRequestHandlerThread() or - * implicitly on KNI device destruction. - * Linux nanosleep() function is used for sleeping. - * @note Callbacks provided for this KNI device will be called asynchronously in new thread - * @param[in] sleepSeconds Sleeping time in seconds - * @param[in] sleepNanoSeconds Sleeping time in nanoseconds - * @return true if new thread is started successfully false otherwise - */ - bool startRequestHandlerThread(long sleepSeconds, long sleepNanoSeconds = 0); - /** - * @brief Explicitly stops request thread for this device if it was running. - * See description of handleRequests() about requests. - * @warning There may be a rare error when request thread handles requests on already - * destroyed device. It occurres only because request thread is detached one but it is really really rare. - * In case of this error occurring (must be SIGSEGV) change type of created thread in startRequestHandlerThread - * function from DETACHED to JOINABLE. - */ - void stopRequestHandlerThread(); - - /* Packet receive */ - - /** - * @brief Receive raw packets from kernel. - * @param[out] rawPacketsArr A vector where all received packets will be written into - * @return The number of packets received. If an error occurred 0 will be returned and the error will be printed to log - */ - uint16_t receivePackets(MBufRawPacketVector& rawPacketsArr); - /** - * @brief Receive raw packets from kernel. - * Please notice that in terms of performance, this is the best method to use - * for receiving packets because out of all receivePackets overloads this method requires the least overhead and is - * almost as efficient as receiving packets directly through DPDK. So if performance is a critical factor in your - * application, please use this method - * @param[out] rawPacketsArr A pointer to an array of MBufRawPacket pointers where all received packets will be written into. The array is expected to - * be allocated by the user and its length should be provided in rawPacketArrLength. Number of packets received will be returned. - * Notice it's the user responsibility to free the array and its content when done using it - * @param[out] rawPacketArrLength The length of MBufRawPacket pointers array - * @return The number of packets received. If an error occurred 0 will be returned and the error will be printed to log - */ - uint16_t receivePackets(MBufRawPacket** rawPacketsArr, uint16_t rawPacketArrLength); - /** - * @brief Receive parsed packets from kernel. - * @param[out] packetsArr A pointer to an allocated array of Packet pointers where all received packets will be written into. The array is expected to - * be allocated by the user and its length should be provided in packetsArrLength. Number of packets received will be returned. - * Notice it's the user responsibility to free the array and its content when done using it - * @param[out] packetsArrLength The length of Packet pointers array - * @return The number of packets received. If an error occurred 0 will be returned and the error will be printed to log - */ - uint16_t receivePackets(Packet** packetsArr, uint16_t packetsArrLength); - - /* Packet send */ - - /** - * @brief Send an array of MBufRawPacket to kernel. - * Please notice the following:
- * - In terms of performance, this is the best method to use for sending packets because out of all sendPackets overloads - * this method requires the least overhead and is almost as efficient as sending the packets directly through DPDK. So if performance - * is a critical factor in your application, please use this method - * - If the number of packets to send is higher than 64 this method will run multiple iterations of sending packets to DPDK, each - * iteration of 64 packets - * - The mbufs used in this method aren't freed by this method, they will be transparently freed by DPDK - *

- * @param[in] rawPacketsArr A pointer to an array of MBufRawPacket - * @param[in] arrLength The length of the array - * @return The number of packets actually and successfully sent - */ - uint16_t sendPackets(MBufRawPacket** rawPacketsArr, uint16_t arrLength); - /** - * @brief Send an array of parsed packets to kernel. - * Please notice the following:
- * - If some or all of the packets contain raw packets which aren't of type MBufRawPacket, a new temp MBufRawPacket instances - * will be created and packet data will be copied to them. This is necessary to allocate mbufs which will store the data to be sent. - * If performance is a critical factor please make sure you send parsed packets - * that contain only raw packets of type MBufRawPacket - * - If the number of packets to send is higher than 64 this method will run multiple iterations of sending packets to DPDK, each - * iteration of 64 packets - * - The mbufs used or allocated in this method aren't freed by this method, they will be transparently freed by DPDK - *

- * @param[in] packetsArr A pointer to an array of parsed packet pointers - * @param[in] arrLength The length of the array - * @return The number of packets actually and successfully sent - */ - uint16_t sendPackets(Packet** packetsArr, uint16_t arrLength); - /** - * @brief Send a vector of MBufRawPacket pointers to kernel. - * Please notice the following:
- * - If the number of packets to send is higher than 64 this method will run multiple iterations of sending packets to DPDK, each - * iteration of 64 packets - * - The mbufs used in this method aren't freed by this method, they will be transparently freed by DPDK - *

- * @param[in] rawPacketsVec The vector of raw packet - * @return The number of packets actually and successfully sent - */ - uint16_t sendPackets(MBufRawPacketVector& rawPacketsVec); - /** - * @brief Send a vector of RawPacket pointers to kernel. - * Please notice the following:
- * - If some or all of the raw packets aren't of type MBufRawPacket, a new temp MBufRawPacket instances will be created - * and packet data will be copied to them. This is necessary to allocate mbufs which will store the data to be sent. If - * performance is a critical factor please make sure you send only raw packets of type MBufRawPacket - * (or use the sendPackets overload that sends MBufRawPacketVector) - * - If the number of packets to send is higher than 64 this method will run multiple iterations of sending packets to DPDK, each - * iteration of 64 packets - * - The mbufs used or allocated in this method aren't freed by this method, they will be transparently freed by DPDK - *

- * @param[in] rawPacketsVec The vector of raw packet - * @return The number of packets actually and successfully sent - */ - uint16_t sendPackets(RawPacketVector& rawPacketsVec); - /** - * @brief Send a raw packet to kernel. - * Please notice that if the raw packet isn't of type MBufRawPacket, a new temp MBufRawPacket - * will be created and the data will be copied to it. This is necessary to allocate an mbuf which will store the data to be sent. - * If performance is a critical factor please make sure you send a raw packet of type MBufRawPacket. - * Please also notice that the mbuf used or allocated in this method isn't freed by this method, it will be transparently freed by DPDK - * @param[in] rawPacket The raw packet to send - * @return True if packet was sent successfully or false if the packet wasn't sent for any other reason - */ - bool sendPacket(RawPacket& rawPacket); - /** - * @brief Send a MBufRawPacket to kernel. - * Please notice that the mbuf used in this method isn't freed by this method, it will be transparently freed by DPDK - * @param[in] rawPacket The MBufRawPacket to send - * @return True if packet was sent successfully or false if device is not opened or if the packet wasn't sent for any other reason - */ - bool sendPacket(MBufRawPacket& rawPacket); - /** - * @brief Send a parsed packet to kernel. - * Please notice that the mbuf used or allocated in this method isn't freed by this method, it will be transparently freed by DPDK - * @param[in] packet The parsed packet to send. Please notice that if the packet contains a raw packet which isn't of type - * MBufRawPacket, a new temp MBufRawPacket will be created and the data will be copied to it. This is necessary to - * allocate an mbuf which will store the data to be sent. If performance is a critical factor please make sure you send a - * parsed packet that contains a raw packet of type MBufRawPacket - * @return True if packet was sent successfully or false if device is not opened or if the packet wasn't sent for any other reason - */ - bool sendPacket(Packet& packet); - - /* Packet capture */ - - /** - * @brief Start capturing packets asynchronously on this KNI interface. - * Each time a burst of packets is captured the onPacketArrives callback is called. - * The capture is done on a new thread created by this method, meaning all callback - * calls are done in a thread other than the caller thread. - * Capture process will stop and this capture thread will be terminated when calling stopCapture(). - * This method must be called after the device is opened (i.e the open() method was called), otherwise an error will be returned. - * Capturing thread will be terminated automatically on KNI device destruction or when close() is called. - * @param[in] onPacketArrives A callback that is called each time a burst of packets is captured - * @param[in] onPacketArrivesUserCookie A pointer to a user provided object. This object will be transferred to the onPacketArrives callback - * each time it is called. This cookie is very useful for transferring objects that give context to the capture callback, for example: - * objects that counts packets, manages flow state or manages the application state according to the packet that was captured - * @return True if capture started successfully, false if (relevant log error is printed in any case): - * - Capture is already running - * - Device is not opened - * - Capture thread could not be created - */ - bool startCapture(OnKniPacketArriveCallback onPacketArrives, void* onPacketArrivesUserCookie); - /** - * @brief Start capturing packets synchronously on this KNI interface in blocking mode. - * Blocking mode means that this method block and won't return until the user frees the blocking - * (via onPacketArrives callback) or until a user defined timeout expires. - * Whenever a burst of packets is captured the onPacketArrives callback is called and lets the user handle the packet. - * In each callback call the user should return true if he wants to release the block or false if it wants it to keep blocking. - * Regardless of this callback a timeout is defined when stop capturing. - * When this timeout expires the method will return.
- * Please notice that stopCapture() isn't needed here because when the method returns (after timeout or per user decision) capturing - * on the device is stopped. - * @param[in] onPacketArrives A callback given by the user for handling incoming packets. After handling each burst of packets - * the user needs to return a boolean value. True value indicates stop capturing and stop blocking and - * false value indicates continue capturing and blocking - * @param[in] onPacketArrivesUserCookie A pointer to a user provided object. This object will be transferred to the onPacketArrives callback - * each time it is called. This cookie is very useful for transferring objects that give context to the capture callback, for example: - * objects that counts packets, manages flow state or manages the application state according to the packet that was captured - * @param[in] timeout A timeout in seconds for the blocking to stop even if the user didn't return "true" in the onPacketArrives callback - * If this timeout is set to 0 or less the timeout will be ignored, meaning the method will keep blocking until the user frees it via - * the onPacketArrives callback - * @return -1 if timeout expired, 1 if blocking was stopped via onPacketArrives callback or 0 if an error occurred (such as device - * not open etc.). When returning 0 an appropriate error message is printed to log - */ - int startCaptureBlockingMode(OnKniPacketArriveCallback onPacketArrives, void* onPacketArrivesUserCookie, int timeout); - /** - * Stop a currently running asynchronous packet capture. - */ - void stopCapture(); - - /* Device control */ - - /** - * Takes appropriate actions for opening KNI device. - * @return true if the device was opened successfully, false if device is already opened, - * or KNI device configuration and startup failed - */ - bool open(); - /** - * @brief Close the KNI device. - * When device is closed it's not possible to work with it. - * Stops asynchronous packet capture if it is running. - */ - void close(); - - private: - struct rte_kni* m_Device; - struct rte_mempool* m_MBufMempool; - struct KniDeviceInfo - { - LinuxNicInformationSocket soc; - KniLinkState link; - KniPromiscuousMode promisc; - uint16_t portId; - uint16_t mtu; - MacAddress mac; - std::string name; - - bool init(const KniDeviceConfiguration& conf); - } m_DeviceInfo; - struct KniThread; - struct KniCapturing - { - OnKniPacketArriveCallback callback; - void* userCookie; - KniThread* thread; - - static void runCapture(void* devicePointer, std::atomic& stopThread); - inline bool isRunning() const { return thread != NULL; } - void cleanup(); - } m_Capturing; - struct KniRequests - { - long sleepS; - long sleepNs; - KniThread* thread; - - static void runRequests(void* devicePointer, std::atomic& stopThread); - void cleanup(); - } m_Requests; - }; +namespace pcpp { +class KniDevice; +class KniDeviceList; + +/** + * Defines the signature callback used by capturing API on KNI device + */ +typedef bool (*OnKniPacketArriveCallback)(MBufRawPacket* packets, + uint32_t numOfPackets, + KniDevice* device, void* userCookie); + +/** + * @class KniDevice + * This class represents special kind of DPDK devices called KNI - Kernel + * Network Interface that are used to exchange DPDK mbuf packets with Linux + * kernel network stack. This devices have only one RX and one TX queue so MT + * receiving or MT sending is not safe but simultaneous receiving and sending + * packets is MT safe. The RX queue of KNI device is pointed from kernel to + * application and TX queue is pointed in opposite direction - from application + * to kernel. So receive* methods will obtain packets from kernel and send* + * methods will send them to kernel.
The lifecycle of the KNI device is as + * follows: + * - KniDeviceConfiguration structure is created by user and filled with device + * settings; + * - The KniDeviceList#getInstance method is called to initialize the KNI + * module and obtain the KniDeviceList singleton; + * - KniDeviceList#createDevice method of the singleton is called to allocate + * new KNI device; + * - Device then must be opened by calling KniDevice#open; + * - During lifetime the application must handle the requests from kernel + * either by calling KniDevice#handleRequests to handle them synchronously or by + * starting separate thread to do this by calling + * KniDevice#startRequestHandlerThread; + * - During lifetime the packets may be send to/received from KNI device via + * calls to synchronous API (send/receive methods) or asynchronously by + * running capturing thread using KniDevice#startCapture; + * - KNI device will be destroyed or implicitly on application exit. User must + * assure that NO OTHER linux application is using KNI device when and after it + * is being destroyed otherwise Linux kernel may crush dramatically. + */ +class KniDevice : public IDevice { + friend class KniDeviceList; + friend class MBufRawPacket; + + public: + /** + * Various link related constants for KNI device + */ + enum KniLinkState { + /** Returned by KNI functions if DPDK version used don't support link setup + capability */ + LINK_NOT_SUPPORTED = -2, + /** Returned by KNI functions if link changing function meets an error */ + LINK_ERROR = -1, + /** Used to put link status on KNI device DOWN */ + LINK_DOWN = 0, + /** Used to put link status on KNI device UP */ + LINK_UP = 1 + }; + /** + * Various information related constants for KNI device + */ + enum KniInfoState { + /** Used to identify intent to obtain cached version of KNI device + information */ + INFO_CACHED = 0, + /** Used to identify intent to renew/update KNI device information */ + INFO_RENEW = 1 + }; + /** + * Promiscuous mode related constants for KNI device + */ + enum KniPromiscuousMode { + /** Used to DISABLE promiscuous mode on KNI device */ + PROMISC_DISABLE = 0, + /** Used to ENABLE promiscuous mode on KNI device */ + PROMISC_ENABLE = 1 + }; + + /** + * @brief New callbacks for KNI device events. + * This structure MUST be used ONLY when KniDeviceList#callbackVersion returns + * KniDeviceList#KniCallbackVersion#CALLBACKS_NEW. + * Or if You are sure that DPDK version used is 17.11 or higher. + * If some callback is not provided (NULL) the request will always succeeds + * if other is not specified in callback description. + * @note This callbacks are direct copy of one defined in rte_kni_ops. Future + * maintainers of KNI device feature MUST refer to rte_kni_ops structure in + * rte_kni.h header file of DPDK to track the difference in signatures + */ + struct KniIoctlCallbacks { + /** + * Pointer to function of changing MTU. + * Must return 0 in case of success or negative error code + */ + int (*change_mtu)(uint16_t port_id, unsigned int new_mtu); + /** + * Pointer to function of configuring network interface. + * Must return 0 in case of success or negative error code + */ + int (*config_network_if)(uint16_t port_id, uint8_t if_up); + /** + * Pointer to function of configuring mac address. + * If callback is not provided and port_id of KNI device is not UINT16_MAX + * then DPDK will use predefined callback witch sets MAC address of + * DPDK port port_id via rte_eth_dev_default_mac_addr_set. + * Must return 0 in case of success or negative error code + */ + int (*config_mac_address)(uint16_t port_id, uint8_t mac_addr[]); + /** + * Pointer to function of configuring promiscuous mode. + * If callback is not provided and port_id of KNI device is not UINT16_MAX + * then DPDK will use predefined callback witch sets promiscuous mode of + * DPDK port port_id via + * rte_eth_promiscuous_enable/rte_eth_promiscuous_disable. Must return 0 in + * case of success or negative error code + */ + int (*config_promiscusity)(uint16_t port_id, uint8_t to_on); + }; + + /** + * @brief Old callbacks for KNI device events. + * This structure MUST be used ONLY when KniDeviceList#callbackVersion returns + * KniDeviceList#KniCallbackVersion#CALLBACKS_OLD. + * Or if You are sure that DPDK version used is lower than 17.11. + * If some callback is not provided (NULL) the request will always succeeds. + */ + struct KniOldIoctlCallbacks { + /** + * Pointer to function of changing MTU. + * Must return 0 in case of success or negative error code + */ + int (*change_mtu)(uint8_t port_id, unsigned int new_mtu); + /** + * Pointer to function of configuring network interface. + * Must return 0 in case of success or negative error code + */ + int (*config_network_if)(uint8_t port_id, uint8_t if_up); + }; + + /** + * @brief KNI device initialization data. + * Used to create new KNI device. + * Usage of callbacks member or oldCallbacks member is defined by + * result of KniDeviceList#callbackVersion + */ + struct KniDeviceConfiguration { + /** + * Name used to display device in system. + * Must not interfere with already existing network interfaces. + * Must be less than or equal to IFNAMSIZ (16 chars including \0 on most + * systems) + */ + std::string name; + union { + KniIoctlCallbacks* callbacks; + KniOldIoctlCallbacks* oldCallbacks; + }; + /** + * MAC (ETHERNET) address of new KNI device. + * If MacAddress::Zero is provided then + * some valid address will be automatically generated. + * If provided (not MacAddress::Zero) will be cached by new KNI device info + * structure. + */ + MacAddress mac; + /** + * Used in same way as DPDK port id. + * If some new callbacks are omitted then have separate meaning + * (see KniIoctlCallbacks#config_mac_address and + * KniIoctlCallbacks#config_promiscusity). Can be set to UINT16_MAX. + */ + uint16_t portId; + /** MTU of new KNI device. Will be cached by new KNI device info structure + */ + uint16_t mtu; + /** + * If set forces to bind KNI Linux kernel thread (NOT userspace thread) to + * specific core. If rte_kni kernel module is loaded with + * "kthread_mode=single" then - rebinds kernel thread used for all KNI + * devices to specified core. If rte_kni kernel module is loaded with + * "kthread_mode=multiple" then - binds new kernel thread for this device to + * specified core. + */ + bool bindKthread; + /** ID of core to bind Linux kernel thread to (same as DPDK cores IDs) */ + uint32_t kthreadCoreId; + }; + + private: + /** All instances of this class MUST be produced by KniDeviceList class */ + KniDevice(const KniDeviceConfiguration& conf, size_t mempoolSize, int unique); + /** This class is not copyable */ + KniDevice(const KniDevice&); + /** This class is not copyable */ + KniDevice& operator=(const KniDevice&); + /** All instances of this class MUST be destroyed by KniDeviceList class */ + ~KniDevice(); + + public: + /* Information getters */ + + /** + * Indicates whether the KNI device was initialized successfully + */ + inline bool isInitialized() const { + return !(m_Device == NULL || m_MBufMempool == NULL); + } + /** + * Obtains name of KNI device in form of C++ string + */ + inline std::string getName() const { return std::string(m_DeviceInfo.name); } + /** + * Obtains port ID of KNI device + */ + inline uint16_t getPort() const { return m_DeviceInfo.portId; } + /** + * @brief Obtains link status of KNI device. + * If called with INFO_CACHED - returns cached data about link state (SUPER + * FAST may be INACCURATE). If called with INFO_RENEW - makes system call to + * Linux to obtain link information and caches it (VERY SLOW but ACCURATE). + * @param[in] state Defines information relevance level + * @return LINK_UP, LINK_DOWN, LINK_NOT_SUPPORTED if device is not + * initialized, some times LINK_ERROR + */ + KniLinkState getLinkState(KniInfoState state = INFO_CACHED); + /** + * @brief Obtains MAC address of KNI device. + * If called with INFO_CACHED - returns cached data about MAC address (SUPER + * FAST may be INACCURATE). If called with INFO_RENEW - makes system call to + * Linux to obtain MAC address and caches it (VERY SLOW but ACCURATE). + * @param[in] state Defines information relevance level + * @return Known MAC address of KNI interface + */ + MacAddress getMacAddress(KniInfoState state = INFO_CACHED); + /** + * @brief Obtains MTU of KNI device. + * If called with INFO_CACHED - returns cached data about MTU (SUPER FAST may + * be INACCURATE). If called with INFO_RENEW - makes system call to Linux to + * obtain MTU and caches it (VERY SLOW but ACCURATE). + * @param[in] state Defines information relevance level + * @return Known MTU of KNI interface + */ + uint16_t getMtu(KniInfoState state = INFO_CACHED); + /** + * @brief Obtains information about promiscuous mode of KNI device. + * If called with INFO_CACHED - returns cached data about promiscuous mode + * (SUPER FAST may be INACCURATE). If called with INFO_RENEW - makes system + * call to Linux to obtain promiscuous mode and caches it (VERY SLOW but + * ACCURATE). + * @param[in] state Defines information relevance level + * @return Known promiscuous mode of KNI interface + */ + KniPromiscuousMode getPromiscuous(KniInfoState state = INFO_CACHED); + + /* Information setters */ + + /** + * @brief Sets link state of KNI device. + * Firstly the link information is updated as by call to + * getLinkState(INFO_RENEW). Then link state is set only if obtained state + * differs from provided. New link state of KNI device is cached. + * @note You must be using sudo or be root or have CAP_NET_ADMIN capability to + * use this function + * @note Generates change link state request + * @param[in] state Must be LINK_UP or LINK_DOWN + * @return true if desired link state of KNI device is set (was as provided or + * set successfully), false if some error occurred (debug info is printed) + */ + bool setLinkState(KniLinkState state); + /** + * @brief Sets MAC address of KNI device. + * Unconditionally changes MAC of KNI device. + * If MAC is updated successfully then it is cached. + * @note You must be using sudo or be root or have CAP_NET_ADMIN capability to + * use this function + * @note Generates change mac request + * @param[in] mac New MAC address of KNI device + * @return true if desired MAC address is set, false if not and some error + * occurred (debug info is printed) + */ + bool setMacAddress(MacAddress mac); + /** + * @brief Sets MTU of KNI device. + * Unconditionally changes MTU of KNI device. + * If MTU is updated successfully then it is cached. + * @note You must be using sudo or be root or have CAP_NET_ADMIN capability to + * use this function + * @note Generates change mtu request + * @warning Low MTU values may crush Linux kernel. Follow Your kernel version + * documentation for details + * @param[in] mtu New MTU address of KNI device + * @return true if desired MTU is set, false if not and some error occurred + * (debug info is printed) + */ + bool setMtu(uint16_t mtu); + /** + * @brief Sets promiscuous mode of KNI device. + * Firstly the promiscuous mode information is updated as by call to + * getPromiscuous(INFO_RENEW). Then promiscuous mode is set only if obtained + * mode differs from provided. New promiscuous mode of KNI device is cached. + * @note You must be using sudo or be root or have CAP_NET_ADMIN capability to + * use this function + * @note Generates promiscuous mode request + * @param[in] mode Must be PROMISC_DISABLE or PROMISC_ENABLE + * @return true if desired promiscuous mode of KNI device is set (was as + * provided or set successfully), false if some error occurred (debug info is + * printed) + */ + bool setPromiscuous(KniPromiscuousMode mode); + /** + * @brief Updates link state of KNI device. + * Unconditionally updates link state of KNI device via call to DPDK + * librte_kni API. FASTER THAN setLinkState(state) but may not be supported or + * may fail. If link state is updated successfully then it is cached. + * @param[in] state New link state of KNI device + * @return LINK_NOT_SUPPORTED if this capability is not supported by DPDK + * version used (DPDK ver < 18.11), LINK_ERROR if attempt to set link state + * failed (may always fail on some systems see class description) LINK_DOWN - + * previous link state was DOWN, state is successfully updated to provided one + * LINK_UP - previous link state was UP, state is successfully updated to + * provided one + */ + KniLinkState updateLinkState(KniLinkState state); + + /* Requests */ + + /** + * @brief Handle requests from Linux kernel synchronously in calling thread. + * When one of events which is needed application attention occurres it must + * be handled by calling this function (or by running RequestHandlerThread for + * this device). Until the request is handled the Linux kernel thread that + * manages this KNI is blocked. If it is not handled by application in 3 + * seconds the request is reported to kernel as failed one. Current known + * requests are: + * - change link state: ip l set [interface] up/down + * - change mtu: ip l set dev [interface] mtu [mtu_count] + * - change mac: ip l set [interface] address [new_mac] + * - change promiscuous mode: ip l set [interface] promisc on/off + * @warning Functions setLinkState, setMacAddress, setMtu and setPromiscuous + * will generate this requests. + * @note Callbacks provided for this KNI device will be called synchronously + * in calling thread during execution of this function + * @return true if no error happened during request handling false otherwise + */ + bool handleRequests(); + /** + * @brief Starts new thread (using pthread) to asynchronously handle KNI + * device requests. See description of handleRequests() about requests. New + * thread is detached using pthread_detach. This thread can be stopped + * explicitly by calling stopRequestHandlerThread() or implicitly on KNI + * device destruction. Linux nanosleep() + * function is used for sleeping. + * @note Callbacks provided for this KNI device will be called asynchronously + * in new thread + * @param[in] sleepSeconds Sleeping time in seconds + * @param[in] sleepNanoSeconds Sleeping time in nanoseconds + * @return true if new thread is started successfully false otherwise + */ + bool startRequestHandlerThread(long sleepSeconds, long sleepNanoSeconds = 0); + /** + * @brief Explicitly stops request thread for this device if it was running. + * See description of handleRequests() about requests. + * @warning There may be a rare error when request thread handles requests on + * already destroyed device. It occurres only because request thread is + * detached one but it is really really rare. In case of this error occurring + * (must be SIGSEGV) change type of created thread in + * startRequestHandlerThread function from DETACHED to JOINABLE. + */ + void stopRequestHandlerThread(); + + /* Packet receive */ + + /** + * @brief Receive raw packets from kernel. + * @param[out] rawPacketsArr A vector where all received packets will be + * written into + * @return The number of packets received. If an error occurred 0 will be + * returned and the error will be printed to log + */ + uint16_t receivePackets(MBufRawPacketVector& rawPacketsArr); + /** + * @brief Receive raw packets from kernel. + * Please notice that in terms of performance, this is the best method to use + * for receiving packets because out of all receivePackets overloads this + * method requires the least overhead and is almost as efficient as receiving + * packets directly through DPDK. So if performance is a critical factor in + * your application, please use this method + * @param[out] rawPacketsArr A pointer to an array of MBufRawPacket pointers + * where all received packets will be written into. The array is expected to + * be allocated by the user and its length should be provided in + * rawPacketArrLength. Number of packets received will be returned. Notice + * it's the user responsibility to free the array and its content when done + * using it + * @param[out] rawPacketArrLength The length of MBufRawPacket pointers array + * @return The number of packets received. If an error occurred 0 will be + * returned and the error will be printed to log + */ + uint16_t receivePackets(MBufRawPacket** rawPacketsArr, + uint16_t rawPacketArrLength); + /** + * @brief Receive parsed packets from kernel. + * @param[out] packetsArr A pointer to an allocated array of Packet pointers + * where all received packets will be written into. The array is expected to + * be allocated by the user and its length should be provided in + * packetsArrLength. Number of packets received will be returned. Notice it's + * the user responsibility to free the array and its content when done using + * it + * @param[out] packetsArrLength The length of Packet pointers array + * @return The number of packets received. If an error occurred 0 will be + * returned and the error will be printed to log + */ + uint16_t receivePackets(Packet** packetsArr, uint16_t packetsArrLength); + + /* Packet send */ + + /** + * @brief Send an array of MBufRawPacket to kernel. + * Please notice the following:
+ * - In terms of performance, this is the best method to use for sending + * packets because out of all sendPackets overloads this method requires the + * least overhead and is almost as efficient as sending the packets directly + * through DPDK. So if performance is a critical factor in your application, + * please use this method + * - If the number of packets to send is higher than 64 this method will run + * multiple iterations of sending packets to DPDK, each iteration of 64 + * packets + * - The mbufs used in this method aren't freed by this method, they will be + * transparently freed by DPDK

+ * @param[in] rawPacketsArr A pointer to an array of MBufRawPacket + * @param[in] arrLength The length of the array + * @return The number of packets actually and successfully sent + */ + uint16_t sendPackets(MBufRawPacket** rawPacketsArr, uint16_t arrLength); + /** + * @brief Send an array of parsed packets to kernel. + * Please notice the following:
+ * - If some or all of the packets contain raw packets which aren't of type + * MBufRawPacket, a new temp MBufRawPacket instances will be created and + * packet data will be copied to them. This is necessary to allocate mbufs + * which will store the data to be sent. If performance is a critical factor + * please make sure you send parsed packets that contain only raw packets of + * type MBufRawPacket + * - If the number of packets to send is higher than 64 this method will run + * multiple iterations of sending packets to DPDK, each iteration of 64 + * packets + * - The mbufs used or allocated in this method aren't freed by this method, + * they will be transparently freed by DPDK

+ * @param[in] packetsArr A pointer to an array of parsed packet pointers + * @param[in] arrLength The length of the array + * @return The number of packets actually and successfully sent + */ + uint16_t sendPackets(Packet** packetsArr, uint16_t arrLength); + /** + * @brief Send a vector of MBufRawPacket pointers to kernel. + * Please notice the following:
+ * - If the number of packets to send is higher than 64 this method will run + * multiple iterations of sending packets to DPDK, each iteration of 64 + * packets + * - The mbufs used in this method aren't freed by this method, they will be + * transparently freed by DPDK

+ * @param[in] rawPacketsVec The vector of raw packet + * @return The number of packets actually and successfully sent + */ + uint16_t sendPackets(MBufRawPacketVector& rawPacketsVec); + /** + * @brief Send a vector of RawPacket pointers to kernel. + * Please notice the following:
+ * - If some or all of the raw packets aren't of type MBufRawPacket, a new + * temp MBufRawPacket instances will be created and packet data will be copied + * to them. This is necessary to allocate mbufs which will store the data to + * be sent. If performance is a critical factor please make sure you send only + * raw packets of type MBufRawPacket (or use the sendPackets overload that + * sends MBufRawPacketVector) + * - If the number of packets to send is higher than 64 this method will run + * multiple iterations of sending packets to DPDK, each iteration of 64 + * packets + * - The mbufs used or allocated in this method aren't freed by this method, + * they will be transparently freed by DPDK

+ * @param[in] rawPacketsVec The vector of raw packet + * @return The number of packets actually and successfully sent + */ + uint16_t sendPackets(RawPacketVector& rawPacketsVec); + /** + * @brief Send a raw packet to kernel. + * Please notice that if the raw packet isn't of type MBufRawPacket, a new + * temp MBufRawPacket will be created and the data will be copied to it. This + * is necessary to allocate an mbuf which will store the data to be sent. If + * performance is a critical factor please make sure you send a raw packet of + * type MBufRawPacket. Please also notice that the mbuf used or allocated in + * this method isn't freed by this method, it will be transparently freed by + * DPDK + * @param[in] rawPacket The raw packet to send + * @return True if packet was sent successfully or false if the packet wasn't + * sent for any other reason + */ + bool sendPacket(RawPacket& rawPacket); + /** + * @brief Send a MBufRawPacket to kernel. + * Please notice that the mbuf used in this method isn't freed by this method, + * it will be transparently freed by DPDK + * @param[in] rawPacket The MBufRawPacket to send + * @return True if packet was sent successfully or false if device is not + * opened or if the packet wasn't sent for any other reason + */ + bool sendPacket(MBufRawPacket& rawPacket); + /** + * @brief Send a parsed packet to kernel. + * Please notice that the mbuf used or allocated in this method isn't freed by + * this method, it will be transparently freed by DPDK + * @param[in] packet The parsed packet to send. Please notice that if the + * packet contains a raw packet which isn't of type MBufRawPacket, a new temp + * MBufRawPacket will be created and the data will be copied to it. This is + * necessary to allocate an mbuf which will store the data to be sent. If + * performance is a critical factor please make sure you send a parsed packet + * that contains a raw packet of type MBufRawPacket + * @return True if packet was sent successfully or false if device is not + * opened or if the packet wasn't sent for any other reason + */ + bool sendPacket(Packet& packet); + + /* Packet capture */ + + /** + * @brief Start capturing packets asynchronously on this KNI interface. + * Each time a burst of packets is captured the onPacketArrives callback is + * called. The capture is done on a new thread created by this method, meaning + * all callback calls are done in a thread other than the caller thread. + * Capture process will stop and this capture thread will be terminated when + * calling stopCapture(). This method must be called after the device is + * opened (i.e the open() method was called), otherwise an error will be + * returned. Capturing thread will be terminated automatically on KNI device + * destruction or when close() is called. + * @param[in] onPacketArrives A callback that is called each time a burst of + * packets is captured + * @param[in] onPacketArrivesUserCookie A pointer to a user provided object. + * This object will be transferred to the onPacketArrives callback each time + * it is called. This cookie is very useful for transferring objects that give + * context to the capture callback, for example: objects that counts packets, + * manages flow state or manages the application state according to the packet + * that was captured + * @return True if capture started successfully, false if (relevant log error + * is printed in any case): + * - Capture is already running + * - Device is not opened + * - Capture thread could not be created + */ + bool startCapture(OnKniPacketArriveCallback onPacketArrives, + void* onPacketArrivesUserCookie); + /** + * @brief Start capturing packets synchronously on this KNI interface in + * blocking mode. Blocking mode means that this method block and won't return + * until the user frees the blocking (via onPacketArrives callback) or until a + * user defined timeout expires. Whenever a burst of packets is captured the + * onPacketArrives callback is called and lets the user handle the packet. In + * each callback call the user should return true if he wants to release the + * block or false if it wants it to keep blocking. Regardless of this callback + * a timeout is defined when stop capturing. When this timeout expires the + * method will return.
Please notice that stopCapture() isn't needed here + * because when the method returns (after timeout or per user decision) + * capturing on the device is stopped. + * @param[in] onPacketArrives A callback given by the user for handling + * incoming packets. After handling each burst of packets the user needs to + * return a boolean value. True value indicates stop capturing and stop + * blocking and false value indicates continue capturing and blocking + * @param[in] onPacketArrivesUserCookie A pointer to a user provided object. + * This object will be transferred to the onPacketArrives callback each time + * it is called. This cookie is very useful for transferring objects that give + * context to the capture callback, for example: objects that counts packets, + * manages flow state or manages the application state according to the packet + * that was captured + * @param[in] timeout A timeout in seconds for the blocking to stop even if + * the user didn't return "true" in the onPacketArrives callback If this + * timeout is set to 0 or less the timeout will be ignored, meaning the method + * will keep blocking until the user frees it via the onPacketArrives callback + * @return -1 if timeout expired, 1 if blocking was stopped via + * onPacketArrives callback or 0 if an error occurred (such as device not open + * etc.). When returning 0 an appropriate error message is printed to log + */ + int startCaptureBlockingMode(OnKniPacketArriveCallback onPacketArrives, + void* onPacketArrivesUserCookie, int timeout); + /** + * Stop a currently running asynchronous packet capture. + */ + void stopCapture(); + + /* Device control */ + + /** + * Takes appropriate actions for opening KNI device. + * @return true if the device was opened successfully, false if device is + * already opened, or KNI device configuration and startup failed + */ + bool open(); + /** + * @brief Close the KNI device. + * When device is closed it's not possible to work with it. + * Stops asynchronous packet capture if it is running. + */ + void close(); + + private: + struct rte_kni* m_Device; + struct rte_mempool* m_MBufMempool; + struct KniDeviceInfo { + LinuxNicInformationSocket soc; + KniLinkState link; + KniPromiscuousMode promisc; + uint16_t portId; + uint16_t mtu; + MacAddress mac; + std::string name; + + bool init(const KniDeviceConfiguration& conf); + } m_DeviceInfo; + struct KniThread; + struct KniCapturing { + OnKniPacketArriveCallback callback; + void* userCookie; + KniThread* thread; + + static void runCapture(void* devicePointer, std::atomic& stopThread); + inline bool isRunning() const { return thread != NULL; } + void cleanup(); + } m_Capturing; + struct KniRequests { + long sleepS; + long sleepNs; + KniThread* thread; + + static void runRequests(void* devicePointer, std::atomic& stopThread); + void cleanup(); + } m_Requests; +}; } // namespace pcpp diff --git a/Pcap++/header/KniDeviceList.h b/Pcap++/header/KniDeviceList.h index 51f4e09d43..962201f1ea 100644 --- a/Pcap++/header/KniDeviceList.h +++ b/Pcap++/header/KniDeviceList.h @@ -5,126 +5,133 @@ #include -#include "KniDevice.h" #include "DpdkDeviceList.h" +#include "KniDevice.h" /** * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - - /** - * @class KniDeviceList - * A singleton class that encapsulates DPDK KNI module initialization - * and holds the list of KniDevice instances. - * As it's a singleton, it has only one active instance doesn't have a public c'tor. - */ - class KniDeviceList - { - KniDeviceList(); - - /** - * @brief Explicit destruction of KNI device. - * After this call device is no longer available for external (by Linux) - * or internal (by application) usage. - * All threads running on this device are stopped (request and/or capturing). - * The device can no longer be found by it's name or id. - * @warning NOT MT SAFE - * @warning This method is forbidden as the result of discussion about packet memory pool - * lifetime made here. - * If You feel safe to use it please do, but no guarantee is provided. - * @param[in] kniDevice KNI device to be destroyed explicitly - */ - void destroyDevice(KniDevice* kniDevice); - public: - /** - * Callback related constants for KNI device - */ - enum KniCallbackVersion - { - /** Reports that DPDK supports only KniDevice#KniIoctlCallbacks callback structure */ - CALLBACKS_NEW = 0, - /** Reports that DPDK supports only KniDevice#KniOldIoctlCallbacks callback structure */ - CALLBACKS_OLD = 1 - }; - /** - * Various callback types supported by KNI device - */ - enum KniCallbackType - { - /** KniDevice#KniIoctlCallbacks#change_mtu and KniDevice#KniOldIoctlCallbacks#change_mtu callback */ - CALLBACK_MTU, - /** KniDevice#KniIoctlCallbacks#config_network_if and KniDevice#KniOldIoctlCallbacks#config_network_if callback */ - CALLBACK_LINK, - /** KniDevice#KniIoctlCallbacks#config_mac_address callback */ - CALLBACK_MAC, - /** KniDevice#KniIoctlCallbacks#config_promiscusity callback */ - CALLBACK_PROMISC - }; - - ~KniDeviceList(); - - /** - * @brief Getter for singleton - * @warning Initialization of Kni module depends on initialization of DPDK made by DpdkDeviceList - * @return The singleton instance of KniDeviceList - */ - static KniDeviceList& getInstance(); - - /** - * @return true if KNI module was initialized successfully false otherwise - */ - inline bool isInitialized() { return m_Initialized; } - - /* Device manipulation */ - - /** - * @brief Factory method for KNI devices. - * Newly created device is remembered under portId and name provided in config and can be found later by them. - * If KNI device is not destroyed explicitly thru KniDeviceList#destroyDevice - * then it will be destroyed implicitly by the time application exits. - * @warning NOT MT SAFE - * @param[in] config KNI device configuration structure - * @param[in] mempoolSize Size of packet mempool used by this device - * @return Pointer to new KNI device or NULL in case of error - */ - KniDevice* createDevice(const KniDevice::KniDeviceConfiguration& config, const size_t mempoolSize); - /** - * @brief Returns KNI device with specified portId. - * @note MT SAFE if createDevice or destroyDevice is not called concurrently - * @param[in] portId ID of KNI device to find - * @return Pointer to KNI device or NULL if device not found - */ - KniDevice* getDeviceByPort(const uint16_t portId); - /** - * @brief Returns KNI device with specified name. - * @note MT SAFE if createDevice or destroyDevice is not called concurrently - * @param[in] name Name of KNI device to find - * @return Pointer to KNI device or NULL if device not found - */ - KniDevice* getDeviceByName(const std::string& name); - - /* Static information */ - - /** - * Returns KniCallbackVersion#CALLBACKS_NEW or - * KniCallbackVersion#CALLBACKS_OLD based on DPDK version used - * @note MT SAFE - */ - static KniCallbackVersion callbackVersion(); - /** - * Returns true if provided callback type is supported by used DPDK version - * @note MT SAFE - * @param[in] cbType One of KniCallbackType enum values - */ - static bool isCallbackSupported(const KniCallbackType cbType); - private: - std::vector m_Devices; - bool m_Initialized; - int m_KniUniqueId; - }; +namespace pcpp { + +/** + * @class KniDeviceList + * A singleton class that encapsulates DPDK KNI module initialization + * and holds the list of KniDevice instances. + * As it's a singleton, it has only one active instance doesn't have a public + * c'tor. + */ +class KniDeviceList { + KniDeviceList(); + + /** + * @brief Explicit destruction of KNI device. + * After this call device is no longer available for external (by Linux) + * or internal (by application) usage. + * All threads running on this device are stopped (request and/or capturing). + * The device can no longer be found by it's name or id. + * @warning NOT MT SAFE + * @warning This method is forbidden as the result of discussion about packet + * memory pool lifetime made here. + * If You feel safe to use it please do, but no guarantee is provided. + * @param[in] kniDevice KNI device to be destroyed explicitly + */ + void destroyDevice(KniDevice* kniDevice); + + public: + /** + * Callback related constants for KNI device + */ + enum KniCallbackVersion { + /** Reports that DPDK supports only KniDevice#KniIoctlCallbacks callback + structure */ + CALLBACKS_NEW = 0, + /** Reports that DPDK supports only KniDevice#KniOldIoctlCallbacks callback + structure */ + CALLBACKS_OLD = 1 + }; + /** + * Various callback types supported by KNI device + */ + enum KniCallbackType { + /** KniDevice#KniIoctlCallbacks#change_mtu and + KniDevice#KniOldIoctlCallbacks#change_mtu callback */ + CALLBACK_MTU, + /** KniDevice#KniIoctlCallbacks#config_network_if and + KniDevice#KniOldIoctlCallbacks#config_network_if callback */ + CALLBACK_LINK, + /** KniDevice#KniIoctlCallbacks#config_mac_address callback */ + CALLBACK_MAC, + /** KniDevice#KniIoctlCallbacks#config_promiscusity callback */ + CALLBACK_PROMISC + }; + + ~KniDeviceList(); + + /** + * @brief Getter for singleton + * @warning Initialization of Kni module depends on initialization of DPDK + * made by DpdkDeviceList + * @return The singleton instance of KniDeviceList + */ + static KniDeviceList& getInstance(); + + /** + * @return true if KNI module was initialized successfully false otherwise + */ + inline bool isInitialized() { return m_Initialized; } + + /* Device manipulation */ + + /** + * @brief Factory method for KNI devices. + * Newly created device is remembered under portId and name provided in config + * and can be found later by them. If KNI device is not destroyed explicitly + * thru KniDeviceList#destroyDevice then it will be destroyed implicitly by + * the time application exits. + * @warning NOT MT SAFE + * @param[in] config KNI device configuration structure + * @param[in] mempoolSize Size of packet mempool used by this device + * @return Pointer to new KNI device or NULL in case of error + */ + KniDevice* createDevice(const KniDevice::KniDeviceConfiguration& config, + const size_t mempoolSize); + /** + * @brief Returns KNI device with specified portId. + * @note MT SAFE if createDevice or destroyDevice is not called concurrently + * @param[in] portId ID of KNI device to find + * @return Pointer to KNI device or NULL if device not found + */ + KniDevice* getDeviceByPort(const uint16_t portId); + /** + * @brief Returns KNI device with specified name. + * @note MT SAFE if createDevice or destroyDevice is not called concurrently + * @param[in] name Name of KNI device to find + * @return Pointer to KNI device or NULL if device not found + */ + KniDevice* getDeviceByName(const std::string& name); + + /* Static information */ + + /** + * Returns KniCallbackVersion#CALLBACKS_NEW or + * KniCallbackVersion#CALLBACKS_OLD based on DPDK version used + * @note MT SAFE + */ + static KniCallbackVersion callbackVersion(); + /** + * Returns true if provided callback type is supported by used DPDK version + * @note MT SAFE + * @param[in] cbType One of KniCallbackType enum values + */ + static bool isCallbackSupported(const KniCallbackType cbType); + + private: + std::vector m_Devices; + bool m_Initialized; + int m_KniUniqueId; +}; } // namespace pcpp // GCOVR_EXCL_STOP diff --git a/Pcap++/header/LinuxNicInformationSocket.h b/Pcap++/header/LinuxNicInformationSocket.h index eb55979c4b..56d02dbe8c 100644 --- a/Pcap++/header/LinuxNicInformationSocket.h +++ b/Pcap++/header/LinuxNicInformationSocket.h @@ -7,68 +7,73 @@ struct ifreq; * \namespace pcpp * \brief The main namespace for the PcapPlusPlus lib */ -namespace pcpp -{ - /** - * @class LinuxNicInformationSocket - * Simple wrapper over Linux socket for making the information - * requests about NICs or making some changes in NICs setup. - * All possible requests are described in - * netdevice(7). - * The instance of this class handles underlying socket during its lifetime - * and takes an appropriate actions to close socket on destruction. - * The user must call LinuxNicInformationSocket#makeRequest method with - * known ioctl type and properly filled ifreq structure for this ioctl type. - * Filling of ifr_name may be omitted as it will be done automatically from - * provided NIC name. - * @note Usage of this class requires the inclusion of and Linux headers - */ - class LinuxNicInformationSocket - { - public: - /** - * Simple type rename for convenience - */ - typedef int LinuxSocket; - /** - * Simple type rename for convenience - */ - typedef unsigned long IoctlType; +namespace pcpp { +/** + * @class LinuxNicInformationSocket + * Simple wrapper over Linux socket for making the information + * requests about NICs or making some changes in NICs setup. + * All possible requests are described in + * netdevice(7). + * The instance of this class handles underlying socket during its lifetime + * and takes an appropriate actions to close socket on destruction. + * The user must call LinuxNicInformationSocket#makeRequest method with + * known ioctl type and properly filled ifreq structure for this ioctl type. + * Filling of ifr_name may be omitted as it will be done automatically from + * provided NIC name. + * @note Usage of this class requires the inclusion of and + * Linux headers + */ +class LinuxNicInformationSocket { + public: + /** + * Simple type rename for convenience + */ + typedef int LinuxSocket; + /** + * Simple type rename for convenience + */ + typedef unsigned long IoctlType; + + /** + * Tries to open handled socket on construction. + * If fails prints the debug message + */ + LinuxNicInformationSocket(); + /** + * Closes handled socket on destruction. + * If no socket was opened prints the debug message + */ + ~LinuxNicInformationSocket(); - /** - * Tries to open handled socket on construction. - * If fails prints the debug message - */ - LinuxNicInformationSocket(); - /** - * Closes handled socket on destruction. - * If no socket was opened prints the debug message - */ - ~LinuxNicInformationSocket(); + /** + * @brief Makes request to socket. + * Firstly tries to open socket if it is not opened. + * Then makes an ioctl(2) request to handled socket with provided request + * structure. See: netdevice(7) + * for description of possible values of ioctlType and content of request. + * @note User have no need to fill ifr_name field of request. It will be + * filled automatically from provided nicName argument. + * @param[in] nicName Name of internet controller as displayed by Linux + * @param[in] ioctlType Value of ioctl to make + * @param[in,out] request Pointer to ifreq structure that contains some + * information or will be used for obtaining the information (depends on + * ioctlType) + * @return false if request was not made or socket can't be opened otherwise + * true + * @warning For some types of requests to succeed You need to be a root + * or have the CAP_NET_ADMIN capability. + */ + bool makeRequest(const char* nicName, const IoctlType ioctlType, + ifreq* request); - /** - * @brief Makes request to socket. - * Firstly tries to open socket if it is not opened. - * Then makes an ioctl(2) request to handled socket with provided request structure. - * See: netdevice(7) - * for description of possible values of ioctlType and content of request. - * @note User have no need to fill ifr_name field of request. It will be filled - * automatically from provided nicName argument. - * @param[in] nicName Name of internet controller as displayed by Linux - * @param[in] ioctlType Value of ioctl to make - * @param[in,out] request Pointer to ifreq structure that contains some information - * or will be used for obtaining the information (depends on ioctlType) - * @return false if request was not made or socket can't be opened otherwise true - * @warning For some types of requests to succeed You need to be a root - * or have the CAP_NET_ADMIN capability. - */ - bool makeRequest(const char* nicName, const IoctlType ioctlType, ifreq* request); - private: - /* Hidden copy constructor. This structure is not copyable */ - LinuxNicInformationSocket(const LinuxNicInformationSocket&); - /* Hidden copy assignment operator. This structure is not copyable */ - LinuxNicInformationSocket operator=(const LinuxNicInformationSocket&); - LinuxSocket m_Socket; - }; + private: + /* Hidden copy constructor. This structure is not copyable */ + LinuxNicInformationSocket(const LinuxNicInformationSocket&); + /* Hidden copy assignment operator. This structure is not copyable */ + LinuxNicInformationSocket operator=(const LinuxNicInformationSocket&); + LinuxSocket m_Socket; +}; } // namespace pcpp #endif /* PCAPPP_LINUX_NIC_INFORMATION_SOCKET */ diff --git a/Pcap++/header/MBufRawPacket.h b/Pcap++/header/MBufRawPacket.h index aa66ed5a8b..69ea3846d2 100644 --- a/Pcap++/header/MBufRawPacket.h +++ b/Pcap++/header/MBufRawPacket.h @@ -3,227 +3,279 @@ // GCOVR_EXCL_START -#include #include "Packet.h" #include "PointerVector.h" +#include struct rte_mbuf; struct rte_mempool; /** -* \namespace pcpp -* \brief The main namespace for the PcapPlusPlus lib -*/ -namespace pcpp -{ + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp { - class DpdkDevice; +class DpdkDevice; #ifdef USE_DPDK_KNI - class KniDevice; +class KniDevice; #endif - #define MBUFRAWPACKET_OBJECT_TYPE 1 - - /** - * @class MBufRawPacket - * A class that inherits RawPacket and wraps DPDK's mbuf object (see some info about mbuf in DpdkDevice.h) but is - * compatible with PcapPlusPlus framework. Using MBufRawPacket is be almost similar to using RawPacket, the implementation - * differences are encapsulated in the class implementation. For example: user can create and manipulate a Packet object from - * MBufRawPacket the same way it is done with RawPacket; User can use PcapFileWriterDevice to save MBufRawPacket to pcap the - * same way it's used with RawPacket; etc.
- * The main difference is that RawPacket contains a pointer to the data itself and MBufRawPacket is holding a pointer to an mbuf - * object which contains a pointer to the data. This implies that MBufRawPacket without an mbuf allocated to it is not usable. - * Getting instances of MBufRawPacket can be done in one to the following ways: - * - Receiving packets from DpdkDevice. In this case DpdkDevice takes care of getting the mbuf from DPDK and wrapping it with - * MBufRawPacket - * - Creating MBufRawPacket from scratch (in order to send it with DpdkDevice, for example). In this case the user should call - * the init() method after constructing the object in order to allocate a new mbuf from DPDK port pool (encapsulated by DpdkDevice) - * - * Limitations of this class: - * - Currently chained mbufs are not supported. An mbuf has the capability to be linked to another mbuf and create a linked list - * of mbufs. This is good for Jumbo packets or other uses. MBufRawPacket doesn't support this capability so there is no way to - * access the mbufs linked to the mbuf wrapped by MBufRawPacket instance. I hope I'll be able to add this support in the future - */ - class MBufRawPacket : public RawPacket - { - friend class DpdkDevice; +#define MBUFRAWPACKET_OBJECT_TYPE 1 + +/** + * @class MBufRawPacket + * A class that inherits RawPacket and wraps DPDK's mbuf object (see some info + * about mbuf in DpdkDevice.h) but is compatible with PcapPlusPlus framework. + * Using MBufRawPacket is be almost similar to using RawPacket, the + * implementation differences are encapsulated in the class implementation. For + * example: user can create and manipulate a Packet object from MBufRawPacket + * the same way it is done with RawPacket; User can use PcapFileWriterDevice to + * save MBufRawPacket to pcap the same way it's used with RawPacket; etc.
+ * The main difference is that RawPacket contains a pointer to the data itself + * and MBufRawPacket is holding a pointer to an mbuf object which contains a + * pointer to the data. This implies that MBufRawPacket without an mbuf + * allocated to it is not usable. Getting instances of MBufRawPacket can be done + * in one to the following ways: + * - Receiving packets from DpdkDevice. In this case DpdkDevice takes care of + * getting the mbuf from DPDK and wrapping it with MBufRawPacket + * - Creating MBufRawPacket from scratch (in order to send it with + * DpdkDevice, for example). In this case the user should call the init() method + * after constructing the object in order to allocate a new mbuf from DPDK port + * pool (encapsulated by DpdkDevice) + * + * Limitations of this class: + * - Currently chained mbufs are not supported. An mbuf has the capability to + * be linked to another mbuf and create a linked list of mbufs. This is good for + * Jumbo packets or other uses. MBufRawPacket doesn't support this capability so + * there is no way to access the mbufs linked to the mbuf wrapped by + * MBufRawPacket instance. I hope I'll be able to add this support in the future + */ +class MBufRawPacket : public RawPacket { + friend class DpdkDevice; #ifdef USE_DPDK_KNI - friend class KniDevice; + friend class KniDevice; #endif - static const int MBUF_DATA_SIZE; - - protected: - struct rte_mbuf* m_MBuf; - struct rte_mempool* m_Mempool; - bool m_FreeMbuf; - - void setMBuf(struct rte_mbuf* mBuf, timespec timestamp); - bool init(struct rte_mempool* mempool); - bool initFromRawPacket(const RawPacket* rawPacket, struct rte_mempool* mempool); - public: - - /** - * A default c'tor for this class. Constructs an instance of this class without an mbuf attached to it. In order to allocate - * an mbuf the user should call the init() method. Without calling init() the instance of this class is not usable. - * This c'tor can be used for initializing an array of MBufRawPacket (which requires an empty c'tor) - */ - MBufRawPacket() : RawPacket(), m_MBuf(NULL), m_Mempool(NULL), m_FreeMbuf(true) { m_DeleteRawDataAtDestructor = false; } - - /** - * A d'tor for this class. Once called it frees the mbuf attached to it (returning it back to the mbuf pool it was allocated from) - */ - virtual ~MBufRawPacket(); - - /** - * A copy c'tor for this class. The copy c'tor allocates a new mbuf from the same pool the original mbuf was - * allocated from, attaches the new mbuf to this instance of MBufRawPacket and copies the data from the original mbuf - * to the new mbuf - * @param[in] other The MBufRawPacket instance to copy from - */ - MBufRawPacket(const MBufRawPacket& other); - - /** - * @brief Initialize an instance of this class from DpdkDevice. - * Initialization includes allocating an mbuf from the pool that resides in DpdkDevice. - * The user should call this method only once per instance. - * Calling it more than once will result with an error - * @param[in] device The DpdkDevice which has the pool to allocate the mbuf from - * @return True if initialization succeeded and false if this method was already called for this instance (and an mbuf is - * already attached) or if allocating an mbuf from the pool failed for some reason - */ - bool init(DpdkDevice* device); + static const int MBUF_DATA_SIZE; + + protected: + struct rte_mbuf* m_MBuf; + struct rte_mempool* m_Mempool; + bool m_FreeMbuf; + + void setMBuf(struct rte_mbuf* mBuf, timespec timestamp); + bool init(struct rte_mempool* mempool); + bool initFromRawPacket(const RawPacket* rawPacket, + struct rte_mempool* mempool); + + public: + /** + * A default c'tor for this class. Constructs an instance of this class + * without an mbuf attached to it. In order to allocate an mbuf the user + * should call the init() method. Without calling init() the instance of this + * class is not usable. This c'tor can be used for initializing an array of + * MBufRawPacket (which requires an empty c'tor) + */ + MBufRawPacket() + : RawPacket(), m_MBuf(NULL), m_Mempool(NULL), m_FreeMbuf(true) { + m_DeleteRawDataAtDestructor = false; + } + + /** + * A d'tor for this class. Once called it frees the mbuf attached to it + * (returning it back to the mbuf pool it was allocated from) + */ + virtual ~MBufRawPacket(); + + /** + * A copy c'tor for this class. The copy c'tor allocates a new mbuf from the + * same pool the original mbuf was allocated from, attaches the new mbuf to + * this instance of MBufRawPacket and copies the data from the original mbuf + * to the new mbuf + * @param[in] other The MBufRawPacket instance to copy from + */ + MBufRawPacket(const MBufRawPacket& other); + + /** + * @brief Initialize an instance of this class from DpdkDevice. + * Initialization includes allocating an mbuf from the pool that resides in + * DpdkDevice. The user should call this method only once per instance. + * Calling it more than once will result with an error + * @param[in] device The DpdkDevice which has the pool to allocate the mbuf + * from + * @return True if initialization succeeded and false if this method was + * already called for this instance (and an mbuf is already attached) or if + * allocating an mbuf from the pool failed for some reason + */ + bool init(DpdkDevice* device); #ifdef USE_DPDK_KNI - /** - * @brief Initialize an instance of this class from KniDevice. - * Initialization includes allocating an mbuf from the pool that resides in KniDevice. - * The user should call this method only once per instance. - * Calling it more than once will result with an error - * @param[in] device The KniDevice which has the pool to allocate the mbuf from - * @return True if initialization succeeded and false if this method was already called for this instance (and an mbuf is - * already attached) or if allocating an mbuf from the pool failed for some reason - */ - bool init(KniDevice* device); + /** + * @brief Initialize an instance of this class from KniDevice. + * Initialization includes allocating an mbuf from the pool that resides in + * KniDevice. The user should call this method only once per instance. Calling + * it more than once will result with an error + * @param[in] device The KniDevice which has the pool to allocate the mbuf + * from + * @return True if initialization succeeded and false if this method was + * already called for this instance (and an mbuf is already attached) or if + * allocating an mbuf from the pool failed for some reason + */ + bool init(KniDevice* device); #endif - /** - * @brief Initialize an instance of this class and copies the content of a RawPacket object. - * Initialization includes allocating an mbuf from the pool that resides in provided DpdkDevice, - * and copying the data from the input RawPacket object into this mBuf. - * The user should call this method only once per instance. - * Calling it more than once will result with an error - * @param[in] rawPacket A pointer to a RawPacket object from which data will be copied - * @param[in] device The DpdkDevice which has the pool to allocate the mbuf from - * @return True if initialization succeeded and false if this method was already called for this instance (and an mbuf is - * already attached) or if allocating an mbuf from the pool failed for some reason - */ - bool initFromRawPacket(const RawPacket* rawPacket, DpdkDevice* device); + /** + * @brief Initialize an instance of this class and copies the content of a + * RawPacket object. Initialization includes allocating an mbuf from the pool + * that resides in provided DpdkDevice, and copying the data from the input + * RawPacket object into this mBuf. The user should call this method only once + * per instance. Calling it more than once will result with an error + * @param[in] rawPacket A pointer to a RawPacket object from which data will + * be copied + * @param[in] device The DpdkDevice which has the pool to allocate the mbuf + * from + * @return True if initialization succeeded and false if this method was + * already called for this instance (and an mbuf is already attached) or if + * allocating an mbuf from the pool failed for some reason + */ + bool initFromRawPacket(const RawPacket* rawPacket, DpdkDevice* device); #ifdef USE_DPDK_KNI - /** - * @brief Initialize an instance of this class and copies the content of a RawPacket object. - * Initialization includes allocating an mbuf from the pool that resides in provided KniDevice, - * and copying the data from the input RawPacket object into this mBuf. - * The user should call this method only once per instance. - * Calling it more than once will result with an error - * @param[in] rawPacket A pointer to a RawPacket object from which data will be copied - * @param[in] device The KniDevice which has the pool to allocate the mbuf from - * @return True if initialization succeeded and false if this method was already called for this instance (and an mbuf is - * already attached) or if allocating an mbuf from the pool failed for some reason - */ - bool initFromRawPacket(const RawPacket* rawPacket, KniDevice* device); + /** + * @brief Initialize an instance of this class and copies the content of a + * RawPacket object. Initialization includes allocating an mbuf from the pool + * that resides in provided KniDevice, and copying the data from the input + * RawPacket object into this mBuf. The user should call this method only once + * per instance. Calling it more than once will result with an error + * @param[in] rawPacket A pointer to a RawPacket object from which data will + * be copied + * @param[in] device The KniDevice which has the pool to allocate the mbuf + * from + * @return True if initialization succeeded and false if this method was + * already called for this instance (and an mbuf is already attached) or if + * allocating an mbuf from the pool failed for some reason + */ + bool initFromRawPacket(const RawPacket* rawPacket, KniDevice* device); #endif - /** - * @return A pointer to the DPDK mbuf stored in this object - */ - inline rte_mbuf* getMBuf() { return m_MBuf; } - - // overridden methods - - /** - * @return MBufRawPacket object type - */ - virtual inline uint8_t getObjectType() const { return MBUFRAWPACKET_OBJECT_TYPE; } - - /** - * An assignment operator for this class. Copies the data from the mbuf attached to the other MBufRawPacket to the mbuf - * attached to this instance. If instance is not initialized (meaning no mbuf is attached) nothing will be copied and - * instance will remain uninitialized (also, an error will be printed) - * @param[in] other The MBufRawPacket to assign data from - */ - MBufRawPacket& operator=(const MBufRawPacket& other); - - /** - * Set raw data to the mbuf by copying the data to it. In order to stay compatible with the ancestor method - * which takes control of the data pointer and frees it when RawPacket is destroyed, this method frees this pointer right away after - * data is copied to the mbuf. So when using this method please notice that after it's called pRawData memory is free, don't - * use this pointer again. In addition, if raw packet isn't initialized (mbuf is NULL), this method will call the init() method - * @param[in] pRawData A pointer to the new raw data - * @param[in] rawDataLen The new raw data length in bytes - * @param[in] timestamp The timestamp packet was received by the NIC - * @param[in] layerType The link layer type for this raw data. Default is Ethernet - * @param[in] frameLength When reading from pcap files, sometimes the captured length is different from the actual packet length. This parameter represents the packet - * length. This parameter is optional, if not set or set to -1 it is assumed both lengths are equal - * @return True if raw data was copied to the mbuf successfully, false if rawDataLen is larger than mbuf max size, if initialization - * failed or if copying the data to the mbuf failed. In all of these cases an error will be printed to log - */ - bool setRawData(const uint8_t* pRawData, int rawDataLen, timespec timestamp, LinkLayerType layerType = LINKTYPE_ETHERNET, int frameLength = -1); - - /** - * Clears the object and frees the mbuf - */ - void clear(); - - /** - * Append packet data at the end of current data. This method uses the same mbuf already allocated and tries to append more space and - * copy the data to it. If MBufRawPacket is not initialize (mbuf is NULL) or mbuf append failed an error is printed to log - * @param[in] dataToAppend A pointer to the data to append - * @param[in] dataToAppendLen Length in bytes of dataToAppend - */ - void appendData(const uint8_t* dataToAppend, size_t dataToAppendLen); - - /** - * Insert raw data at some index of the current data and shift the remaining data to the end. This method uses the - * same mbuf already allocated and tries to append more space to it. Then it just copies dataToAppend at the relevant index and shifts - * the remaining data to the end. If MBufRawPacket is not initialize (mbuf is NULL) or mbuf append failed an error is printed to log - * @param[in] atIndex The index to insert the new data to - * @param[in] dataToInsert A pointer to the new data to insert - * @param[in] dataToInsertLen Length in bytes of dataToInsert - */ - void insertData(int atIndex, const uint8_t* dataToInsert, size_t dataToInsertLen); - - /** - * Remove certain number of bytes from current raw data buffer. All data after the removed bytes will be shifted back. This method - * uses the mbuf already allocated and tries to trim space from it - * @param[in] atIndex The index to start removing bytes from - * @param[in] numOfBytesToRemove Number of bytes to remove - * @return True if all bytes were removed successfully, or false if MBufRawPacket is not initialize (mbuf is NULL), mbuf trim - * failed or logatIndex+numOfBytesToRemove is out-of-bounds of the raw data buffer. In all of these cases an error is printed to log - */ - bool removeData(int atIndex, size_t numOfBytesToRemove); - - /** - * This overridden method,in contrast to its ancestor RawPacket#reallocateData() doesn't need to do anything because mbuf is already - * allocated to its maximum extent. So it only performs a check to verify the size after re-allocation doesn't exceed mbuf max size - * @param[in] newBufferLength The new buffer length as required by the user - * @return True if new size is larger than current size but smaller than mbuf max size, false otherwise - */ - bool reallocateData(size_t newBufferLength); - - /** - * Set an indication whether to free the mbuf when done using it or not ("done using it" means setting another mbuf or class d'tor). - * Default value is true. - * @param[in] val The value to set. True means free the mbuf when done using it. Default it True - */ - inline void setFreeMbuf(bool val = true) { m_FreeMbuf = val; } - }; - - /** - * @typedef MBufRawPacketVector - * A vector of pointers to MBufRawPacket - */ - typedef PointerVector MBufRawPacketVector; + /** + * @return A pointer to the DPDK mbuf stored in this object + */ + inline rte_mbuf* getMBuf() { return m_MBuf; } + + // overridden methods + + /** + * @return MBufRawPacket object type + */ + virtual inline uint8_t getObjectType() const { + return MBUFRAWPACKET_OBJECT_TYPE; + } + + /** + * An assignment operator for this class. Copies the data from the mbuf + * attached to the other MBufRawPacket to the mbuf attached to this instance. + * If instance is not initialized (meaning no mbuf is attached) nothing will + * be copied and instance will remain uninitialized (also, an error will be + * printed) + * @param[in] other The MBufRawPacket to assign data from + */ + MBufRawPacket& operator=(const MBufRawPacket& other); + + /** + * Set raw data to the mbuf by copying the data to it. In order to stay + * compatible with the ancestor method which takes control of the data pointer + * and frees it when RawPacket is destroyed, this method frees this pointer + * right away after data is copied to the mbuf. So when using this method + * please notice that after it's called pRawData memory is free, don't use + * this pointer again. In addition, if raw packet isn't initialized (mbuf is + * NULL), this method will call the init() method + * @param[in] pRawData A pointer to the new raw data + * @param[in] rawDataLen The new raw data length in bytes + * @param[in] timestamp The timestamp packet was received by the NIC + * @param[in] layerType The link layer type for this raw data. Default is + * Ethernet + * @param[in] frameLength When reading from pcap files, sometimes the captured + * length is different from the actual packet length. This parameter + * represents the packet length. This parameter is optional, if not set or set + * to -1 it is assumed both lengths are equal + * @return True if raw data was copied to the mbuf successfully, false if + * rawDataLen is larger than mbuf max size, if initialization failed or if + * copying the data to the mbuf failed. In all of these cases an error will be + * printed to log + */ + bool setRawData(const uint8_t* pRawData, int rawDataLen, timespec timestamp, + LinkLayerType layerType = LINKTYPE_ETHERNET, + int frameLength = -1); + + /** + * Clears the object and frees the mbuf + */ + void clear(); + + /** + * Append packet data at the end of current data. This method uses the same + * mbuf already allocated and tries to append more space and copy the data to + * it. If MBufRawPacket is not initialize (mbuf is NULL) or mbuf append failed + * an error is printed to log + * @param[in] dataToAppend A pointer to the data to append + * @param[in] dataToAppendLen Length in bytes of dataToAppend + */ + void appendData(const uint8_t* dataToAppend, size_t dataToAppendLen); + + /** + * Insert raw data at some index of the current data and shift the remaining + * data to the end. This method uses the same mbuf already allocated and tries + * to append more space to it. Then it just copies dataToAppend at the + * relevant index and shifts the remaining data to the end. If MBufRawPacket + * is not initialize (mbuf is NULL) or mbuf append failed an error is printed + * to log + * @param[in] atIndex The index to insert the new data to + * @param[in] dataToInsert A pointer to the new data to insert + * @param[in] dataToInsertLen Length in bytes of dataToInsert + */ + void insertData(int atIndex, const uint8_t* dataToInsert, + size_t dataToInsertLen); + + /** + * Remove certain number of bytes from current raw data buffer. All data after + * the removed bytes will be shifted back. This method uses the mbuf already + * allocated and tries to trim space from it + * @param[in] atIndex The index to start removing bytes from + * @param[in] numOfBytesToRemove Number of bytes to remove + * @return True if all bytes were removed successfully, or false if + * MBufRawPacket is not initialize (mbuf is NULL), mbuf trim failed or + * logatIndex+numOfBytesToRemove is out-of-bounds of the raw data buffer. In + * all of these cases an error is printed to log + */ + bool removeData(int atIndex, size_t numOfBytesToRemove); + + /** + * This overridden method,in contrast to its ancestor + * RawPacket#reallocateData() doesn't need to do anything because mbuf is + * already allocated to its maximum extent. So it only performs a check to + * verify the size after re-allocation doesn't exceed mbuf max size + * @param[in] newBufferLength The new buffer length as required by the user + * @return True if new size is larger than current size but smaller than mbuf + * max size, false otherwise + */ + bool reallocateData(size_t newBufferLength); + + /** + * Set an indication whether to free the mbuf when done using it or not ("done + * using it" means setting another mbuf or class d'tor). Default value is + * true. + * @param[in] val The value to set. True means free the mbuf when done using + * it. Default it True + */ + inline void setFreeMbuf(bool val = true) { m_FreeMbuf = val; } +}; + +/** + * @typedef MBufRawPacketVector + * A vector of pointers to MBufRawPacket + */ +typedef PointerVector MBufRawPacketVector; } // namespace pcpp diff --git a/Pcap++/header/NetworkUtils.h b/Pcap++/header/NetworkUtils.h index edb0cc09ad..36c3dc884a 100644 --- a/Pcap++/header/NetworkUtils.h +++ b/Pcap++/header/NetworkUtils.h @@ -1,89 +1,105 @@ #ifndef PCAPPP_NETWORK_UTILS #define PCAPPP_NETWORK_UTILS -#include "MacAddress.h" #include "IpAddress.h" +#include "MacAddress.h" #include "PcapLiveDevice.h" - /// @file /** -* \namespace pcpp -* \brief The main namespace for the PcapPlusPlus lib -*/ -namespace pcpp -{ - - /** - * @class NetworkUtils - * This class bundles several network utilities that are very common and useful. These utilities use Pcap++ and Packet++ packet - * crafting and processing capabilities. This class is a singleton and can be access by getInstance() only - */ - class NetworkUtils - { - public: + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp { - /** - * The access method to the singleton - * @return The singleton instance of this class - */ - static NetworkUtils& getInstance() - { - static NetworkUtils instance; - return instance; - } - - /** - * Default timeout used for several utilities. Currently set to 5 seconds - */ - static const int DefaultTimeout; - - /** - * Resolve the MAC address for a given IPv4 address. It's done using the ARP protocol: send an ARP request and interpret the response - * @param[in] ipAddr The IPv4 address to resolve MAC address to - * @param[in] device The interface to send and receive the ARP packets on - * @param[out] arpResponseTimeMS An output parameter that will contain the time in milliseconds that took the ARP response to arrive - * @param[in] sourceMac An optional parameter to set the source MAC address that will be sent with the ARP request - * if this parameter isn't set or set with MacAddress#Zero the MAC address of the interface will be used - * @param[in] sourceIP An optional parameter to set the source IPv4 address that will be sent with the ARP request - * if this parameter isn't set or set with IPv4Address#Zero the default IPv4 address of the interface will be used - * @param[in] arpTimeout An optional parameter to set the timeout to wait for the ARP response to return. - * If this parameter isn't set or set with a number smaller than 0, a default timeout of 5 seconds will be set - * @return The resolved MAC address or MacAddress#Zero if an error occurred or address could not be resolved. Errors will be printed - * to log - */ - MacAddress getMacAddress(IPv4Address ipAddr, PcapLiveDevice* device, double& arpResponseTimeMS, - MacAddress sourceMac = MacAddress::Zero, IPv4Address sourceIP = IPv4Address::Zero, int arpTimeout = -1) const; +/** + * @class NetworkUtils + * This class bundles several network utilities that are very common and useful. + * These utilities use Pcap++ and Packet++ packet crafting and processing + * capabilities. This class is a singleton and can be access by getInstance() + * only + */ +class NetworkUtils { + public: + /** + * The access method to the singleton + * @return The singleton instance of this class + */ + static NetworkUtils& getInstance() { + static NetworkUtils instance; + return instance; + } + /** + * Default timeout used for several utilities. Currently set to 5 seconds + */ + static const int DefaultTimeout; - /** - * Resolve an IPv4 address for a given hostname. Resolving is done in multiple phases: first resolving the LAN gateway MAC address - * (or default gateway if a gateway isn't provided) using ARP protocol (by using NetworkUtils#getMacAddress() ). Then a DNS request - * is sent to a DNS server (if specified) or to the LAN gateway (if DNS server is not specified). The DNS response is decoded and - * the IPv4 address is determined. In addition the method outputs the time it took the DNS response to arrive and the DNS TTL - * written on the DNS response. If DNS response doesn't contain an IPv4 address resolving an IPv4Address#Zero will be returned. - * @param[in] hostname The hostname to resolve - * @param[in] device The interface to send and receive packets on - * @param[out] dnsResponseTimeMS When method returns successfully will contain the time it took to receive the DNS response - * (in milli-seconds) - * @param[out] dnsTTL When method returns successfully will contain The DNS TTL written in the DNS response - * @param[in] dnsTimeout An optional parameter to specify the timeout to wait for a DNS response. If not specified the default timeout - * is 5 sec - * @param[in] dnsServerIP An optional parameter to specify the DNS server IP to send the DNS request to. If not specified - * or specified with IPv4Address#Zero the DNS request will be sent to the default DNS server configured in the system - * @param[in] gatewayIP An optional parameter to specify the LAN gateway to send the DNS request through. If not specified - * or specified with IPv4Address#Zero the interface's default gateway will be used - * @return The resolved IPv4 address or IPv4Address#Zero if something went wrong (in this case an error will be printed to log) - */ - IPv4Address getIPv4Address(const std::string& hostname, PcapLiveDevice* device, double& dnsResponseTimeMS, uint32_t& dnsTTL, - int dnsTimeout = -1, IPv4Address dnsServerIP = IPv4Address::Zero, IPv4Address gatewayIP = IPv4Address::Zero) const; + /** + * Resolve the MAC address for a given IPv4 address. It's done using the ARP + * protocol: send an ARP request and interpret the response + * @param[in] ipAddr The IPv4 address to resolve MAC address to + * @param[in] device The interface to send and receive the ARP packets on + * @param[out] arpResponseTimeMS An output parameter that will contain the + * time in milliseconds that took the ARP response to arrive + * @param[in] sourceMac An optional parameter to set the source MAC address + * that will be sent with the ARP request if this parameter isn't set or set + * with MacAddress#Zero the MAC address of the interface will be used + * @param[in] sourceIP An optional parameter to set the source IPv4 address + * that will be sent with the ARP request if this parameter isn't set or set + * with IPv4Address#Zero the default IPv4 address of the interface will be + * used + * @param[in] arpTimeout An optional parameter to set the timeout to wait for + * the ARP response to return. If this parameter isn't set or set with a + * number smaller than 0, a default timeout of 5 seconds will be set + * @return The resolved MAC address or MacAddress#Zero if an error occurred or + * address could not be resolved. Errors will be printed to log + */ + MacAddress getMacAddress(IPv4Address ipAddr, PcapLiveDevice* device, + double& arpResponseTimeMS, + MacAddress sourceMac = MacAddress::Zero, + IPv4Address sourceIP = IPv4Address::Zero, + int arpTimeout = -1) const; - private: + /** + * Resolve an IPv4 address for a given hostname. Resolving is done in multiple + * phases: first resolving the LAN gateway MAC address (or default gateway if + * a gateway isn't provided) using ARP protocol (by using + * NetworkUtils#getMacAddress() ). Then a DNS request is sent to a DNS server + * (if specified) or to the LAN gateway (if DNS server is not specified). The + * DNS response is decoded and the IPv4 address is determined. In addition the + * method outputs the time it took the DNS response to arrive and the DNS TTL + * written on the DNS response. If DNS response doesn't contain an IPv4 + * address resolving an IPv4Address#Zero will be returned. + * @param[in] hostname The hostname to resolve + * @param[in] device The interface to send and receive packets on + * @param[out] dnsResponseTimeMS When method returns successfully will contain + * the time it took to receive the DNS response (in milli-seconds) + * @param[out] dnsTTL When method returns successfully will contain The DNS + * TTL written in the DNS response + * @param[in] dnsTimeout An optional parameter to specify the timeout to wait + * for a DNS response. If not specified the default timeout is 5 sec + * @param[in] dnsServerIP An optional parameter to specify the DNS server IP + * to send the DNS request to. If not specified or specified with + * IPv4Address#Zero the DNS request will be sent to the default DNS server + * configured in the system + * @param[in] gatewayIP An optional parameter to specify the LAN gateway to + * send the DNS request through. If not specified or specified with + * IPv4Address#Zero the interface's default gateway will be used + * @return The resolved IPv4 address or IPv4Address#Zero if something went + * wrong (in this case an error will be printed to log) + */ + IPv4Address getIPv4Address(const std::string& hostname, + PcapLiveDevice* device, double& dnsResponseTimeMS, + uint32_t& dnsTTL, int dnsTimeout = -1, + IPv4Address dnsServerIP = IPv4Address::Zero, + IPv4Address gatewayIP = IPv4Address::Zero) const; - // private c'tor - NetworkUtils() {} - }; + private: + // private c'tor + NetworkUtils() {} +}; } // namespace pcpp diff --git a/Pcap++/header/PcapDevice.h b/Pcap++/header/PcapDevice.h index 0471472ebb..34ffb3842d 100644 --- a/Pcap++/header/PcapDevice.h +++ b/Pcap++/header/PcapDevice.h @@ -11,88 +11,90 @@ struct pcap_pkthdr; /// @file /** -* \namespace pcpp -* \brief The main namespace for the PcapPlusPlus lib -*/ -namespace pcpp -{ - //Forward Declaration - required for IPcapDevice::matchPacketWithFilter - class GeneralFilter; - - /** - * @class IPcapDevice - * An abstract class representing all libpcap-based packet capturing devices: files, libPcap, WinPcap/Npcap and RemoteCapture. - * This class is abstract and cannot be instantiated - */ - class IPcapDevice : public IDevice, public IFilterableDevice - { - protected: - pcap_t* m_PcapDescriptor; - - // c'tor should not be public - IPcapDevice() : IDevice() { m_PcapDescriptor = NULL; } - - public: - - /** - * @struct PcapStats - * A container for pcap device statistics - */ - struct PcapStats - { - /** Number of packets received */ - uint64_t packetsRecv; - /** Number of packets dropped */ - uint64_t packetsDrop; - /** number of packets dropped by interface (not supported on all platforms) */ - uint64_t packetsDropByInterface; - }; - - - virtual ~IPcapDevice(); - - /** - * Get statistics from the device - * @param[out] stats An object containing the stats - */ - virtual void getStatistics(PcapStats& stats) const = 0; - - /** - * A static method for retrieving pcap lib (libpcap/WinPcap/etc.) version information. This method is actually - * a wrapper for [pcap_lib_version()](https://www.tcpdump.org/manpages/pcap_lib_version.3pcap.html) - * @return A string containing the pcap lib version information - */ - static std::string getPcapLibVersionInfo(); - - /** - * Match a raw packet with a given BPF filter. Notice this method is static which means you don't need any device instance - * in order to perform this match - * @param[in] filter A filter class to test against - * @param[in] rawPacket A pointer to the raw packet to match the filter with - * @return True if raw packet matches the filter or false otherwise - */ - static bool matchPacketWithFilter(GeneralFilter& filter, RawPacket* rawPacket); - - - // implement abstract methods - - using IFilterableDevice::setFilter; - - /** - * Set a filter for the device. When implemented by the device, only packets that match the filter will be received. - * Please note that when the device is closed the filter is reset so when reopening the device you need to call this - * method again in order to reactivate the filter - * @param[in] filterAsString The filter to be set in Berkeley Packet Filter (BPF) syntax (http://biot.com/capstats/bpf.html) - * @return True if filter set successfully, false otherwise - */ - virtual bool setFilter(std::string filterAsString); - - /** - * Clear the filter currently set on device - * @return True if filter was removed successfully or if no filter was set, false otherwise - */ - bool clearFilter(); - }; + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp { +// Forward Declaration - required for IPcapDevice::matchPacketWithFilter +class GeneralFilter; + +/** + * @class IPcapDevice + * An abstract class representing all libpcap-based packet capturing devices: + * files, libPcap, WinPcap/Npcap and RemoteCapture. This class is abstract and + * cannot be instantiated + */ +class IPcapDevice : public IDevice, public IFilterableDevice { + protected: + pcap_t* m_PcapDescriptor; + + // c'tor should not be public + IPcapDevice() : IDevice() { m_PcapDescriptor = NULL; } + + public: + /** + * @struct PcapStats + * A container for pcap device statistics + */ + struct PcapStats { + /** Number of packets received */ + uint64_t packetsRecv; + /** Number of packets dropped */ + uint64_t packetsDrop; + /** number of packets dropped by interface (not supported on all platforms) + */ + uint64_t packetsDropByInterface; + }; + + virtual ~IPcapDevice(); + + /** + * Get statistics from the device + * @param[out] stats An object containing the stats + */ + virtual void getStatistics(PcapStats& stats) const = 0; + + /** + * A static method for retrieving pcap lib (libpcap/WinPcap/etc.) version + * information. This method is actually a wrapper for + * [pcap_lib_version()](https://www.tcpdump.org/manpages/pcap_lib_version.3pcap.html) + * @return A string containing the pcap lib version information + */ + static std::string getPcapLibVersionInfo(); + + /** + * Match a raw packet with a given BPF filter. Notice this method is static + * which means you don't need any device instance in order to perform this + * match + * @param[in] filter A filter class to test against + * @param[in] rawPacket A pointer to the raw packet to match the filter with + * @return True if raw packet matches the filter or false otherwise + */ + static bool matchPacketWithFilter(GeneralFilter& filter, + RawPacket* rawPacket); + + // implement abstract methods + + using IFilterableDevice::setFilter; + + /** + * Set a filter for the device. When implemented by the device, only packets + * that match the filter will be received. Please note that when the device is + * closed the filter is reset so when reopening the device you need to call + * this method again in order to reactivate the filter + * @param[in] filterAsString The filter to be set in Berkeley Packet Filter + * (BPF) syntax (http://biot.com/capstats/bpf.html) + * @return True if filter set successfully, false otherwise + */ + virtual bool setFilter(std::string filterAsString); + + /** + * Clear the filter currently set on device + * @return True if filter was removed successfully or if no filter was set, + * false otherwise + */ + bool clearFilter(); +}; } // namespace pcpp diff --git a/Pcap++/header/PcapFileDevice.h b/Pcap++/header/PcapFileDevice.h index fdad0a4c45..d32e0b1bf6 100644 --- a/Pcap++/header/PcapFileDevice.h +++ b/Pcap++/header/PcapFileDevice.h @@ -12,583 +12,645 @@ typedef struct pcap_dumper pcap_dumper_t; /// @file /** -* \namespace pcpp -* \brief The main namespace for the PcapPlusPlus lib -*/ -namespace pcpp -{ - - /** - * @class IFileDevice - * An abstract class (cannot be instantiated, has a private c'tor) which is the parent class for all file devices - */ - class IFileDevice : public IPcapDevice - { - protected: - std::string m_FileName; - - explicit IFileDevice(const std::string& fileName); - virtual ~IFileDevice(); - - public: - - /** - * @return The name of the file - */ - std::string getFileName() const; - - - //override methods - - /** - * Close the file - */ - virtual void close(); - }; - - - /** - * @class IFileReaderDevice - * An abstract class (cannot be instantiated, has a private c'tor) which is the parent class for file reader devices - */ - class IFileReaderDevice : public IFileDevice - { - protected: - uint32_t m_NumOfPacketsRead; - uint32_t m_NumOfPacketsNotParsed; - - /** - * A constructor for this class that gets the pcap full path file name to open. Notice that after calling this constructor the file - * isn't opened yet, so reading packets will fail. For opening the file call open() - * @param[in] fileName The full path of the file to read - */ - IFileReaderDevice(const std::string& fileName); - - public: - - /** - * A destructor for this class - */ - virtual ~IFileReaderDevice() {} - - /** - * @return The file size in bytes - */ - uint64_t getFileSize() const; - - virtual bool getNextPacket(RawPacket& rawPacket) = 0; - - /** - * Read the next N packets into a raw packet vector - * @param[out] packetVec The raw packet vector to read packets into - * @param[in] numOfPacketsToRead Number of packets to read. If value <0 all remaining packets in the file will be read into the - * raw packet vector (this is the default value) - * @return The number of packets actually read - */ - int getNextPackets(RawPacketVector& packetVec, int numOfPacketsToRead = -1); - - /** - * A static method that creates an instance of the reader best fit to read the file. It decides by the file extension: for .pcapng - * files it returns an instance of PcapNgFileReaderDevice and for all other extensions it returns an instance of PcapFileReaderDevice - * @param[in] fileName The file name to open - * @return An instance of the reader to read the file. Notice you should free this instance when done using it - */ - static IFileReaderDevice* getReader(const std::string& fileName); - }; - - - /** - * @class PcapFileReaderDevice - * A class for opening a pcap file in read-only mode. This class enable to open the file and read all packets, packet-by-packet - */ - class PcapFileReaderDevice : public IFileReaderDevice - { - private: - LinkLayerType m_PcapLinkLayerType; - - // private copy c'tor - PcapFileReaderDevice(const PcapFileReaderDevice& other); - PcapFileReaderDevice& operator=(const PcapFileReaderDevice& other); - - public: - /** - * A constructor for this class that gets the pcap full path file name to open. Notice that after calling this constructor the file - * isn't opened yet, so reading packets will fail. For opening the file call open() - * @param[in] fileName The full path of the file to read - */ - PcapFileReaderDevice(const std::string& fileName) : IFileReaderDevice(fileName), m_PcapLinkLayerType(LINKTYPE_ETHERNET) {} - - /** - * A destructor for this class - */ - virtual ~PcapFileReaderDevice() {} - - /** - * @return The link layer type of this file - */ - LinkLayerType getLinkLayerType() const { return m_PcapLinkLayerType; } - - - //overridden methods - - /** - * Read the next packet from the file. Before using this method please verify the file is opened using open() - * @param[out] rawPacket A reference for an empty RawPacket where the packet will be written - * @return True if a packet was read successfully. False will be returned if the file isn't opened (also, an error log will be printed) - * or if reached end-of-file - */ - bool getNextPacket(RawPacket& rawPacket); - - /** - * Open the file name which path was specified in the constructor in a read-only mode - * @return True if file was opened successfully or if file is already opened. False if opening the file failed for some reason (for example: - * file path does not exist) - */ - bool open(); - - /** - * Get statistics of packets read so far. In the PcapStats struct, only the packetsRecv member is relevant. The rest of the members will contain 0 - * @param[out] stats The stats struct where stats are returned - */ - void getStatistics(PcapStats& stats) const; - }; - - /** - * @class SnoopFileReaderDevice - * A class for opening a snoop file in read-only mode. This class enable to open the file and read all packets, packet-by-packet - */ - class SnoopFileReaderDevice : public IFileReaderDevice - { - private: - #pragma pack(1) - /* - * File format header. - */ - typedef struct { - uint64_t identification_pattern; - uint32_t version_number; - uint32_t datalink_type; - } snoop_file_header_t; - - /* - * Packet record header. - */ - typedef struct { - uint32_t original_length; /* original packet length */ - uint32_t included_length; /* saved packet length */ - uint32_t packet_record_length;/* total record length */ - uint32_t ndrops_cumulative; /* cumulative drops */ - uint32_t time_sec; /* timestamp */ - uint32_t time_usec; /* microsecond timestamp */ - } snoop_packet_header_t; - #pragma pack() - - LinkLayerType m_PcapLinkLayerType; - std::ifstream m_snoopFile; - - // private copy c'tor - SnoopFileReaderDevice(const PcapFileReaderDevice& other); - SnoopFileReaderDevice& operator=(const PcapFileReaderDevice& other); - - public: - /** - * A constructor for this class that gets the snoop full path file name to open. Notice that after calling this constructor the file - * isn't opened yet, so reading packets will fail. For opening the file call open() - * @param[in] fileName The full path of the file to read - */ - SnoopFileReaderDevice(const std::string& fileName) : IFileReaderDevice(fileName), m_PcapLinkLayerType(LINKTYPE_ETHERNET) {} - - /** - * A destructor for this class - */ - virtual ~SnoopFileReaderDevice(); - - /** - * @return The link layer type of this file - */ - LinkLayerType getLinkLayerType() const { return m_PcapLinkLayerType; } - - - //overridden methods - - /** - * Read the next packet from the file. Before using this method please verify the file is opened using open() - * @param[out] rawPacket A reference for an empty RawPacket where the packet will be written - * @return True if a packet was read successfully. False will be returned if the file isn't opened (also, an error log will be printed) - * or if reached end-of-file - */ - bool getNextPacket(RawPacket& rawPacket); - - /** - * Open the file name which path was specified in the constructor in a read-only mode - * @return True if file was opened successfully or if file is already opened. False if opening the file failed for some reason (for example: - * file path does not exist) - */ - bool open(); - - /** - * Get statistics of packets read so far. In the PcapStats struct, only the packetsRecv member is relevant. The rest of the members will contain 0 - * @param[out] stats The stats struct where stats are returned - */ - void getStatistics(PcapStats& stats) const; - - /** - * Close the snoop file - */ - void close(); - }; - - - /** - * @class PcapNgFileReaderDevice - * A class for opening a pcap-ng file in read-only mode. This class enable to open the file and read all packets, packet-by-packet - */ - class PcapNgFileReaderDevice : public IFileReaderDevice - { - private: - void* m_LightPcapNg; - BpfFilterWrapper m_BpfWrapper; - - // private copy c'tor - PcapNgFileReaderDevice(const PcapNgFileReaderDevice& other); - PcapNgFileReaderDevice& operator=(const PcapNgFileReaderDevice& other); - - public: - /** - * A constructor for this class that gets the pcap-ng full path file name to open. Notice that after calling this constructor the file - * isn't opened yet, so reading packets will fail. For opening the file call open() - * @param[in] fileName The full path of the file to read - */ - PcapNgFileReaderDevice(const std::string& fileName); - - /** - * A destructor for this class - */ - virtual ~PcapNgFileReaderDevice() { close(); } - - /** - * The pcap-ng format allows storing metadata at the header of the file. Part of this metadata is a string specifying the - * operating system that was used for capturing the packets. This method reads this string from the metadata (if exists) and - * returns it - * @return The operating system string if exists, or an empty string otherwise - */ - std::string getOS() const; - - /** - * The pcap-ng format allows storing metadata at the header of the file. Part of this metadata is a string specifying the - * hardware that was used for capturing the packets. This method reads this string from the metadata (if exists) and - * returns it - * @return The hardware string if exists, or an empty string otherwise - */ - std::string getHardware() const; - - /** - * The pcap-ng format allows storing metadata at the header of the file. Part of this metadata is a string specifying the - * capture application that was used for capturing the packets. This method reads this string from the metadata (if exists) and - * returns it - * @return The capture application string if exists, or an empty string otherwise - */ - std::string getCaptureApplication() const; - - /** - * The pcap-ng format allows storing metadata at the header of the file. Part of this metadata is a string containing a user-defined - * comment (can be any string). This method reads this string from the metadata (if exists) and - * returns it - * @return The comment written inside the file if exists, or an empty string otherwise - */ - std::string getCaptureFileComment() const; - - /** - * The pcap-ng format allows storing a user-defined comment for every packet (besides the comment per-file). This method reads - * the next packet and the comment attached to it (if such comment exists), and returns them both - * @param[out] rawPacket A reference for an empty RawPacket where the packet will be written - * @param[out] packetComment The comment attached to the packet or an empty string if no comment exists - * @return True if a packet was read successfully. False will be returned if the file isn't opened (also, an error log will be printed) - * or if reached end-of-file - */ - bool getNextPacket(RawPacket& rawPacket, std::string& packetComment); - - //overridden methods - - /** - * Read the next packet from the file. Before using this method please verify the file is opened using open() - * @param[out] rawPacket A reference for an empty RawPacket where the packet will be written - * @return True if a packet was read successfully. False will be returned if the file isn't opened (also, an error log will be printed) - * or if reached end-of-file - */ - bool getNextPacket(RawPacket& rawPacket); - - /** - * Open the file name which path was specified in the constructor in a read-only mode - * @return True if file was opened successfully or if file is already opened. False if opening the file failed for some reason (for example: - * file path does not exist) - */ - bool open(); - - /** - * Get statistics of packets read so far. - * @param[out] stats The stats struct where stats are returned - */ - void getStatistics(PcapStats& stats) const; - - /** - * Set a filter for PcapNG reader device. Only packets that match the filter will be received - * @param[in] filterAsString The filter to be set in Berkeley Packet Filter (BPF) syntax (http://biot.com/capstats/bpf.html) - * @return True if filter set successfully, false otherwise - */ - bool setFilter(std::string filterAsString); - - /** - * Close the pacp-ng file - */ - void close(); - }; - - - /** - * @class IFileWriterDevice - * An abstract class (cannot be instantiated, has a private c'tor) which is the parent class for file writer devices - */ - class IFileWriterDevice : public IFileDevice - { - protected: - uint32_t m_NumOfPacketsWritten; - uint32_t m_NumOfPacketsNotWritten; - - IFileWriterDevice(const std::string& fileName); - - public: - - /** - * A destructor for this class - */ - virtual ~IFileWriterDevice() {} - - virtual bool writePacket(RawPacket const& packet) = 0; - - virtual bool writePackets(const RawPacketVector& packets) = 0; - - using IFileDevice::open; - virtual bool open(bool appendMode) = 0; - }; - - - /** - * @class PcapFileWriterDevice - * A class for opening a pcap file for writing or create a new pcap file and write packets to it. This class adds - * a unique capability that isn't supported in WinPcap and in older libpcap versions which is to open a pcap file - * in append mode where packets are written at the end of the pcap file instead of running it over - */ - class PcapFileWriterDevice : public IFileWriterDevice - { - private: - pcap_dumper_t* m_PcapDumpHandler; - LinkLayerType m_PcapLinkLayerType; - bool m_AppendMode; - FILE* m_File; - - // private copy c'tor - PcapFileWriterDevice(const PcapFileWriterDevice& other); - PcapFileWriterDevice& operator=(const PcapFileWriterDevice& other); - - void closeFile(); - - public: - /** - * A constructor for this class that gets the pcap full path file name to open for writing or create. Notice that after calling this - * constructor the file isn't opened yet, so writing packets will fail. For opening the file call open() - * @param[in] fileName The full path of the file - * @param[in] linkLayerType The link layer type all packet in this file will be based on. The default is Ethernet - */ - PcapFileWriterDevice(const std::string& fileName, LinkLayerType linkLayerType = LINKTYPE_ETHERNET); - - /** - * A destructor for this class - */ - ~PcapFileWriterDevice() {} - - /** - * Write a RawPacket to the file. Before using this method please verify the file is opened using open(). This method won't change the - * written packet - * @param[in] packet A reference for an existing RawPcket to write to the file - * @return True if a packet was written successfully. False will be returned if the file isn't opened - * or if the packet link layer type is different than the one defined for the file - * (in all cases, an error will be printed to log) - */ - bool writePacket(RawPacket const& packet); - - /** - * Write multiple RawPacket to the file. Before using this method please verify the file is opened using open(). This method won't change - * the written packets or the RawPacketVector instance - * @param[in] packets A reference for an existing RawPcketVector, all of its packets will be written to the file - * @return True if all packets were written successfully to the file. False will be returned if the file isn't opened (also, an error - * log will be printed) or if at least one of the packets wasn't written successfully to the file - */ - bool writePackets(const RawPacketVector& packets); - - //override methods - - /** - * Open the file in a write mode. If file doesn't exist, it will be created. If it does exist it will be - * overwritten, meaning all its current content will be deleted - * @return True if file was opened/created successfully or if file is already opened. False if opening the file failed for some reason - * (an error will be printed to log) - */ - virtual bool open(); - - /** - * Same as open(), but enables to open the file in append mode in which packets will be appended to the file - * instead of overwrite its current content. In append mode file must exist, otherwise opening will fail - * @param[in] appendMode A boolean indicating whether to open the file in append mode or not. If set to false - * this method will act exactly like open(). If set to true, file will be opened in append mode - * @return True of managed to open the file successfully. In case appendMode is set to true, false will be returned - * if file wasn't found or couldn't be read, if file type is not pcap, or if link type specified in c'tor is - * different from current file link type. In case appendMode is set to false, please refer to open() for return - * values - */ - bool open(bool appendMode); - - /** - * Flush and close the pacp file - */ - virtual void close(); - - /** - * Flush packets to disk. - */ - void flush(); - - /** - * Get statistics of packets written so far. - * @param[out] stats The stats struct where stats are returned - */ - virtual void getStatistics(PcapStats& stats) const; - }; - - - /** - * @class PcapNgFileWriterDevice - * A class for opening a pcap-ng file for writing or creating a new pcap-ng file and write packets to it. This class adds - * unique capabilities such as writing metadata attributes into the file header, adding comments per packet and opening - * the file in append mode where packets are added to a file instead of overriding it. This capabilities are part of the - * pcap-ng standard but aren't supported in most tools and libraries - */ - class PcapNgFileWriterDevice : public IFileWriterDevice - { - private: - void* m_LightPcapNg; - int m_CompressionLevel; - BpfFilterWrapper m_BpfWrapper; - - // private copy c'tor - PcapNgFileWriterDevice(const PcapFileWriterDevice& other); - PcapNgFileWriterDevice& operator=(const PcapNgFileWriterDevice& other); - - public: - - /** - * A constructor for this class that gets the pcap-ng full path file name to open for writing or create. Notice that after calling this - * constructor the file isn't opened yet, so writing packets will fail. For opening the file call open() - * @param[in] fileName The full path of the file - * @param[in] compressionLevel The compression level to use when writing the file, use 0 to disable compression or 10 for max compression. Default is 0 - */ - PcapNgFileWriterDevice(const std::string& fileName, int compressionLevel = 0); - - /** - * A destructor for this class - */ - virtual ~PcapNgFileWriterDevice() { close(); } - - /** - * Open the file in a write mode. If file doesn't exist, it will be created. If it does exist it will be - * overwritten, meaning all its current content will be deleted. As opposed to open(), this method also allows writing several - * metadata attributes that will be stored in the header of the file - * @param[in] os A string describing the operating system that was used to capture the packets. If this string is empty or null it - * will be ignored - * @param[in] hardware A string describing the hardware that was used to capture the packets. If this string is empty or null it - * will be ignored - * @param[in] captureApp A string describing the application that was used to capture the packets. If this string is empty or null it - * will be ignored - * @param[in] fileComment A string containing a user-defined comment that will be part of the metadata of the file. - * If this string is empty or null it will be ignored - * @return True if file was opened/created successfully or if file is already opened. False if opening the file failed for some reason - * (an error will be printed to log) - */ - bool open(const std::string& os, const std::string& hardware, const std::string& captureApp, const std::string& fileComment); - - /** - * The pcap-ng format allows adding a user-defined comment for each stored packet. This method writes a RawPacket to the file and - * adds a comment to it. Before using this method please verify the file is opened using open(). This method won't change the - * written packet or the input comment - * @param[in] packet A reference for an existing RawPcket to write to the file - * @param[in] comment The comment to be written for the packet. If this string is empty or null it will be ignored - * @return True if a packet was written successfully. False will be returned if the file isn't opened (an error will be printed to log) - */ - bool writePacket(RawPacket const& packet, const std::string& comment); - - //overridden methods - - /** - * Write a RawPacket to the file. Before using this method please verify the file is opened using open(). This method won't change the - * written packet - * @param[in] packet A reference for an existing RawPcket to write to the file - * @return True if a packet was written successfully. False will be returned if the file isn't opened (an error will be printed to log) - */ - bool writePacket(RawPacket const& packet); - - /** - * Write multiple RawPacket to the file. Before using this method please verify the file is opened using open(). This method won't change - * the written packets or the RawPacketVector instance - * @param[in] packets A reference for an existing RawPcketVector, all of its packets will be written to the file - * @return True if all packets were written successfully to the file. False will be returned if the file isn't opened (also, an error - * log will be printed) or if at least one of the packets wasn't written successfully to the file - */ - bool writePackets(const RawPacketVector& packets); - - /** - * Open the file in a write mode. If file doesn't exist, it will be created. If it does exist it will be - * overwritten, meaning all its current content will be deleted - * @return True if file was opened/created successfully or if file is already opened. False if opening the file failed for some reason - * (an error will be printed to log) - */ - bool open(); - - /** - * Same as open(), but enables to open the file in append mode in which packets will be appended to the file - * instead of overwrite its current content. In append mode file must exist, otherwise opening will fail - * @param[in] appendMode A boolean indicating whether to open the file in append mode or not. If set to false - * this method will act exactly like open(). If set to true, file will be opened in append mode - * @return True of managed to open the file successfully. In case appendMode is set to true, false will be returned - * if file wasn't found or couldn't be read, if file type is not pcap-ng. In case appendMode is set to false, please refer to open() - * for return values - */ - bool open(bool appendMode); - - /** - * Flush packets to the pcap-ng file - */ - void flush(); - - /** - * Flush and close the pcap-ng file - */ - void close(); - - /** - * Get statistics of packets written so far. - * @param[out] stats The stats struct where stats are returned - */ - void getStatistics(PcapStats& stats) const; - - /** - * Set a filter for PcapNG writer device. Only packets that match the filter will be persisted - * @param[in] filterAsString The filter to be set in Berkeley Packet Filter (BPF) syntax (http://biot.com/capstats/bpf.html) - * @return True if filter set successfully, false otherwise - */ - bool setFilter(std::string filterAsString); - - }; - -}// namespace pcpp + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp { + +/** + * @class IFileDevice + * An abstract class (cannot be instantiated, has a private c'tor) which is the + * parent class for all file devices + */ +class IFileDevice : public IPcapDevice { + protected: + std::string m_FileName; + + explicit IFileDevice(const std::string& fileName); + virtual ~IFileDevice(); + + public: + /** + * @return The name of the file + */ + std::string getFileName() const; + + // override methods + + /** + * Close the file + */ + virtual void close(); +}; + +/** + * @class IFileReaderDevice + * An abstract class (cannot be instantiated, has a private c'tor) which is the + * parent class for file reader devices + */ +class IFileReaderDevice : public IFileDevice { + protected: + uint32_t m_NumOfPacketsRead; + uint32_t m_NumOfPacketsNotParsed; + + /** + * A constructor for this class that gets the pcap full path file name to + * open. Notice that after calling this constructor the file isn't opened yet, + * so reading packets will fail. For opening the file call open() + * @param[in] fileName The full path of the file to read + */ + IFileReaderDevice(const std::string& fileName); + + public: + /** + * A destructor for this class + */ + virtual ~IFileReaderDevice() {} + + /** + * @return The file size in bytes + */ + uint64_t getFileSize() const; + + virtual bool getNextPacket(RawPacket& rawPacket) = 0; + + /** + * Read the next N packets into a raw packet vector + * @param[out] packetVec The raw packet vector to read packets into + * @param[in] numOfPacketsToRead Number of packets to read. If value <0 all + * remaining packets in the file will be read into the raw packet vector (this + * is the default value) + * @return The number of packets actually read + */ + int getNextPackets(RawPacketVector& packetVec, int numOfPacketsToRead = -1); + + /** + * A static method that creates an instance of the reader best fit to read the + * file. It decides by the file extension: for .pcapng files it returns an + * instance of PcapNgFileReaderDevice and for all other extensions it returns + * an instance of PcapFileReaderDevice + * @param[in] fileName The file name to open + * @return An instance of the reader to read the file. Notice you should free + * this instance when done using it + */ + static IFileReaderDevice* getReader(const std::string& fileName); +}; + +/** + * @class PcapFileReaderDevice + * A class for opening a pcap file in read-only mode. This class enable to open + * the file and read all packets, packet-by-packet + */ +class PcapFileReaderDevice : public IFileReaderDevice { + private: + LinkLayerType m_PcapLinkLayerType; + + // private copy c'tor + PcapFileReaderDevice(const PcapFileReaderDevice& other); + PcapFileReaderDevice& operator=(const PcapFileReaderDevice& other); + + public: + /** + * A constructor for this class that gets the pcap full path file name to + * open. Notice that after calling this constructor the file isn't opened yet, + * so reading packets will fail. For opening the file call open() + * @param[in] fileName The full path of the file to read + */ + PcapFileReaderDevice(const std::string& fileName) + : IFileReaderDevice(fileName), m_PcapLinkLayerType(LINKTYPE_ETHERNET) {} + + /** + * A destructor for this class + */ + virtual ~PcapFileReaderDevice() {} + + /** + * @return The link layer type of this file + */ + LinkLayerType getLinkLayerType() const { return m_PcapLinkLayerType; } + + // overridden methods + + /** + * Read the next packet from the file. Before using this method please verify + * the file is opened using open() + * @param[out] rawPacket A reference for an empty RawPacket where the packet + * will be written + * @return True if a packet was read successfully. False will be returned if + * the file isn't opened (also, an error log will be printed) or if reached + * end-of-file + */ + bool getNextPacket(RawPacket& rawPacket); + + /** + * Open the file name which path was specified in the constructor in a + * read-only mode + * @return True if file was opened successfully or if file is already opened. + * False if opening the file failed for some reason (for example: file path + * does not exist) + */ + bool open(); + + /** + * Get statistics of packets read so far. In the PcapStats struct, only the + * packetsRecv member is relevant. The rest of the members will contain 0 + * @param[out] stats The stats struct where stats are returned + */ + void getStatistics(PcapStats& stats) const; +}; + +/** + * @class SnoopFileReaderDevice + * A class for opening a snoop file in read-only mode. This class enable to open + * the file and read all packets, packet-by-packet + */ +class SnoopFileReaderDevice : public IFileReaderDevice { + private: +#pragma pack(1) + /* + * File format header. + */ + typedef struct { + uint64_t identification_pattern; + uint32_t version_number; + uint32_t datalink_type; + } snoop_file_header_t; + + /* + * Packet record header. + */ + typedef struct { + uint32_t original_length; /* original packet length */ + uint32_t included_length; /* saved packet length */ + uint32_t packet_record_length; /* total record length */ + uint32_t ndrops_cumulative; /* cumulative drops */ + uint32_t time_sec; /* timestamp */ + uint32_t time_usec; /* microsecond timestamp */ + } snoop_packet_header_t; +#pragma pack() + + LinkLayerType m_PcapLinkLayerType; + std::ifstream m_snoopFile; + + // private copy c'tor + SnoopFileReaderDevice(const PcapFileReaderDevice& other); + SnoopFileReaderDevice& operator=(const PcapFileReaderDevice& other); + + public: + /** + * A constructor for this class that gets the snoop full path file name to + * open. Notice that after calling this constructor the file isn't opened yet, + * so reading packets will fail. For opening the file call open() + * @param[in] fileName The full path of the file to read + */ + SnoopFileReaderDevice(const std::string& fileName) + : IFileReaderDevice(fileName), m_PcapLinkLayerType(LINKTYPE_ETHERNET) {} + + /** + * A destructor for this class + */ + virtual ~SnoopFileReaderDevice(); + + /** + * @return The link layer type of this file + */ + LinkLayerType getLinkLayerType() const { return m_PcapLinkLayerType; } + + // overridden methods + + /** + * Read the next packet from the file. Before using this method please verify + * the file is opened using open() + * @param[out] rawPacket A reference for an empty RawPacket where the packet + * will be written + * @return True if a packet was read successfully. False will be returned if + * the file isn't opened (also, an error log will be printed) or if reached + * end-of-file + */ + bool getNextPacket(RawPacket& rawPacket); + + /** + * Open the file name which path was specified in the constructor in a + * read-only mode + * @return True if file was opened successfully or if file is already opened. + * False if opening the file failed for some reason (for example: file path + * does not exist) + */ + bool open(); + + /** + * Get statistics of packets read so far. In the PcapStats struct, only the + * packetsRecv member is relevant. The rest of the members will contain 0 + * @param[out] stats The stats struct where stats are returned + */ + void getStatistics(PcapStats& stats) const; + + /** + * Close the snoop file + */ + void close(); +}; + +/** + * @class PcapNgFileReaderDevice + * A class for opening a pcap-ng file in read-only mode. This class enable to + * open the file and read all packets, packet-by-packet + */ +class PcapNgFileReaderDevice : public IFileReaderDevice { + private: + void* m_LightPcapNg; + BpfFilterWrapper m_BpfWrapper; + + // private copy c'tor + PcapNgFileReaderDevice(const PcapNgFileReaderDevice& other); + PcapNgFileReaderDevice& operator=(const PcapNgFileReaderDevice& other); + + public: + /** + * A constructor for this class that gets the pcap-ng full path file name to + * open. Notice that after calling this constructor the file isn't opened yet, + * so reading packets will fail. For opening the file call open() + * @param[in] fileName The full path of the file to read + */ + PcapNgFileReaderDevice(const std::string& fileName); + + /** + * A destructor for this class + */ + virtual ~PcapNgFileReaderDevice() { close(); } + + /** + * The pcap-ng format allows storing metadata at the header of the file. Part + * of this metadata is a string specifying the operating system that was used + * for capturing the packets. This method reads this string from the metadata + * (if exists) and returns it + * @return The operating system string if exists, or an empty string otherwise + */ + std::string getOS() const; + + /** + * The pcap-ng format allows storing metadata at the header of the file. Part + * of this metadata is a string specifying the hardware that was used for + * capturing the packets. This method reads this string from the metadata (if + * exists) and returns it + * @return The hardware string if exists, or an empty string otherwise + */ + std::string getHardware() const; + + /** + * The pcap-ng format allows storing metadata at the header of the file. Part + * of this metadata is a string specifying the capture application that was + * used for capturing the packets. This method reads this string from the + * metadata (if exists) and returns it + * @return The capture application string if exists, or an empty string + * otherwise + */ + std::string getCaptureApplication() const; + + /** + * The pcap-ng format allows storing metadata at the header of the file. Part + * of this metadata is a string containing a user-defined comment (can be any + * string). This method reads this string from the metadata (if exists) and + * returns it + * @return The comment written inside the file if exists, or an empty string + * otherwise + */ + std::string getCaptureFileComment() const; + + /** + * The pcap-ng format allows storing a user-defined comment for every packet + * (besides the comment per-file). This method reads the next packet and the + * comment attached to it (if such comment exists), and returns them both + * @param[out] rawPacket A reference for an empty RawPacket where the packet + * will be written + * @param[out] packetComment The comment attached to the packet or an empty + * string if no comment exists + * @return True if a packet was read successfully. False will be returned if + * the file isn't opened (also, an error log will be printed) or if reached + * end-of-file + */ + bool getNextPacket(RawPacket& rawPacket, std::string& packetComment); + + // overridden methods + + /** + * Read the next packet from the file. Before using this method please verify + * the file is opened using open() + * @param[out] rawPacket A reference for an empty RawPacket where the packet + * will be written + * @return True if a packet was read successfully. False will be returned if + * the file isn't opened (also, an error log will be printed) or if reached + * end-of-file + */ + bool getNextPacket(RawPacket& rawPacket); + + /** + * Open the file name which path was specified in the constructor in a + * read-only mode + * @return True if file was opened successfully or if file is already opened. + * False if opening the file failed for some reason (for example: file path + * does not exist) + */ + bool open(); + + /** + * Get statistics of packets read so far. + * @param[out] stats The stats struct where stats are returned + */ + void getStatistics(PcapStats& stats) const; + + /** + * Set a filter for PcapNG reader device. Only packets that match the filter + * will be received + * @param[in] filterAsString The filter to be set in Berkeley Packet Filter + * (BPF) syntax (http://biot.com/capstats/bpf.html) + * @return True if filter set successfully, false otherwise + */ + bool setFilter(std::string filterAsString); + + /** + * Close the pacp-ng file + */ + void close(); +}; + +/** + * @class IFileWriterDevice + * An abstract class (cannot be instantiated, has a private c'tor) which is the + * parent class for file writer devices + */ +class IFileWriterDevice : public IFileDevice { + protected: + uint32_t m_NumOfPacketsWritten; + uint32_t m_NumOfPacketsNotWritten; + + IFileWriterDevice(const std::string& fileName); + + public: + /** + * A destructor for this class + */ + virtual ~IFileWriterDevice() {} + + virtual bool writePacket(RawPacket const& packet) = 0; + + virtual bool writePackets(const RawPacketVector& packets) = 0; + + using IFileDevice::open; + virtual bool open(bool appendMode) = 0; +}; + +/** + * @class PcapFileWriterDevice + * A class for opening a pcap file for writing or create a new pcap file and + * write packets to it. This class adds a unique capability that isn't supported + * in WinPcap and in older libpcap versions which is to open a pcap file in + * append mode where packets are written at the end of the pcap file instead of + * running it over + */ +class PcapFileWriterDevice : public IFileWriterDevice { + private: + pcap_dumper_t* m_PcapDumpHandler; + LinkLayerType m_PcapLinkLayerType; + bool m_AppendMode; + FILE* m_File; + + // private copy c'tor + PcapFileWriterDevice(const PcapFileWriterDevice& other); + PcapFileWriterDevice& operator=(const PcapFileWriterDevice& other); + + void closeFile(); + + public: + /** + * A constructor for this class that gets the pcap full path file name to open + * for writing or create. Notice that after calling this constructor the file + * isn't opened yet, so writing packets will fail. For opening the file call + * open() + * @param[in] fileName The full path of the file + * @param[in] linkLayerType The link layer type all packet in this file will + * be based on. The default is Ethernet + */ + PcapFileWriterDevice(const std::string& fileName, + LinkLayerType linkLayerType = LINKTYPE_ETHERNET); + + /** + * A destructor for this class + */ + ~PcapFileWriterDevice() {} + + /** + * Write a RawPacket to the file. Before using this method please verify the + * file is opened using open(). This method won't change the written packet + * @param[in] packet A reference for an existing RawPcket to write to the file + * @return True if a packet was written successfully. False will be returned + * if the file isn't opened or if the packet link layer type is different than + * the one defined for the file (in all cases, an error will be printed to + * log) + */ + bool writePacket(RawPacket const& packet); + + /** + * Write multiple RawPacket to the file. Before using this method please + * verify the file is opened using open(). This method won't change the + * written packets or the RawPacketVector instance + * @param[in] packets A reference for an existing RawPcketVector, all of its + * packets will be written to the file + * @return True if all packets were written successfully to the file. False + * will be returned if the file isn't opened (also, an error log will be + * printed) or if at least one of the packets wasn't written successfully to + * the file + */ + bool writePackets(const RawPacketVector& packets); + + // override methods + + /** + * Open the file in a write mode. If file doesn't exist, it will be created. + * If it does exist it will be overwritten, meaning all its current content + * will be deleted + * @return True if file was opened/created successfully or if file is already + * opened. False if opening the file failed for some reason (an error will be + * printed to log) + */ + virtual bool open(); + + /** + * Same as open(), but enables to open the file in append mode in which + * packets will be appended to the file instead of overwrite its current + * content. In append mode file must exist, otherwise opening will fail + * @param[in] appendMode A boolean indicating whether to open the file in + * append mode or not. If set to false this method will act exactly like + * open(). If set to true, file will be opened in append mode + * @return True of managed to open the file successfully. In case appendMode + * is set to true, false will be returned if file wasn't found or couldn't be + * read, if file type is not pcap, or if link type specified in c'tor is + * different from current file link type. In case appendMode is set to false, + * please refer to open() for return values + */ + bool open(bool appendMode); + + /** + * Flush and close the pacp file + */ + virtual void close(); + + /** + * Flush packets to disk. + */ + void flush(); + + /** + * Get statistics of packets written so far. + * @param[out] stats The stats struct where stats are returned + */ + virtual void getStatistics(PcapStats& stats) const; +}; + +/** + * @class PcapNgFileWriterDevice + * A class for opening a pcap-ng file for writing or creating a new pcap-ng file + * and write packets to it. This class adds unique capabilities such as writing + * metadata attributes into the file header, adding comments per packet and + * opening the file in append mode where packets are added to a file instead of + * overriding it. This capabilities are part of the pcap-ng standard but aren't + * supported in most tools and libraries + */ +class PcapNgFileWriterDevice : public IFileWriterDevice { + private: + void* m_LightPcapNg; + int m_CompressionLevel; + BpfFilterWrapper m_BpfWrapper; + + // private copy c'tor + PcapNgFileWriterDevice(const PcapFileWriterDevice& other); + PcapNgFileWriterDevice& operator=(const PcapNgFileWriterDevice& other); + + public: + /** + * A constructor for this class that gets the pcap-ng full path file name to + * open for writing or create. Notice that after calling this constructor the + * file isn't opened yet, so writing packets will fail. For opening the file + * call open() + * @param[in] fileName The full path of the file + * @param[in] compressionLevel The compression level to use when writing the + * file, use 0 to disable compression or 10 for max compression. Default is 0 + */ + PcapNgFileWriterDevice(const std::string& fileName, int compressionLevel = 0); + + /** + * A destructor for this class + */ + virtual ~PcapNgFileWriterDevice() { close(); } + + /** + * Open the file in a write mode. If file doesn't exist, it will be created. + * If it does exist it will be overwritten, meaning all its current content + * will be deleted. As opposed to open(), this method also allows writing + * several metadata attributes that will be stored in the header of the file + * @param[in] os A string describing the operating system that was used to + * capture the packets. If this string is empty or null it will be ignored + * @param[in] hardware A string describing the hardware that was used to + * capture the packets. If this string is empty or null it will be ignored + * @param[in] captureApp A string describing the application that was used to + * capture the packets. If this string is empty or null it will be ignored + * @param[in] fileComment A string containing a user-defined comment that will + * be part of the metadata of the file. If this string is empty or null it + * will be ignored + * @return True if file was opened/created successfully or if file is already + * opened. False if opening the file failed for some reason (an error will be + * printed to log) + */ + bool open(const std::string& os, const std::string& hardware, + const std::string& captureApp, const std::string& fileComment); + + /** + * The pcap-ng format allows adding a user-defined comment for each stored + * packet. This method writes a RawPacket to the file and adds a comment to + * it. Before using this method please verify the file is opened using open(). + * This method won't change the written packet or the input comment + * @param[in] packet A reference for an existing RawPcket to write to the file + * @param[in] comment The comment to be written for the packet. If this string + * is empty or null it will be ignored + * @return True if a packet was written successfully. False will be returned + * if the file isn't opened (an error will be printed to log) + */ + bool writePacket(RawPacket const& packet, const std::string& comment); + + // overridden methods + + /** + * Write a RawPacket to the file. Before using this method please verify the + * file is opened using open(). This method won't change the written packet + * @param[in] packet A reference for an existing RawPcket to write to the file + * @return True if a packet was written successfully. False will be returned + * if the file isn't opened (an error will be printed to log) + */ + bool writePacket(RawPacket const& packet); + + /** + * Write multiple RawPacket to the file. Before using this method please + * verify the file is opened using open(). This method won't change the + * written packets or the RawPacketVector instance + * @param[in] packets A reference for an existing RawPcketVector, all of its + * packets will be written to the file + * @return True if all packets were written successfully to the file. False + * will be returned if the file isn't opened (also, an error log will be + * printed) or if at least one of the packets wasn't written successfully to + * the file + */ + bool writePackets(const RawPacketVector& packets); + + /** + * Open the file in a write mode. If file doesn't exist, it will be created. + * If it does exist it will be overwritten, meaning all its current content + * will be deleted + * @return True if file was opened/created successfully or if file is already + * opened. False if opening the file failed for some reason (an error will be + * printed to log) + */ + bool open(); + + /** + * Same as open(), but enables to open the file in append mode in which + * packets will be appended to the file instead of overwrite its current + * content. In append mode file must exist, otherwise opening will fail + * @param[in] appendMode A boolean indicating whether to open the file in + * append mode or not. If set to false this method will act exactly like + * open(). If set to true, file will be opened in append mode + * @return True of managed to open the file successfully. In case appendMode + * is set to true, false will be returned if file wasn't found or couldn't be + * read, if file type is not pcap-ng. In case appendMode is set to false, + * please refer to open() for return values + */ + bool open(bool appendMode); + + /** + * Flush packets to the pcap-ng file + */ + void flush(); + + /** + * Flush and close the pcap-ng file + */ + void close(); + + /** + * Get statistics of packets written so far. + * @param[out] stats The stats struct where stats are returned + */ + void getStatistics(PcapStats& stats) const; + + /** + * Set a filter for PcapNG writer device. Only packets that match the filter + * will be persisted + * @param[in] filterAsString The filter to be set in Berkeley Packet Filter + * (BPF) syntax (http://biot.com/capstats/bpf.html) + * @return True if filter set successfully, false otherwise + */ + bool setFilter(std::string filterAsString); +}; + +} // namespace pcpp #endif diff --git a/Pcap++/header/PcapFilter.h b/Pcap++/header/PcapFilter.h index 6e933a5827..ba363edf2e 100644 --- a/Pcap++/header/PcapFilter.h +++ b/Pcap++/header/PcapFilter.h @@ -1,800 +1,860 @@ #ifndef PCAPP_FILTER #define PCAPP_FILTER -#include -#include -#include -#include "ProtocolType.h" -#include #include "ArpLayer.h" +#include "ProtocolType.h" #include "RawPacket.h" +#include +#include +#include +#include -//Forward Declaration - used in GeneralFilter +// Forward Declaration - used in GeneralFilter struct bpf_program; /** * @file - * Most packet capture engines contain packet filtering capabilities. In order to set the filters there should be a known syntax user can use. - * The most popular syntax is Berkeley Packet Filter (BPF) - see more in here: http://en.wikipedia.org/wiki/Berkeley_Packet_Filter. - * Detailed explanation of the syntax can be found here: http://www.tcpdump.org/manpages/pcap-filter.7.html.
- * The problem with BPF is that, for my opinion, the syntax is too complicated and too poorly documented. In addition the BPF filter compilers - * may output syntax errors that are hard to understand. My experience with BPF was not good, so I decided to make the filters mechanism more - * structured, easier to understand and less error-prone by creating classes that represent filters. Each possible filter phrase is represented - * by a class. The filter, at the end, is that class.
- * For example: the filter "src host 1.1.1.1" will be represented by IPFilter instance; "dst port 80" will be represented by PortFilter, and - * so on.
- * So what about complex filters that involve "and", "or"? There are also 2 classes: AndFilter and OrFilter that can store more filters (in a - * composite idea) and connect them by "and" or "or". For example: "src host 1.1.1.1 and dst port 80" will be represented by an AndFilter that + * Most packet capture engines contain packet filtering capabilities. In order + * to set the filters there should be a known syntax user can use. The most + * popular syntax is Berkeley Packet Filter (BPF) - see more in here: + * http://en.wikipedia.org/wiki/Berkeley_Packet_Filter. Detailed explanation of + * the syntax can be found here: + * http://www.tcpdump.org/manpages/pcap-filter.7.html.
The problem with BPF + * is that, for my opinion, the syntax is too complicated and too poorly + * documented. In addition the BPF filter compilers may output syntax errors + * that are hard to understand. My experience with BPF was not good, so I + * decided to make the filters mechanism more structured, easier to understand + * and less error-prone by creating classes that represent filters. Each + * possible filter phrase is represented by a class. The filter, at the end, is + * that class.
For example: the filter "src host 1.1.1.1" will be + * represented by IPFilter instance; "dst port 80" will be represented by + * PortFilter, and so on.
So what about complex filters that involve "and", + * "or"? There are also 2 classes: AndFilter and OrFilter that can store more + * filters (in a composite idea) and connect them by "and" or "or". For example: + * "src host 1.1.1.1 and dst port 80" will be represented by an AndFilter that * h olds IPFilter and PortFilter inside it */ /** -* \namespace pcpp -* \brief The main namespace for the PcapPlusPlus lib -*/ -namespace pcpp -{ - //Forward Declaration - used in GeneralFilter - class RawPacket; - - /** - * An enum that contains direction (source or destination) - */ - typedef enum - { - /** Source */ - SRC, - /** Destination */ - DST, - /** Source or destination */ - SRC_OR_DST - } Direction; - - - /** - * Supported operators enum - */ - typedef enum - { - /** Equals */ - EQUALS, - /** Not equals */ - NOT_EQUALS, - /** Greater than */ - GREATER_THAN, - /** Greater or equal */ - GREATER_OR_EQUAL, - /** Less than */ - LESS_THAN, - /** Less or equal */ - LESS_OR_EQUAL - } FilterOperator; - - /** - * @class BpfFilterWrapper - * A wrapper class for BPF filtering. Enables setting a BPF filter and matching it against a packet - */ - class BpfFilterWrapper - { - private: - std::string m_FilterStr; - LinkLayerType m_LinkType; - bpf_program* m_Program; - - void freeProgram(); - - public: - - /** - * A c'tor for this class - */ - BpfFilterWrapper(); - - /** - * A d'tor for this class. Makes sure to clear the bpf_program object if was previously set. - */ - ~BpfFilterWrapper(); - - /** - * Set a filter. This method receives a filter in BPF syntax (https://biot.com/capstats/bpf.html) and an optional link type, - * compiles them, and if compilation is successful it stores the filter. - * @param[in] filter A filter in BPF syntax - * @param[in] linkType An optional parameter to set the filter's link type. The default is LINKTYPE_ETHERNET - * @return True if compilation is successful and filter is stored in side this object, false otherwise - */ - bool setFilter(const std::string& filter, LinkLayerType linkType = LINKTYPE_ETHERNET); - - /** - * Match a packet with the filter stored in this object. If the filter is empty the method returns "true". - * If the link type of the raw packet is different than the one set in setFilter(), the filter will be - * re-compiled and stored in the object. - * @param[in] rawPacket A pointer to a raw packet which the filter will be matched against - * @return True if the filter matches (or if it's empty). False if the packet doesn't match or if the filter - * could not be compiled - */ - bool matchPacketWithFilter(const RawPacket* rawPacket); - - /** - * Match a packet data with the filter stored in this object. If the filter is empty the method returns "true". - * If the link type provided is different than the one set in setFilter(), the filter will be re-compiled - * and stored in the object. - * @param[in] packetData A byte stream containing the packet data - * @param[in] packetDataLength The length in [bytes] of the byte stream - * @param[in] packetTimestamp The packet timestamp - * @param[in] linkType The packet link type - * @return True if the filter matches (or if it's empty). False if the packet doesn't match or if the filter - * could not be compiled - */ - bool matchPacketWithFilter(const uint8_t* packetData, uint32_t packetDataLength, timespec packetTimestamp, uint16_t linkType); - }; - - /** - * @class GeneralFilter - * The base class for all filter classes. This class is virtual and abstract, hence cannot be instantiated.
- * For deeper understanding of the filter concept please refer to PcapFilter.h - */ - class GeneralFilter - { - protected: - BpfFilterWrapper m_BpfWrapper; - - public: - /** - * A method that parses the class instance into BPF string format - * @param[out] result An empty string that the parsing will be written into. If the string isn't empty, its content will be overridden - */ - virtual void parseToString(std::string& result) = 0; - - /** - * Match a raw packet with a given BPF filter. - * @param[in] rawPacket A pointer to the raw packet to match the BPF filter with - * @return True if a raw packet matches the BPF filter or false otherwise - */ - bool matchPacketWithFilter(RawPacket* rawPacket); - - GeneralFilter() {} - - /** - * Virtual destructor, frees the bpf program - */ - virtual ~GeneralFilter() {} - }; - - /** - * @class BPFStringFilter - * This class can be loaded with a BPF filter string and then can be used to verify the string is valid.
- */ - class BPFStringFilter : public GeneralFilter - { - private: - const std::string m_FilterStr; - - public: - explicit BPFStringFilter(const std::string& filterStr) : m_FilterStr(filterStr) {} - - virtual ~BPFStringFilter() {} - - /** - * A method that parses the class instance into BPF string format - * @param[out] result An empty string that the parsing will be written into. If the string isn't empty, its content will be overridden - * If the filter is not valid the result will be an empty string - */ - virtual void parseToString(std::string& result); - - /** - * Verify the filter is valid - * @return True if the filter is valid or false otherwise - */ - bool verifyFilter(); - }; - - - /** - * @class IFilterWithDirection - * An abstract class that is the base class for all filters which contain a direction (source or destination). This class cannot be instantiated
- * For deeper understanding of the filter concept please refer to PcapFilter.h - */ - class IFilterWithDirection : public GeneralFilter - { - private: - Direction m_Dir; - protected: - void parseDirection(std::string& directionAsString); - Direction getDir() const { return m_Dir; } - explicit IFilterWithDirection(Direction dir) { m_Dir = dir; } - public: - /** - * Set the direction for the filter (source or destination) - * @param[in] dir The direction - */ - void setDirection(Direction dir) { m_Dir = dir; } - }; - - - /** - * @class IFilterWithOperator - * An abstract class that is the base class for all filters which contain an operator (e.g X equals Y; A is greater than B; Z1 not equals Z2, etc.). - * This class cannot be instantiated
- * For deeper understanding of the filter concept please refer to PcapFilter.h - */ - class IFilterWithOperator : public GeneralFilter - { - private: - FilterOperator m_Operator; - protected: - std::string parseOperator(); - FilterOperator getOperator() const { return m_Operator; } - explicit IFilterWithOperator(FilterOperator op) { m_Operator = op; } - public: - /** - * Set the operator for the filter - * @param[in] op The operator to set - */ - void setOperator(FilterOperator op) { m_Operator = op; } - }; - - - - /** - * @class IPFilter - * A class for representing IPv4 address filter, equivalent to "net src x.x.x.x" or "net dst x.x.x.x"
- * For deeper understanding of the filter concept please refer to PcapFilter.h - * @todo Add IPv6 filtering support - */ - class IPFilter : public IFilterWithDirection - { - private: - std::string m_Address; - std::string m_IPv4Mask; - int m_Len; - void convertToIPAddressWithMask(std::string& ipAddrmodified, std::string& mask) const; - void convertToIPAddressWithLen(std::string& ipAddrmodified) const; - public: - /** - * The basic constructor that creates the filter from an IPv4 address and direction (source or destination) - * @param[in] ipAddress The IPv4 address to build the filter with. If this address is not a valid IPv4 address an error will be - * written to log and parsing this filter will fail - * @param[in] dir The address direction to filter (source or destination) - */ - IPFilter(const std::string& ipAddress, Direction dir) : IFilterWithDirection(dir), m_Address(ipAddress), m_IPv4Mask(""), m_Len(0) {} - - /** - * A constructor that enable to filter only part of the address by using a mask (aka subnet). For example: "filter only IP addresses that matches - * the subnet 10.0.0.x" - * @param[in] ipAddress The IPv4 address to use. Only the part of the address that is not masked will be matched. For example: if the address - * is "1.2.3.4" and the mask is "255.255.255.0" than the part of the address that will be matched is "1.2.3.X". If this address is not a - * valid IPv4 address an error will be written to log and parsing this filter will fail - * @param[in] dir The address direction to filter (source or destination) - * @param[in] ipv4Mask The mask to use. Mask should also be in a valid IPv4 format (i.e x.x.x.x), otherwise parsing this filter will fail - */ - IPFilter(const std::string& ipAddress, Direction dir, const std::string& ipv4Mask) : IFilterWithDirection(dir), m_Address(ipAddress), m_IPv4Mask(ipv4Mask), m_Len(0) {} - - /** - * A constructor that enables to filter by a subnet. For example: "filter only IP addresses that matches the subnet 10.0.0.3/24" which means - * the part of the address that will be matched is "10.0.0.X" - * @param[in] ipAddress The IPv4 address to use. Only the part of the address that is not masked will be matched. For example: if the address - * is "1.2.3.4" and the subnet is "/24" than the part of the address that will be matched is "1.2.3.X". If this address is not a - * valid IPv4 address an error will be written to log and parsing this filter will fail - * @param[in] dir The address direction to filter (source or destination) - * @param[in] len The subnet to use (e.g "/24") - */ - IPFilter(const std::string& ipAddress, Direction dir, int len) : IFilterWithDirection(dir), m_Address(ipAddress), m_IPv4Mask(""), m_Len(len) {} - - void parseToString(std::string& result); - - /** - * Set the IPv4 address - * @param[in] ipAddress The IPv4 address to build the filter with. If this address is not a valid IPv4 address an error will be - * written to log and parsing this filter will fail - */ - void setAddr(const std::string& ipAddress) { m_Address = ipAddress; } - - /** - * Set the IPv4 mask - * @param[in] ipv4Mask The mask to use. Mask should also be in a valid IPv4 format (i.e x.x.x.x), otherwise parsing this filter will fail - */ - void setMask(const std::string& ipv4Mask) { m_IPv4Mask = ipv4Mask; m_Len = 0; } - - /** - * Set the subnet - * @param[in] len The subnet to use (e.g "/24") - */ - void setLen(int len) { m_IPv4Mask = ""; m_Len = len; } - }; - - - - /** - * @class IPv4IDFilter - * A class for filtering IPv4 traffic by IP ID field of the IPv4 protocol, For example: - * "filter only IPv4 traffic which IP ID is greater than 1234"
- * For deeper understanding of the filter concept please refer to PcapFilter.h - */ - class IPv4IDFilter : public IFilterWithOperator - { - private: - uint16_t m_IpID; - public: - /** - * A constructor that gets the IP ID to filter and the operator and creates the filter out of them - * @param[in] ipID The IP ID to filter - * @param[in] op The operator to use (e.g "equal", "greater than", etc.) - */ - IPv4IDFilter(uint16_t ipID, FilterOperator op) : IFilterWithOperator(op), m_IpID(ipID) {} - - void parseToString(std::string& result); - - /** - * Set the IP ID to filter - * @param[in] ipID The IP ID to filter - */ - void setIpID(uint16_t ipID) { m_IpID = ipID; } - }; - - - - /** - * @class IPv4TotalLengthFilter - * A class for filtering IPv4 traffic by "total length" field of the IPv4 protocol, For example: - * "filter only IPv4 traffic which "total length" value is less than 60B"
- * For deeper understanding of the filter concept please refer to PcapFilter.h - */ - class IPv4TotalLengthFilter : public IFilterWithOperator - { - private: - uint16_t m_TotalLength; - public: - /** - * A constructor that gets the total length to filter and the operator and creates the filter out of them - * @param[in] totalLength The total length value to filter - * @param[in] op The operator to use (e.g "equal", "greater than", etc.) - */ - IPv4TotalLengthFilter(uint16_t totalLength, FilterOperator op) : IFilterWithOperator(op), m_TotalLength(totalLength) {} - - void parseToString(std::string& result); - - /** - * Set the total length value - * @param[in] totalLength The total length value to filter - */ - void setTotalLength(uint16_t totalLength) { m_TotalLength = totalLength; } - }; - - - - /** - * @class PortFilter - * A class for filtering TCP or UDP traffic by port, for example: "dst port 80" or "src port 12345"
- * For deeper understanding of the filter concept please refer to PcapFilter.h - */ - class PortFilter : public IFilterWithDirection - { - private: - std::string m_Port; - void portToString(uint16_t portAsInt); - public: - /** - * A constructor that gets the port and the direction and creates the filter - * @param[in] port The port to create the filter with - * @param[in] dir The port direction to filter (source or destination) - */ - PortFilter(uint16_t port, Direction dir); - - void parseToString(std::string& result); - - /** - * Set the port - * @param[in] port The port to create the filter with - */ - void setPort(uint16_t port) { portToString(port); } - }; - - - - /** - * @class PortRangeFilter - * A class for filtering TCP or UDP port ranges, meaning match only packets which port is within this range, for example: "src portrange 1000-2000" - * will match only TCP or UDP traffic which source port is in the range of 1000 - 2000
- * For deeper understanding of the filter concept please refer to PcapFilter.h - */ - class PortRangeFilter : public IFilterWithDirection - { - private: - uint16_t m_FromPort; - uint16_t m_ToPort; - public: - /** - * A constructor that gets the port range the the direction and creates the filter with them - * @param[in] fromPort The lower end of the port range - * @param[in] toPort The higher end of the port range - * @param[in] dir The port range direction to filter (source or destination) - */ - PortRangeFilter(uint16_t fromPort, uint16_t toPort, Direction dir) : IFilterWithDirection(dir), m_FromPort(fromPort), m_ToPort(toPort) {} - - void parseToString(std::string& result); - - /** - * Set the lower end of the port range - * @param[in] fromPort The lower end of the port range - */ - void setFromPort(uint16_t fromPort) { m_FromPort = fromPort; } - - /** - * Set the higher end of the port range - * @param[in] toPort The higher end of the port range - */ - void setToPort(uint16_t toPort) { m_ToPort = toPort; } - }; - - - - /** - * @class MacAddressFilter - * A class for filtering Ethernet traffic by MAC addresses, for example: "ether src 12:34:56:78:90:12" or "ether dst "10:29:38:47:56:10:29"
- * For deeper understanding of the filter concept please refer to PcapFilter.h - */ - class MacAddressFilter : public IFilterWithDirection - { - private: - MacAddress m_MacAddress; - public: - /** - * A constructor that gets the MAC address and the direction and creates the filter with them - * @param[in] address The MAC address to use for filtering - * @param[in] dir The MAC address direction to filter (source or destination) - */ - MacAddressFilter(MacAddress address, Direction dir) : IFilterWithDirection(dir), m_MacAddress(address) {} - - void parseToString(std::string& result); - - /** - * Set the MAC address - * @param[in] address The MAC address to use for filtering - */ - void setMacAddress(MacAddress address) { m_MacAddress = address; } - }; - - - - /** - * @class EtherTypeFilter - * A class for filtering by EtherType field of the Ethernet protocol. This enables to filter packets from certain protocols, such as ARP, IPv4, - * IPv6, VLAN tags, etc.
- * For deeper understanding of the filter concept please refer to PcapFilter.h - */ - class EtherTypeFilter : public GeneralFilter - { - private: - uint16_t m_EtherType; - public: - /** - * A constructor that gets the EtherType and creates the filter with it - * @param[in] etherType The EtherType value to create the filter with - */ - explicit EtherTypeFilter(uint16_t etherType) : m_EtherType(etherType) {} - - void parseToString(std::string& result); - - /** - * Set the EtherType value - * @param[in] etherType The EtherType value to create the filter with - */ - void setEtherType(uint16_t etherType) { m_EtherType = etherType; } - }; - - - - /** - * @class AndFilter - * A class for connecting several filters into one filter with logical "and" between them. For example: if the 2 filters are: "IPv4 address = - * x.x.x.x" + "TCP port dst = 80", then the new filter will be: "IPv4 address = x.x.x.x _AND_ TCP port dst = 80"
- * This class follows the composite design pattern
- * For deeper understanding of the filter concept please refer to PcapFilter.h - * @todo add some methods: "addFilter", "removeFilter", "clearAllFilter" - */ - class AndFilter : public GeneralFilter - { - private: - std::vector m_FilterList; - public: - - /** - * An empty constructor for this class. Use addFilter() to add filters to the and condition - */ - AndFilter() {} - - /** - * A constructor that gets a list of pointers to filters and creates one filter from all filters with logical "and" between them - * @param[in] filters The list of pointers to filters - */ - explicit AndFilter(std::vector& filters); - - /** - * Add filter to the and condition - * @param[in] filter The filter to add - */ - void addFilter(GeneralFilter* filter) { m_FilterList.push_back(filter); } - - /** - * Remove the current filters and set new ones - * @param[in] filters The new filters to set. The previous ones will be removed - */ - void setFilters(std::vector& filters); - - void parseToString(std::string& result); - }; - - - - /** - * @class OrFilter - * A class for connecting several filters into one filter with logical "or" between them. For example: if the 2 filters are: "IPv4 address = - * x.x.x.x" + "TCP port dst = 80", then the new filter will be: "IPv4 address = x.x.x.x _OR_ TCP port dst = 80"
- * This class follows the composite design pattern
- * For deeper understanding of the filter concept please refer to PcapFilter.h - * @todo add some methods: "addFilter", "removeFilter", "clearAllFilter" - */ - class OrFilter : public GeneralFilter - { - private: - std::vector m_FilterList; - public: - - /** - * An empty constructor for this class. Use addFilter() to add filters to the or condition - */ - OrFilter() {} - - /** - * A constructor that gets a list of pointers to filters and creates one filter from all filters with logical "or" between them - * @param[in] filters The list of pointers to filters - */ - explicit OrFilter(std::vector& filters); - - /** - * Add filter to the or condition - * @param[in] filter The filter to add - */ - void addFilter(GeneralFilter* filter) { m_FilterList.push_back(filter); } - - void parseToString(std::string& result); - }; - - - - /** - * @class NotFilter - * A class for creating a filter which is inverse to another filter
- * For deeper understanding of the filter concept please refer to PcapFilter.h - */ - class NotFilter : public GeneralFilter - { - private: - GeneralFilter* m_FilterToInverse; - public: - /** - * A constructor that gets a pointer to a filter and create the inverse version of it - * @param[in] filterToInverse A pointer to filter which the created filter be the inverse of - */ - explicit NotFilter(GeneralFilter* filterToInverse) { m_FilterToInverse = filterToInverse; } - - void parseToString(std::string& result); - - /** - * Set a filter to create an inverse filter from - * @param[in] filterToInverse A pointer to filter which the created filter be the inverse of - */ - void setFilter(GeneralFilter* filterToInverse) { m_FilterToInverse = filterToInverse; } - }; - - - - /** - * @class ProtoFilter - * A class for filtering traffic by protocol. Notice not all protocols are supported, only the following are supported: - * ::TCP, ::UDP, ::ICMP, ::VLAN, ::IPv4, ::IPv6, ::ARP, ::Ethernet, ::GRE (distinguish between ::GREv0 and ::GREv1 is not supported), - * ::IGMP (distinguish between ::IGMPv1, ::IGMPv2 and ::IGMPv3 is not supported).
- * For deeper understanding of the filter concept please refer to PcapFilter.h - */ - class ProtoFilter : public GeneralFilter - { - private: - ProtocolType m_Proto; - public: - /** - * A constructor that gets the protocol and creates the filter - * @param[in] proto The protocol to filter, only packets matching this protocol will be received. Please note not all protocols are - * supported. List of supported protocols is found in the class description - */ - explicit ProtoFilter(ProtocolType proto) : m_Proto(proto) {} - - void parseToString(std::string& result); - - /** - * Set the protocol to filter with - * @param[in] proto The protocol to filter, only packets matching this protocol will be received. Please note not all protocols are - * supported. List of supported protocols is found in the class description - */ - void setProto(ProtocolType proto) { m_Proto = proto; } - }; - - - - /** - * @class ArpFilter - * A class for filtering ARP packets according the ARP opcode. When using this filter only ARP packets with the relevant opcode will be - * received
- * For deeper understanding of the filter concept please refer to PcapFilter.h - */ - class ArpFilter : public GeneralFilter - { - private: - ArpOpcode m_OpCode; - public: - /** - * A constructor that get the ARP opcode and creates the filter - * @param[in] opCode The ARP opcode: ::ARP_REQUEST or ::ARP_REPLY - */ - explicit ArpFilter(ArpOpcode opCode) : m_OpCode(opCode) {} - - void parseToString(std::string& result); - - /** - * Set the ARP opcode - * @param[in] opCode The ARP opcode: ::ARP_REQUEST or ::ARP_REPLY - */ - void setOpCode(ArpOpcode opCode) { m_OpCode = opCode; } - }; - - - - /** - * @class VlanFilter - * A class for filtering VLAN tagged packets by VLAN ID. When using this filter only packets tagged with VLAN which has the specific VLAN ID - * will be received
- * For deeper understanding of the filter concept please refer to PcapFilter.h - */ - class VlanFilter : public GeneralFilter - { - private: - uint16_t m_VlanID; - public: - /** - * A constructor the gets the VLAN ID and creates the filter - * @param[in] vlanId The VLAN ID to use for the filter - */ - explicit VlanFilter(uint16_t vlanId) : m_VlanID(vlanId) {} - - void parseToString(std::string& result); - - /** - * Set the VLAN ID of the filter - * @param[in] vlanId The VLAN ID to use for the filter - */ - void setVlanID(uint16_t vlanId) { m_VlanID = vlanId; } - }; - - - - /** - * @class TcpFlagsFilter - * A class for filtering only TCP packets which certain TCP flags are set in them
- * For deeper understanding of the filter concept please refer to PcapFilter.h - */ - class TcpFlagsFilter : public GeneralFilter - { - public: - /** - * An enum of all TCP flags that can be use in the filter - */ - enum TcpFlags - { - /** TCP FIN flag */ - tcpFin = 1, - /** TCP SYN flag */ - tcpSyn = 2, - /** TCP RST flag */ - tcpRst = 4, - /** TCP PSH flag */ - tcpPush = 8, - /** TCP ACK flag */ - tcpAck = 16, - /** TCP URG flag */ - tcpUrg = 32 - }; - - /** - * An enum for representing 2 type of matches: match only packets that contain all flags defined in the filter or match packets that - * contain at least one of the flags defined in the filter - */ - enum MatchOptions - { - /** Match only packets that contain all flags defined in the filter */ - MatchAll, - /** Match packets that contain at least one of the flags defined in the filter */ - MatchOneAtLeast - }; - private: - uint8_t m_TcpFlagsBitMask; - MatchOptions m_MatchOption; - public: - /** - * A constructor that gets a 1-byte bitmask containing all TCP flags participating in the filter and the match option, and - * creates the filter - * @param[in] tcpFlagBitMask A 1-byte bitmask containing all TCP flags participating in the filter. This parameter can contain the - * following value for example: TcpFlagsFilter::tcpSyn | TcpFlagsFilter::tcpAck | TcpFlagsFilter::tcpUrg - * @param[in] matchOption The match option: TcpFlagsFilter::MatchAll or TcpFlagsFilter::MatchOneAtLeast - */ - TcpFlagsFilter(uint8_t tcpFlagBitMask, MatchOptions matchOption) : m_TcpFlagsBitMask(tcpFlagBitMask), m_MatchOption(matchOption) {} - - /** - * Set the TCP flags and the match option - * @param[in] tcpFlagBitMask A 1-byte bitmask containing all TCP flags participating in the filter. This parameter can contain the - * following value for example: TcpFlagsFilter::tcpSyn | TcpFlagsFilter::tcpAck | TcpFlagsFilter::tcpUrg - * @param[in] matchOption The match option: TcpFlagsFilter::MatchAll or TcpFlagsFilter::MatchOneAtLeast - */ - void setTcpFlagsBitMask(uint8_t tcpFlagBitMask, MatchOptions matchOption) { m_TcpFlagsBitMask = tcpFlagBitMask; m_MatchOption = matchOption; } - - void parseToString(std::string& result); - }; - - - - /** - * @class TcpWindowSizeFilter - * A class for filtering TCP packets that matches TCP window-size criteria
- * For deeper understanding of the filter concept please refer to PcapFilter.h - */ - class TcpWindowSizeFilter : public IFilterWithOperator - { - private: - uint16_t m_WindowSize; - public: - /** - * A constructor that get the window-size and operator and creates the filter. For example: "filter all TCP packets with window-size - * less than 1000" - * @param[in] windowSize The window-size value that will be used in the filter - * @param[in] op The operator to use (e.g "equal", "greater than", etc.) - */ - TcpWindowSizeFilter(uint16_t windowSize, FilterOperator op) : IFilterWithOperator(op), m_WindowSize(windowSize) {} - - void parseToString(std::string& result); - - /** - * Set window-size value - * @param[in] windowSize The window-size value that will be used in the filter - */ - void setWindowSize(uint16_t windowSize) { m_WindowSize = windowSize; } - }; - - - - /** - * @class UdpLengthFilter - * A class for filtering UDP packets that matches UDP length criteria
- * For deeper understanding of the filter concept please refer to PcapFilter.h - */ - class UdpLengthFilter : public IFilterWithOperator - { - private: - uint16_t m_Length; - public: - /** - * A constructor that get the UDP length and operator and creates the filter. For example: "filter all UDP packets with length - * greater or equal to 500" - * @param[in] length The length value that will be used in the filter - * @param[in] op The operator to use (e.g "equal", "greater than", etc.) - */ - UdpLengthFilter(uint16_t length, FilterOperator op) : IFilterWithOperator(op), m_Length(length) {} - - void parseToString(std::string& result); - - /** - * Set length value - * @param[in] length The length value that will be used in the filter - */ - void setLength(uint16_t length) { m_Length = length; } - }; + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp { +// Forward Declaration - used in GeneralFilter +class RawPacket; + +/** + * An enum that contains direction (source or destination) + */ +typedef enum { + /** Source */ + SRC, + /** Destination */ + DST, + /** Source or destination */ + SRC_OR_DST +} Direction; + +/** + * Supported operators enum + */ +typedef enum { + /** Equals */ + EQUALS, + /** Not equals */ + NOT_EQUALS, + /** Greater than */ + GREATER_THAN, + /** Greater or equal */ + GREATER_OR_EQUAL, + /** Less than */ + LESS_THAN, + /** Less or equal */ + LESS_OR_EQUAL +} FilterOperator; + +/** + * @class BpfFilterWrapper + * A wrapper class for BPF filtering. Enables setting a BPF filter and matching + * it against a packet + */ +class BpfFilterWrapper { + private: + std::string m_FilterStr; + LinkLayerType m_LinkType; + bpf_program* m_Program; + + void freeProgram(); + + public: + /** + * A c'tor for this class + */ + BpfFilterWrapper(); + + /** + * A d'tor for this class. Makes sure to clear the bpf_program object if was + * previously set. + */ + ~BpfFilterWrapper(); + + /** + * Set a filter. This method receives a filter in BPF syntax + * (https://biot.com/capstats/bpf.html) and an optional link type, compiles + * them, and if compilation is successful it stores the filter. + * @param[in] filter A filter in BPF syntax + * @param[in] linkType An optional parameter to set the filter's link type. + * The default is LINKTYPE_ETHERNET + * @return True if compilation is successful and filter is stored in side this + * object, false otherwise + */ + bool setFilter(const std::string& filter, + LinkLayerType linkType = LINKTYPE_ETHERNET); + + /** + * Match a packet with the filter stored in this object. If the filter is + * empty the method returns "true". If the link type of the raw packet is + * different than the one set in setFilter(), the filter will be re-compiled + * and stored in the object. + * @param[in] rawPacket A pointer to a raw packet which the filter will be + * matched against + * @return True if the filter matches (or if it's empty). False if the packet + * doesn't match or if the filter could not be compiled + */ + bool matchPacketWithFilter(const RawPacket* rawPacket); + + /** + * Match a packet data with the filter stored in this object. If the filter is + * empty the method returns "true". If the link type provided is different + * than the one set in setFilter(), the filter will be re-compiled and stored + * in the object. + * @param[in] packetData A byte stream containing the packet data + * @param[in] packetDataLength The length in [bytes] of the byte stream + * @param[in] packetTimestamp The packet timestamp + * @param[in] linkType The packet link type + * @return True if the filter matches (or if it's empty). False if the packet + * doesn't match or if the filter could not be compiled + */ + bool matchPacketWithFilter(const uint8_t* packetData, + uint32_t packetDataLength, + timespec packetTimestamp, uint16_t linkType); +}; + +/** + * @class GeneralFilter + * The base class for all filter classes. This class is virtual and abstract, + * hence cannot be instantiated.
For deeper understanding of the filter + * concept please refer to PcapFilter.h + */ +class GeneralFilter { + protected: + BpfFilterWrapper m_BpfWrapper; + + public: + /** + * A method that parses the class instance into BPF string format + * @param[out] result An empty string that the parsing will be written into. + * If the string isn't empty, its content will be overridden + */ + virtual void parseToString(std::string& result) = 0; + + /** + * Match a raw packet with a given BPF filter. + * @param[in] rawPacket A pointer to the raw packet to match the BPF filter + * with + * @return True if a raw packet matches the BPF filter or false otherwise + */ + bool matchPacketWithFilter(RawPacket* rawPacket); + + GeneralFilter() {} + + /** + * Virtual destructor, frees the bpf program + */ + virtual ~GeneralFilter() {} +}; + +/** + * @class BPFStringFilter + * This class can be loaded with a BPF filter string and then can be used to + * verify the string is valid.
+ */ +class BPFStringFilter : public GeneralFilter { + private: + const std::string m_FilterStr; + + public: + explicit BPFStringFilter(const std::string& filterStr) + : m_FilterStr(filterStr) {} + + virtual ~BPFStringFilter() {} + + /** + * A method that parses the class instance into BPF string format + * @param[out] result An empty string that the parsing will be written into. + * If the string isn't empty, its content will be overridden If the filter is + * not valid the result will be an empty string + */ + virtual void parseToString(std::string& result); + + /** + * Verify the filter is valid + * @return True if the filter is valid or false otherwise + */ + bool verifyFilter(); +}; + +/** + * @class IFilterWithDirection + * An abstract class that is the base class for all filters which contain a + * direction (source or destination). This class cannot be instantiated
For + * deeper understanding of the filter concept please refer to PcapFilter.h + */ +class IFilterWithDirection : public GeneralFilter { + private: + Direction m_Dir; + + protected: + void parseDirection(std::string& directionAsString); + Direction getDir() const { return m_Dir; } + explicit IFilterWithDirection(Direction dir) { m_Dir = dir; } + + public: + /** + * Set the direction for the filter (source or destination) + * @param[in] dir The direction + */ + void setDirection(Direction dir) { m_Dir = dir; } +}; + +/** + * @class IFilterWithOperator + * An abstract class that is the base class for all filters which contain an + * operator (e.g X equals Y; A is greater than B; Z1 not equals Z2, etc.). This + * class cannot be instantiated
For deeper understanding of the filter + * concept please refer to PcapFilter.h + */ +class IFilterWithOperator : public GeneralFilter { + private: + FilterOperator m_Operator; + + protected: + std::string parseOperator(); + FilterOperator getOperator() const { return m_Operator; } + explicit IFilterWithOperator(FilterOperator op) { m_Operator = op; } + + public: + /** + * Set the operator for the filter + * @param[in] op The operator to set + */ + void setOperator(FilterOperator op) { m_Operator = op; } +}; + +/** + * @class IPFilter + * A class for representing IPv4 address filter, equivalent to "net src x.x.x.x" + * or "net dst x.x.x.x"
For deeper understanding of the filter concept + * please refer to PcapFilter.h + * @todo Add IPv6 filtering support + */ +class IPFilter : public IFilterWithDirection { + private: + std::string m_Address; + std::string m_IPv4Mask; + int m_Len; + void convertToIPAddressWithMask(std::string& ipAddrmodified, + std::string& mask) const; + void convertToIPAddressWithLen(std::string& ipAddrmodified) const; + + public: + /** + * The basic constructor that creates the filter from an IPv4 address and + * direction (source or destination) + * @param[in] ipAddress The IPv4 address to build the filter with. If this + * address is not a valid IPv4 address an error will be written to log and + * parsing this filter will fail + * @param[in] dir The address direction to filter (source or destination) + */ + IPFilter(const std::string& ipAddress, Direction dir) + : IFilterWithDirection(dir), m_Address(ipAddress), m_IPv4Mask(""), + m_Len(0) {} + + /** + * A constructor that enable to filter only part of the address by using a + * mask (aka subnet). For example: "filter only IP addresses that matches the + * subnet 10.0.0.x" + * @param[in] ipAddress The IPv4 address to use. Only the part of the address + * that is not masked will be matched. For example: if the address is + * "1.2.3.4" and the mask is "255.255.255.0" than the part of the address that + * will be matched is "1.2.3.X". If this address is not a valid IPv4 address + * an error will be written to log and parsing this filter will fail + * @param[in] dir The address direction to filter (source or destination) + * @param[in] ipv4Mask The mask to use. Mask should also be in a valid IPv4 + * format (i.e x.x.x.x), otherwise parsing this filter will fail + */ + IPFilter(const std::string& ipAddress, Direction dir, + const std::string& ipv4Mask) + : IFilterWithDirection(dir), m_Address(ipAddress), m_IPv4Mask(ipv4Mask), + m_Len(0) {} + + /** + * A constructor that enables to filter by a subnet. For example: "filter only + * IP addresses that matches the subnet 10.0.0.3/24" which means the part of + * the address that will be matched is "10.0.0.X" + * @param[in] ipAddress The IPv4 address to use. Only the part of the address + * that is not masked will be matched. For example: if the address is + * "1.2.3.4" and the subnet is "/24" than the part of the address that will be + * matched is "1.2.3.X". If this address is not a valid IPv4 address an error + * will be written to log and parsing this filter will fail + * @param[in] dir The address direction to filter (source or destination) + * @param[in] len The subnet to use (e.g "/24") + */ + IPFilter(const std::string& ipAddress, Direction dir, int len) + : IFilterWithDirection(dir), m_Address(ipAddress), m_IPv4Mask(""), + m_Len(len) {} + + void parseToString(std::string& result); + + /** + * Set the IPv4 address + * @param[in] ipAddress The IPv4 address to build the filter with. If this + * address is not a valid IPv4 address an error will be written to log and + * parsing this filter will fail + */ + void setAddr(const std::string& ipAddress) { m_Address = ipAddress; } + + /** + * Set the IPv4 mask + * @param[in] ipv4Mask The mask to use. Mask should also be in a valid IPv4 + * format (i.e x.x.x.x), otherwise parsing this filter will fail + */ + void setMask(const std::string& ipv4Mask) { + m_IPv4Mask = ipv4Mask; + m_Len = 0; + } + + /** + * Set the subnet + * @param[in] len The subnet to use (e.g "/24") + */ + void setLen(int len) { + m_IPv4Mask = ""; + m_Len = len; + } +}; + +/** + * @class IPv4IDFilter + * A class for filtering IPv4 traffic by IP ID field of the IPv4 protocol, For + * example: "filter only IPv4 traffic which IP ID is greater than 1234"
For + * deeper understanding of the filter concept please refer to PcapFilter.h + */ +class IPv4IDFilter : public IFilterWithOperator { + private: + uint16_t m_IpID; + + public: + /** + * A constructor that gets the IP ID to filter and the operator and creates + * the filter out of them + * @param[in] ipID The IP ID to filter + * @param[in] op The operator to use (e.g "equal", "greater than", etc.) + */ + IPv4IDFilter(uint16_t ipID, FilterOperator op) + : IFilterWithOperator(op), m_IpID(ipID) {} + + void parseToString(std::string& result); + + /** + * Set the IP ID to filter + * @param[in] ipID The IP ID to filter + */ + void setIpID(uint16_t ipID) { m_IpID = ipID; } +}; + +/** + * @class IPv4TotalLengthFilter + * A class for filtering IPv4 traffic by "total length" field of the IPv4 + * protocol, For example: "filter only IPv4 traffic which "total length" value + * is less than 60B"
For deeper understanding of the filter concept please + * refer to PcapFilter.h + */ +class IPv4TotalLengthFilter : public IFilterWithOperator { + private: + uint16_t m_TotalLength; + + public: + /** + * A constructor that gets the total length to filter and the operator and + * creates the filter out of them + * @param[in] totalLength The total length value to filter + * @param[in] op The operator to use (e.g "equal", "greater than", etc.) + */ + IPv4TotalLengthFilter(uint16_t totalLength, FilterOperator op) + : IFilterWithOperator(op), m_TotalLength(totalLength) {} + + void parseToString(std::string& result); + + /** + * Set the total length value + * @param[in] totalLength The total length value to filter + */ + void setTotalLength(uint16_t totalLength) { m_TotalLength = totalLength; } +}; + +/** + * @class PortFilter + * A class for filtering TCP or UDP traffic by port, for example: "dst port 80" + * or "src port 12345"
For deeper understanding of the filter concept please + * refer to PcapFilter.h + */ +class PortFilter : public IFilterWithDirection { + private: + std::string m_Port; + void portToString(uint16_t portAsInt); + + public: + /** + * A constructor that gets the port and the direction and creates the filter + * @param[in] port The port to create the filter with + * @param[in] dir The port direction to filter (source or destination) + */ + PortFilter(uint16_t port, Direction dir); + + void parseToString(std::string& result); + + /** + * Set the port + * @param[in] port The port to create the filter with + */ + void setPort(uint16_t port) { portToString(port); } +}; + +/** + * @class PortRangeFilter + * A class for filtering TCP or UDP port ranges, meaning match only packets + * which port is within this range, for example: "src portrange 1000-2000" will + * match only TCP or UDP traffic which source port is in the range of 1000 - + * 2000
For deeper understanding of the filter concept please refer to + * PcapFilter.h + */ +class PortRangeFilter : public IFilterWithDirection { + private: + uint16_t m_FromPort; + uint16_t m_ToPort; + + public: + /** + * A constructor that gets the port range the the direction and creates the + * filter with them + * @param[in] fromPort The lower end of the port range + * @param[in] toPort The higher end of the port range + * @param[in] dir The port range direction to filter (source or destination) + */ + PortRangeFilter(uint16_t fromPort, uint16_t toPort, Direction dir) + : IFilterWithDirection(dir), m_FromPort(fromPort), m_ToPort(toPort) {} + + void parseToString(std::string& result); + + /** + * Set the lower end of the port range + * @param[in] fromPort The lower end of the port range + */ + void setFromPort(uint16_t fromPort) { m_FromPort = fromPort; } + + /** + * Set the higher end of the port range + * @param[in] toPort The higher end of the port range + */ + void setToPort(uint16_t toPort) { m_ToPort = toPort; } +}; + +/** + * @class MacAddressFilter + * A class for filtering Ethernet traffic by MAC addresses, for example: "ether + * src 12:34:56:78:90:12" or "ether dst "10:29:38:47:56:10:29"
For deeper + * understanding of the filter concept please refer to PcapFilter.h + */ +class MacAddressFilter : public IFilterWithDirection { + private: + MacAddress m_MacAddress; + + public: + /** + * A constructor that gets the MAC address and the direction and creates the + * filter with them + * @param[in] address The MAC address to use for filtering + * @param[in] dir The MAC address direction to filter (source or destination) + */ + MacAddressFilter(MacAddress address, Direction dir) + : IFilterWithDirection(dir), m_MacAddress(address) {} + + void parseToString(std::string& result); + + /** + * Set the MAC address + * @param[in] address The MAC address to use for filtering + */ + void setMacAddress(MacAddress address) { m_MacAddress = address; } +}; + +/** + * @class EtherTypeFilter + * A class for filtering by EtherType field of the Ethernet protocol. This + * enables to filter packets from certain protocols, such as ARP, IPv4, IPv6, + * VLAN tags, etc.
For deeper understanding of the filter concept please + * refer to PcapFilter.h + */ +class EtherTypeFilter : public GeneralFilter { + private: + uint16_t m_EtherType; + + public: + /** + * A constructor that gets the EtherType and creates the filter with it + * @param[in] etherType The EtherType value to create the filter with + */ + explicit EtherTypeFilter(uint16_t etherType) : m_EtherType(etherType) {} + + void parseToString(std::string& result); + + /** + * Set the EtherType value + * @param[in] etherType The EtherType value to create the filter with + */ + void setEtherType(uint16_t etherType) { m_EtherType = etherType; } +}; + +/** + * @class AndFilter + * A class for connecting several filters into one filter with logical "and" + * between them. For example: if the 2 filters are: "IPv4 address = x.x.x.x" + + * "TCP port dst = 80", then the new filter will be: "IPv4 address = x.x.x.x + * _AND_ TCP port dst = 80"
This class follows the composite design + * pattern
For deeper understanding of the filter concept please refer to + * PcapFilter.h + * @todo add some methods: "addFilter", "removeFilter", "clearAllFilter" + */ +class AndFilter : public GeneralFilter { + private: + std::vector m_FilterList; + + public: + /** + * An empty constructor for this class. Use addFilter() to add filters to the + * and condition + */ + AndFilter() {} + + /** + * A constructor that gets a list of pointers to filters and creates one + * filter from all filters with logical "and" between them + * @param[in] filters The list of pointers to filters + */ + explicit AndFilter(std::vector& filters); + + /** + * Add filter to the and condition + * @param[in] filter The filter to add + */ + void addFilter(GeneralFilter* filter) { m_FilterList.push_back(filter); } + + /** + * Remove the current filters and set new ones + * @param[in] filters The new filters to set. The previous ones will be + * removed + */ + void setFilters(std::vector& filters); + + void parseToString(std::string& result); +}; + +/** + * @class OrFilter + * A class for connecting several filters into one filter with logical "or" + * between them. For example: if the 2 filters are: "IPv4 address = x.x.x.x" + + * "TCP port dst = 80", then the new filter will be: "IPv4 address = x.x.x.x + * _OR_ TCP port dst = 80"
This class follows the composite design + * pattern
For deeper understanding of the filter concept please refer to + * PcapFilter.h + * @todo add some methods: "addFilter", "removeFilter", "clearAllFilter" + */ +class OrFilter : public GeneralFilter { + private: + std::vector m_FilterList; + + public: + /** + * An empty constructor for this class. Use addFilter() to add filters to the + * or condition + */ + OrFilter() {} + + /** + * A constructor that gets a list of pointers to filters and creates one + * filter from all filters with logical "or" between them + * @param[in] filters The list of pointers to filters + */ + explicit OrFilter(std::vector& filters); + + /** + * Add filter to the or condition + * @param[in] filter The filter to add + */ + void addFilter(GeneralFilter* filter) { m_FilterList.push_back(filter); } + + void parseToString(std::string& result); +}; + +/** + * @class NotFilter + * A class for creating a filter which is inverse to another filter
+ * For deeper understanding of the filter concept please refer to PcapFilter.h + */ +class NotFilter : public GeneralFilter { + private: + GeneralFilter* m_FilterToInverse; + + public: + /** + * A constructor that gets a pointer to a filter and create the inverse + * version of it + * @param[in] filterToInverse A pointer to filter which the created filter be + * the inverse of + */ + explicit NotFilter(GeneralFilter* filterToInverse) { + m_FilterToInverse = filterToInverse; + } + + void parseToString(std::string& result); + + /** + * Set a filter to create an inverse filter from + * @param[in] filterToInverse A pointer to filter which the created filter be + * the inverse of + */ + void setFilter(GeneralFilter* filterToInverse) { + m_FilterToInverse = filterToInverse; + } +}; + +/** + * @class ProtoFilter + * A class for filtering traffic by protocol. Notice not all protocols are + * supported, only the following are supported: + * ::TCP, ::UDP, ::ICMP, ::VLAN, ::IPv4, ::IPv6, ::ARP, ::Ethernet, ::GRE + * (distinguish between ::GREv0 and ::GREv1 is not supported), + * ::IGMP (distinguish between ::IGMPv1, ::IGMPv2 and ::IGMPv3 is not + * supported).
For deeper understanding of the filter concept please refer + * to PcapFilter.h + */ +class ProtoFilter : public GeneralFilter { + private: + ProtocolType m_Proto; + + public: + /** + * A constructor that gets the protocol and creates the filter + * @param[in] proto The protocol to filter, only packets matching this + * protocol will be received. Please note not all protocols are supported. + * List of supported protocols is found in the class description + */ + explicit ProtoFilter(ProtocolType proto) : m_Proto(proto) {} + + void parseToString(std::string& result); + + /** + * Set the protocol to filter with + * @param[in] proto The protocol to filter, only packets matching this + * protocol will be received. Please note not all protocols are supported. + * List of supported protocols is found in the class description + */ + void setProto(ProtocolType proto) { m_Proto = proto; } +}; + +/** + * @class ArpFilter + * A class for filtering ARP packets according the ARP opcode. When using this + * filter only ARP packets with the relevant opcode will be received
For + * deeper understanding of the filter concept please refer to PcapFilter.h + */ +class ArpFilter : public GeneralFilter { + private: + ArpOpcode m_OpCode; + + public: + /** + * A constructor that get the ARP opcode and creates the filter + * @param[in] opCode The ARP opcode: ::ARP_REQUEST or ::ARP_REPLY + */ + explicit ArpFilter(ArpOpcode opCode) : m_OpCode(opCode) {} + + void parseToString(std::string& result); + + /** + * Set the ARP opcode + * @param[in] opCode The ARP opcode: ::ARP_REQUEST or ::ARP_REPLY + */ + void setOpCode(ArpOpcode opCode) { m_OpCode = opCode; } +}; + +/** + * @class VlanFilter + * A class for filtering VLAN tagged packets by VLAN ID. When using this filter + * only packets tagged with VLAN which has the specific VLAN ID will be received + *
For deeper understanding of the filter concept please refer to + * PcapFilter.h + */ +class VlanFilter : public GeneralFilter { + private: + uint16_t m_VlanID; + + public: + /** + * A constructor the gets the VLAN ID and creates the filter + * @param[in] vlanId The VLAN ID to use for the filter + */ + explicit VlanFilter(uint16_t vlanId) : m_VlanID(vlanId) {} + + void parseToString(std::string& result); + + /** + * Set the VLAN ID of the filter + * @param[in] vlanId The VLAN ID to use for the filter + */ + void setVlanID(uint16_t vlanId) { m_VlanID = vlanId; } +}; + +/** + * @class TcpFlagsFilter + * A class for filtering only TCP packets which certain TCP flags are set in + * them
For deeper understanding of the filter concept please refer to + * PcapFilter.h + */ +class TcpFlagsFilter : public GeneralFilter { + public: + /** + * An enum of all TCP flags that can be use in the filter + */ + enum TcpFlags { + /** TCP FIN flag */ + tcpFin = 1, + /** TCP SYN flag */ + tcpSyn = 2, + /** TCP RST flag */ + tcpRst = 4, + /** TCP PSH flag */ + tcpPush = 8, + /** TCP ACK flag */ + tcpAck = 16, + /** TCP URG flag */ + tcpUrg = 32 + }; + + /** + * An enum for representing 2 type of matches: match only packets that contain + * all flags defined in the filter or match packets that contain at least one + * of the flags defined in the filter + */ + enum MatchOptions { + /** Match only packets that contain all flags defined in the filter */ + MatchAll, + /** Match packets that contain at least one of the flags defined in the + filter */ + MatchOneAtLeast + }; + + private: + uint8_t m_TcpFlagsBitMask; + MatchOptions m_MatchOption; + + public: + /** + * A constructor that gets a 1-byte bitmask containing all TCP flags + * participating in the filter and the match option, and creates the filter + * @param[in] tcpFlagBitMask A 1-byte bitmask containing all TCP flags + * participating in the filter. This parameter can contain the following value + * for example: TcpFlagsFilter::tcpSyn | TcpFlagsFilter::tcpAck | + * TcpFlagsFilter::tcpUrg + * @param[in] matchOption The match option: TcpFlagsFilter::MatchAll or + * TcpFlagsFilter::MatchOneAtLeast + */ + TcpFlagsFilter(uint8_t tcpFlagBitMask, MatchOptions matchOption) + : m_TcpFlagsBitMask(tcpFlagBitMask), m_MatchOption(matchOption) {} + + /** + * Set the TCP flags and the match option + * @param[in] tcpFlagBitMask A 1-byte bitmask containing all TCP flags + * participating in the filter. This parameter can contain the following value + * for example: TcpFlagsFilter::tcpSyn | TcpFlagsFilter::tcpAck | + * TcpFlagsFilter::tcpUrg + * @param[in] matchOption The match option: TcpFlagsFilter::MatchAll or + * TcpFlagsFilter::MatchOneAtLeast + */ + void setTcpFlagsBitMask(uint8_t tcpFlagBitMask, MatchOptions matchOption) { + m_TcpFlagsBitMask = tcpFlagBitMask; + m_MatchOption = matchOption; + } + + void parseToString(std::string& result); +}; + +/** + * @class TcpWindowSizeFilter + * A class for filtering TCP packets that matches TCP window-size criteria
+ * For deeper understanding of the filter concept please refer to PcapFilter.h + */ +class TcpWindowSizeFilter : public IFilterWithOperator { + private: + uint16_t m_WindowSize; + + public: + /** + * A constructor that get the window-size and operator and creates the filter. + * For example: "filter all TCP packets with window-size less than 1000" + * @param[in] windowSize The window-size value that will be used in the filter + * @param[in] op The operator to use (e.g "equal", "greater than", etc.) + */ + TcpWindowSizeFilter(uint16_t windowSize, FilterOperator op) + : IFilterWithOperator(op), m_WindowSize(windowSize) {} + + void parseToString(std::string& result); + + /** + * Set window-size value + * @param[in] windowSize The window-size value that will be used in the filter + */ + void setWindowSize(uint16_t windowSize) { m_WindowSize = windowSize; } +}; + +/** + * @class UdpLengthFilter + * A class for filtering UDP packets that matches UDP length criteria
+ * For deeper understanding of the filter concept please refer to PcapFilter.h + */ +class UdpLengthFilter : public IFilterWithOperator { + private: + uint16_t m_Length; + + public: + /** + * A constructor that get the UDP length and operator and creates the filter. + * For example: "filter all UDP packets with length greater or equal to 500" + * @param[in] length The length value that will be used in the filter + * @param[in] op The operator to use (e.g "equal", "greater than", etc.) + */ + UdpLengthFilter(uint16_t length, FilterOperator op) + : IFilterWithOperator(op), m_Length(length) {} + + void parseToString(std::string& result); + + /** + * Set length value + * @param[in] length The length value that will be used in the filter + */ + void setLength(uint16_t length) { m_Length = length; } +}; } // namespace pcpp diff --git a/Pcap++/header/PcapLiveDevice.h b/Pcap++/header/PcapLiveDevice.h index 06fb03c6f1..e3e1009696 100644 --- a/Pcap++/header/PcapLiveDevice.h +++ b/Pcap++/header/PcapLiveDevice.h @@ -1,11 +1,11 @@ -//TODO: replace all these defines with #pragma once +// TODO: replace all these defines with #pragma once #ifndef PCAPPP_LIVE_DEVICE #define PCAPPP_LIVE_DEVICE #include -#include #include #include +#include #include "IpAddress.h" #include "Packet.h" @@ -20,552 +20,695 @@ typedef struct pcap_addr pcap_addr_t; /// @file /** -* \namespace pcpp -* \brief The main namespace for the PcapPlusPlus lib -*/ -namespace pcpp -{ - - class PcapLiveDevice; - - /** - * @typedef OnPacketArrivesCallback - * A callback that is called when a packet is captured by PcapLiveDevice - * @param[in] pPacket A pointer to the raw packet - * @param[in] pDevice A pointer to the PcapLiveDevice instance - * @param[in] userCookie A pointer to the object put by the user when packet capturing stared - */ - typedef void (*OnPacketArrivesCallback)(RawPacket* pPacket, PcapLiveDevice* pDevice, void* userCookie); - - /** - * @typedef OnPacketArrivesStopBlocking - * A callback that is called when a packet is captured by PcapLiveDevice - * @param[in] pPacket A pointer to the raw packet - * @param[in] pDevice A pointer to the PcapLiveDevice instance - * @param[in] userCookie A pointer to the object put by the user when packet capturing stared - * @return True when main thread should stop blocking or false otherwise - */ - typedef bool (*OnPacketArrivesStopBlocking)(RawPacket* pPacket, PcapLiveDevice* pDevice, void* userCookie); - - - /** - * @typedef OnStatsUpdateCallback - * A callback that is called periodically for stats collection if user asked to start packet capturing with periodic stats collection - * @param[in] stats A reference to the most updated stats - * @param[in] userCookie A pointer to the object put by the user when packet capturing stared - */ - typedef void (*OnStatsUpdateCallback)(IPcapDevice::PcapStats& stats, void* userCookie); - - // for internal use only - typedef void* (*ThreadStart)(void*); - - /** - * @class PcapLiveDevice - * A class that wraps a network interface (each of the interfaces listed in ifconfig/ipconfig). - * This class wraps the libpcap capabilities of capturing packets from the network, filtering packets and sending packets back to the network. - * This class is relevant for Linux applications only. On Windows the WinPcapLiveDevice (which inherits this class) is used. Both classes are - * almost similar in capabilities, the main difference between them is adapting some capabilities to the specific OS. - * This class cannot be instantiated by the user (it has a private constructor), as network interfaces aren't dynamic. Instances of - * this class (one instance per network interface) are created by PcapLiveDeviceList singleton on application startup and the user can get - * access to them by using PcapLiveDeviceList public methods such as PcapLiveDeviceList#getPcapLiveDeviceByIp()
- * Main capabilities of this class: - * - Get all available information for this network interfaces such as name, IP addresses, MAC address, MTU, etc. This information is taken - * from both libpcap and the OS - * - Capture packets from the network. Capturing is always conducted on a different thread. PcapPlusPlus creates this - * thread when capturing starts and kills it when capturing ends. This prevents the application from being stuck while waiting for packets or - * processing them. Currently only one capturing thread is allowed, so when the interface is in capture mode, no further capturing is allowed. - * In addition to capturing the user can get stats on packets that were received by the application, dropped by the NIC (due to full - * NIC buffers), etc. Stats collection can be initiated by the user by calling getStatistics() or be pushed to the user periodically by - * supplying a callback and a timeout to startCapture() - * - Send packets back to the network. Sending the packets is done on the caller thread. No additional threads are created for this task - */ - class PcapLiveDevice : public IPcapDevice - { - friend class PcapLiveDeviceList; - protected: - // This is a second descriptor for the same device. It is needed because of a bug - // that occurs in libpcap on Linux (on Windows using WinPcap/Npcap it works well): - // It's impossible to capture packets sent by the same descriptor - pcap_t* m_PcapSendDescriptor; - std::string m_Name; - std::string m_Description; - bool m_IsLoopback; - uint32_t m_DeviceMtu; - std::vector m_Addresses; - MacAddress m_MacAddress; - IPv4Address m_DefaultGateway; - std::thread m_CaptureThread; - bool m_CaptureThreadStarted; - std::thread m_StatsThread; - bool m_StatsThreadStarted; - std::atomic m_StopThread; - OnPacketArrivesCallback m_cbOnPacketArrives; - void* m_cbOnPacketArrivesUserCookie; - OnStatsUpdateCallback m_cbOnStatsUpdate; - void* m_cbOnStatsUpdateUserCookie; - OnPacketArrivesStopBlocking m_cbOnPacketArrivesBlockingMode; - void* m_cbOnPacketArrivesBlockingModeUserCookie; - int m_IntervalToUpdateStats; - RawPacketVector* m_CapturedPackets; - bool m_CaptureCallbackMode; - LinkLayerType m_LinkType; - - // c'tor is not public, there should be only one for every interface (created by PcapLiveDeviceList) - PcapLiveDevice(pcap_if_t* pInterface, bool calculateMTU, bool calculateMacAddress, bool calculateDefaultGateway); - // copy c'tor is not public - PcapLiveDevice( const PcapLiveDevice& other ); - PcapLiveDevice& operator=(const PcapLiveDevice& other); - - void setDeviceMtu(); - void setDeviceMacAddress(); - void setDefaultGateway(); - - // threads - void captureThreadMain(); - void statsThreadMain(); - - static void onPacketArrives(uint8_t* user, const struct pcap_pkthdr* pkthdr, const uint8_t* packet); - static void onPacketArrivesNoCallback(uint8_t* user, const struct pcap_pkthdr* pkthdr, const uint8_t* packet); - static void onPacketArrivesBlockingMode(uint8_t* user, const struct pcap_pkthdr* pkthdr, const uint8_t* packet); - public: - - /** - * The type of the live device - */ - enum LiveDeviceType - { - /** libPcap live device */ - LibPcapDevice, - /** WinPcap/Npcap live device */ - WinPcapDevice, - /** WinPcap/Npcap Remote Capture device */ - RemoteDevice - }; - - - /** - * Device capturing mode - */ - enum DeviceMode - { - /** Only packets that their destination is this NIC are captured */ - Normal = 0, - /** All packets that arrive to the NIC are captured, even packets that their destination isn't this NIC */ - Promiscuous = 1 - }; - - - /** - * Set direction for capturing packets (you can read more here: ) - */ - enum PcapDirection - { - /** Capture traffics both incoming and outgoing */ - PCPP_INOUT = 0, - /** Only capture incoming traffics */ - PCPP_IN, - /** Only capture outgoing traffics */ - PCPP_OUT - }; - - - /** - * @struct DeviceConfiguration - * A struct that contains user configurable parameters for opening a device. All parameters have default values so - * the user isn't expected to set all parameters or understand exactly how they work - */ - struct DeviceConfiguration - { - /** Indicates whether to open the device in promiscuous or normal mode */ - DeviceMode mode; - - /** Set the packet buffer timeout in milliseconds. You can read more here: - * https://www.tcpdump.org/manpages/pcap.3pcap.html . - * Any value above 0 is considered legal, otherwise a value of 1 or -1 is used (depends on the platform) - */ - int packetBufferTimeoutMs; - - /** - * Set the packet buffer size. You can read more about the packet buffer here: - * https://www.tcpdump.org/manpages/pcap.3pcap.html . - * Any value of 100 or above is considered valid, otherwise the default value is used (which varies between different OS's). - * However, please notice that setting values which are too low or two high may result in failure to open the device. - * These too low or too high thresholds may vary between OS's, as an example please refer to this thread: - * https://stackoverflow.com/questions/11397367/issue-in-pcap-set-buffer-size - */ - int packetBufferSize; - - /** - * Set the direction for capturing packets. You can read more here: - * . - */ - PcapDirection direction; - - /** - * Set the snapshot length. Snapshot length is the amount of data for each frame that is actually captured. Note that taking - * larger snapshots both increases the amount of time it takes to process packets and, effectively, decreases the amount of - * packet buffering. This may cause packets to be lost. Note also that taking smaller snapshots will discard data from protocols - * above the transport layer, which loses information that may be important. - * You can read more here: - * https://wiki.wireshark.org/SnapLen - */ - int snapshotLength; - - /** - * Set NFLOG group. Which NFLOG group to be listened to when connecting to NFLOG device. If device is not of type NFLOG this - * attribute is ignored. - */ - unsigned int nflogGroup; - - /** - * A c'tor for this struct - * @param[in] mode The mode to open the device: promiscuous or non-promiscuous. Default value is promiscuous - * @param[in] packetBufferTimeoutMs Buffer timeout in millisecond. Default value is 0 which means set timeout of - * 1 or -1 (depends on the platform) - * @param[in] packetBufferSize The packet buffer size. Default value is 0 which means use the default value - * (varies between different OS's) - * @param[in] direction Direction for capturing packets. Default value is INOUT which means capture both incoming - * and outgoing packets (not all platforms support this) - * @param[in] snapshotLength Snapshot length for capturing packets. Default value is 0 which means use the default value. - * A snapshot length of 262144 should be big enough for maximum-size Linux loopback packets (65549) and some USB packets - * captured with USBPcap (> 131072, < 262144). A snapshot length of 65535 should be sufficient, on most if not all networks, - * to capture all the data available from the packet. - * @param[in] nflogGroup NFLOG group for NFLOG devices. Default value is 0. - */ - explicit DeviceConfiguration(DeviceMode mode = Promiscuous, int packetBufferTimeoutMs = 0, int packetBufferSize = 0, - PcapDirection direction = PCPP_INOUT, int snapshotLength = 0, unsigned int nflogGroup = 0) - { - this->mode = mode; - this->packetBufferTimeoutMs = packetBufferTimeoutMs; - this->packetBufferSize = packetBufferSize; - this->direction = direction; - this->snapshotLength = snapshotLength; - this->nflogGroup = nflogGroup; - } - }; - - - /** - * A destructor for this class - */ - virtual ~PcapLiveDevice(); - - /** - * @return The type of the device (libPcap, WinPcap/Npcap or a remote device) - */ - virtual LiveDeviceType getDeviceType() const { return LibPcapDevice; } - - /** - * @return The name of the device (e.g eth0), taken from pcap_if_t->name - */ - std::string getName() const { return m_Name; } - - /** - * @return A human-readable description of the device, taken from pcap_if_t->description. May be NULL in some interfaces - */ - std::string getDesc() const { return m_Description; } - - /** - * @return True if this interface is a loopback interface, false otherwise - */ - bool getLoopback() const { return m_IsLoopback; } - - /** - * @return The device's maximum transmission unit (MTU) in bytes - */ - virtual uint32_t getMtu() const { return m_DeviceMtu; } - - /** - * @return The device's link layer type - */ - virtual LinkLayerType getLinkType() const { return m_LinkType; } - - /** - * @return A vector containing all addresses defined for this interface, each in pcap_addr_t struct - */ - const std::vector& getAddresses() const { return m_Addresses; } - - /** - * @return The MAC address for this interface - */ - virtual MacAddress getMacAddress() const { return m_MacAddress; } - - /** - * @return The IPv4 address for this interface. If multiple IPv4 addresses are defined for this interface, the first will be picked. - * If no IPv4 addresses are defined, a zeroed IPv4 address (IPv4Address#Zero) will be returned - */ - IPv4Address getIPv4Address() const; - - /** - * @return The IPv6 address for this interface. If multiple IPv6 addresses are defined for this interface, the first will be picked. - * If no IPv6 addresses are defined, a zeroed IPv6 address (IPv6Address#Zero) will be returned - */ - IPv6Address getIPv6Address() const; - - /** - * @return The default gateway defined for this interface. If no default gateway is defined, if it's not IPv4 or if couldn't extract - * default gateway IPv4Address#Zero will be returned. If multiple gateways were defined the first one will be returned - */ - IPv4Address getDefaultGateway() const; - - /** - * @return A list of all DNS servers defined for this machine. If this list is empty it means no DNS servers were defined or they - * couldn't be extracted from some reason. This list is created in PcapLiveDeviceList class and can be also retrieved from there. - * This method exists for convenience - so it'll be possible to get this list from PcapLiveDevice as well - */ - const std::vector& getDnsServers() const; - - /** - * Start capturing packets on this network interface (device). Each time a packet is captured the onPacketArrives callback is called. - * The capture is done on a new thread created by this method, meaning all callback calls are done in a thread other than the - * caller thread. Capture process will stop and this capture thread will be terminated when calling stopCapture(). This method must be - * called after the device is opened (i.e the open() method was called), otherwise an error will be returned. - * @param[in] onPacketArrives A callback that is called each time a packet is captured - * @param[in] onPacketArrivesUserCookie A pointer to a user provided object. This object will be transferred to the onPacketArrives callback - * each time it is called. This cookie is very useful for transferring objects that give context to the capture callback, for example: - * objects that counts packets, manages flow state or manages the application state according to the packet that was captured - * @return True if capture started successfully, false if (relevant log error is printed in any case): - * - Capture is already running - * - Device is not opened - * - Capture thread could not be created - */ - virtual bool startCapture(OnPacketArrivesCallback onPacketArrives, void* onPacketArrivesUserCookie); - - /** - * Start capturing packets on this network interface (device) with periodic stats collection. Each time a packet is captured the onPacketArrives - * callback is called. In addition, each intervalInSecondsToUpdateStats seconds stats are collected from the device and the onStatsUpdate - * callback is called. Both the capture and periodic stats collection are done on new threads created by this method, each on a different thread, - * meaning all callback calls are done in threads other than the caller thread. Capture process and stats collection will stop and threads will be - * terminated when calling stopCapture(). This method must be called after the device is opened (i.e the open() method was called), otherwise an - * error will be returned. - * @param[in] onPacketArrives A callback that is called each time a packet is captured - * @param[in] onPacketArrivesUserCookie A pointer to a user provided object. This object will be transferred to the onPacketArrives callback - * each time it is called. This cookie is very useful for transferring objects that give context to the capture callback, for example: - * objects that counts packets, manages flow state or manages the application state according to the packet that was captured - * @param[in] intervalInSecondsToUpdateStats The interval in seconds to activate periodic stats collection - * @param[in] onStatsUpdate A callback that will be called each time intervalInSecondsToUpdateStats expires and stats are collected. This - * callback will contain the collected stats - * @param[in] onStatsUpdateUserCookie A pointer to a user provided object. This object will be transferred to the onStatsUpdate callback - * each time it is called - * @return True if capture started successfully, false if (relevant log error is printed in any case): - * - Capture is already running - * - Device is not opened - * - Capture thread could not be created - * - Stats collection thread could not be created - */ - virtual bool startCapture(OnPacketArrivesCallback onPacketArrives, void* onPacketArrivesUserCookie, int intervalInSecondsToUpdateStats, OnStatsUpdateCallback onStatsUpdate, void* onStatsUpdateUserCookie); - - /** - * Start capturing packets on this network interface (device) with periodic stats collection only. This means that packets arriving to the - * network interface aren't delivered to the user but only counted. Each intervalInSecondsToUpdateStats seconds stats are collected from the - * device and the onStatsUpdate callback is called with the updated counters. The periodic stats collection is done on a new thread created - * by this method, meaning all callback calls are done in threads other than the caller thread. Stats collection will stop and threads will - * be terminated when calling stopCapture(). This method must be called after the device is opened (i.e the open() method was called), - * otherwise an error will be returned. - * @param[in] intervalInSecondsToUpdateStats The interval in seconds to activate periodic stats collection - * @param[in] onStatsUpdate A callback that will be called each time intervalInSecondsToUpdateStats expires and stats are collected. This - * callback will contain the collected stats - * @param[in] onStatsUpdateUserCookie A pointer to a user provided object. This object will be transferred to the onStatsUpdate callback - * each time it is called - * @return True if capture started successfully, false if (relevant log error is printed in any case): - * - Capture is already running - * - Device is not opened - * - Stats collection thread could not be created - */ - virtual bool startCapture(int intervalInSecondsToUpdateStats, OnStatsUpdateCallback onStatsUpdate, void* onStatsUpdateUserCookie); - - /** - * Start capturing packets on this network interface (device). All captured packets are added to capturedPacketsVector, so at the end of - * the capture (when calling stopCapture()) this vector contains pointers to all captured packets in the form of RawPacket. The capture - * is done on a new thread created by this method, meaning capturedPacketsVector is updated from another thread other than the caller - * thread (so user should avoid changing or iterating this vector while capture is on). Capture process will stop and this capture thread - * will be terminated when calling stopCapture(). This method must be called after the device is opened (i.e the open() method was called), - * otherwise an error will be returned. - * @param[in] capturedPacketsVector A reference to a RawPacketVector, meaning a vector of pointer to RawPacket objects - * @return True if capture started successfully, false if (relevant log error is printed in any case): - * - Capture is already running - * - Device is not opened - * - Capture thread could not be created - */ - virtual bool startCapture(RawPacketVector& capturedPacketsVector); - - /** - * Start capturing packets on this network interface (device) in blocking mode, meaning this method blocks and won't return until - * the user frees the blocking (via onPacketArrives callback) or until a user defined timeout expires. - * Whenever a packets is captured the onPacketArrives callback is called and lets the user handle the packet. In each callback call - * the user should return true if he wants to release the block or false if it wants it to keep blocking. Regardless of this callback - * a timeout is defined when start capturing. When this timeout expires the method will return.
- * Please notice that stopCapture() isn't needed here because when the method returns (after timeout or per user decision) capturing - * on the device is stopped - * @param[in] onPacketArrives A callback given by the user for handling incoming packets. After handling each packet the user needs to - * return a boolean value. True value indicates stop capturing and stop blocking and false value indicates continue capturing and blocking - * @param[in] userCookie A pointer to a user provided object. This object will be transferred to the onPacketArrives callback - * each time it is called. This cookie is very useful for transferring objects that give context to the capture callback, for example: - * objects that counts packets, manages flow state or manages the application state according to the packet that was captured - * @param[in] timeout A timeout in seconds for the blocking to stop even if the user didn't return "true" in the onPacketArrives callback - * If this timeout is set to 0 or less the timeout will be ignored, meaning the method will keep blocking until the user frees it via - * the onPacketArrives callback - * @return -1 if timeout expired, 1 if blocking was stopped via onPacketArrives callback or 0 if an error occurred (such as device - * not open etc.). When returning 0 an appropriate error message is printed to log - */ - virtual int startCaptureBlockingMode(OnPacketArrivesStopBlocking onPacketArrives, void* userCookie, int timeout); - - /** - * Stop a currently running packet capture. This method terminates gracefully both packet capture thread and periodic stats collection - * thread (both if exist) - */ - void stopCapture(); - - /** - * Check if a capture thread is running - * @return True if a capture thread is currently running - */ - bool captureActive(); - - /** - * Checks whether the packetPayloadLength is larger than the device MTU. Logs an error if check fails - * @param[in] packetPayloadLength The length of the IP layer of the packet - * @return True if the packetPayloadLength is less than or equal to the device MTU - */ - bool doMtuCheck(int packetPayloadLength); - - /** - * Send a RawPacket to the network - * @param[in] rawPacket A reference to the raw packet to send. This method treats the raw packet as read-only, it doesn't change anything - * in it - * @param[in] checkMtu Whether the length of the packet's payload should be checked against the MTU. If enabled this comes with a small performance penalty. - * Default value is false to avoid performance overhead. Set to true if you don't know whether packets fit the live device's MTU and you can afford the overhead. - * @return True if packet was sent successfully. False will be returned in the following cases (relevant log error is printed in any case): - * - Device is not opened - * - Packet length is 0 - * - Packet length is larger than device MTU - * - Packet could not be sent due to some error in libpcap/WinPcap/Npcap - */ - bool sendPacket(RawPacket const& rawPacket, bool checkMtu = false); - - /** - * Send a buffer containing packet raw data (including all layers) to the network. - * This particular version of the sendPacket method should only be used if you already have access to the size of the network layer of the packet, - * since it allows you to check the payload size (see packetPayloadLength parameter) MTU of the live device without incurring a parsing overhead. - * If the packetPayloadLength is unknown, please use a different implementation of the sendPacket method. - * @param[in] packetData The buffer containing the packet raw data - * @param[in] packetDataLength The length of the buffer (this is the entire packet, including link layer) - * @param[in] packetPayloadLength The length of the payload for the data link layer. This includes all data apart from the header for the - * data link layer. - * @return True if the packet was sent successfully. False will be returned in the following cases (relevant log error is printed in any case): - * - Device is not opened - * - Packet data length is 0 - * - Packet payload length is larger than device MTU - * - Packet could not be sent due to some error in libpcap/WinPcap/Npcap - */ - bool sendPacket(const uint8_t* packetData, int packetDataLength, int packetPayloadLength); - - /** - * Send a buffer containing packet raw data (including all layers) to the network - * @param[in] packetData The buffer containing the packet raw data - * @param[in] packetDataLength The length of the buffer - * @param[in] checkMtu Whether the length of the packet's payload should be checked against the MTU. If enabled this comes with a small performance penalty. - * Default value is false to avoid performance overhead. Set to true if you don't know whether packets fit the live device's MTU and you can afford the overhead. - * @param[in] linkType Only used if checkMtu is true. Defines the layer type for parsing the first layer of the packet. Used for parsing the packet to - * perform the MTU check. Default value is pcpp::LINKTYPE_ETHERNET. Ensure this parameter matches the linktype of the packet if checkMtu is true. - * @return True if packet was sent successfully. False will be returned in the following cases (relevant log error is printed in any case): - * - Device is not opened - * - Packet length is 0 - * - Packet length is larger than device MTU and checkMtu is true - * - Packet could not be sent due to some error in libpcap/WinPcap/Npcap - */ - bool sendPacket(const uint8_t* packetData, int packetDataLength, bool checkMtu = false, pcpp::LinkLayerType linkType = pcpp::LINKTYPE_ETHERNET); - - /** - * Send a parsed Packet to the network - * @param[in] packet A pointer to the packet to send. This method treats the packet as read-only, it doesn't change anything in it - * @param[in] checkMtu Whether the length of the packet's payload should be checked against the MTU. Default value is true, since the packet - * being passed in has already been parsed, so checking the MTU does not incur significant processing overhead. - * @return True if packet was sent successfully. False will be returned in the following cases (relevant log error is printed in any case): - * - Device is not opened - * - Packet length is 0 - * - Packet length is larger than device MTU and checkMtu is true - * - Packet could not be sent due to some error in libpcap/WinPcap/Npcap - */ - bool sendPacket(Packet* packet, bool checkMtu = true); - - /** - * Send an array of RawPacket objects to the network - * @param[in] rawPacketsArr The array of RawPacket objects to send. This method treats all packets as read-only, it doesn't change anything - * in them - * @param[in] arrLength The length of the array - * @param[in] checkMtu Whether to check the size of the packet payload against MTU size. Incurs a parsing overhead. - * Default value is false to avoid performance overhead. Set to true if you don't know whether packets fit the live device's MTU and you can afford the overhead. - * @return The number of packets sent successfully. Sending a packet can fail if: - * - Device is not opened. In this case no packets will be sent, return value will be 0 - * - Packet length is 0 - * - Packet length is larger than device MTU and checkMtu is true - * - Packet could not be sent due to some error in libpcap/WinPcap/Npcap - */ - virtual int sendPackets(RawPacket* rawPacketsArr, int arrLength, bool checkMtu = false); - - /** - * Send an array of pointers to Packet objects to the network - * @param[in] packetsArr The array of pointers to Packet objects to send. This method treats all packets as read-only, it doesn't change - * anything in them - * @param[in] arrLength The length of the array - * @param[in] checkMtu Whether to check the size of the packet payload against MTU size. Default value is true, since the packets - * being passed in has already been parsed, so checking the MTU does not incur significant processing overhead. - * @return The number of packets sent successfully. Sending a packet can fail if: - * - Device is not opened. In this case no packets will be sent, return value will be 0 - * - Packet length is 0 - * - Packet length is larger than device MTU and checkMtu is true - * - Packet could not be sent due to some error in libpcap/WinPcap/Npcap - */ - virtual int sendPackets(Packet** packetsArr, int arrLength, bool checkMtu = true); - - /** - * Send a vector of pointers to RawPacket objects to the network - * @param[in] rawPackets The array of pointers to RawPacket objects to send. This method treats all packets as read-only, it doesn't change - * anything in them - * @param[in] checkMtu Whether to check the size of the packet payload against MTU size. Incurs a parsing overhead. - * Default value is false to avoid performance overhead. Set to true if you don't know whether packets fit the live device's MTU and you can afford the overhead. - * @return The number of packets sent successfully. Sending a packet can fail if: - * - Device is not opened. In this case no packets will be sent, return value will be 0 - * - Packet length is 0 - * - Packet length is larger than device MTU and checkMtu is true - * - Packet could not be sent due to some error in libpcap/WinPcap/Npcap - */ - virtual int sendPackets(const RawPacketVector& rawPackets, bool checkMtu = false); - - - // implement abstract methods - - /** - * Open the device using libpcap pcap_open_live. Opening the device only makes the device ready for use, it doesn't start packet capturing. - * For packet capturing the user should call startCapture(). This implies that calling this method is a must before calling startCapture() - * (otherwise startCapture() will fail with a "device not open" error). The device is opened in promiscuous mode - * @return True if the device was opened successfully, false otherwise. When opening the device fails an error will be printed to log - * as well - */ - bool open(); - - /** - * Enables to open a device in a non-default configuration. Configuration has parameters like packet buffer timeout & size, open in - * promiscuous/non-promiscuous mode, etc. Please check DeviceConfiguration for more details - * @param[in] config The requested configuration - * @return Same as open() - */ - bool open(const DeviceConfiguration& config); - - void close(); - - /** - * Clones the current device class - * @return Pointer to the copied class - */ - PcapLiveDevice* clone(); - - virtual void getStatistics(IPcapDevice::PcapStats& stats) const; - - protected: - pcap_t* doOpen(const DeviceConfiguration& config); - }; + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp { + +class PcapLiveDevice; + +/** + * @typedef OnPacketArrivesCallback + * A callback that is called when a packet is captured by PcapLiveDevice + * @param[in] pPacket A pointer to the raw packet + * @param[in] pDevice A pointer to the PcapLiveDevice instance + * @param[in] userCookie A pointer to the object put by the user when packet + * capturing stared + */ +typedef void (*OnPacketArrivesCallback)(RawPacket* pPacket, + PcapLiveDevice* pDevice, + void* userCookie); + +/** + * @typedef OnPacketArrivesStopBlocking + * A callback that is called when a packet is captured by PcapLiveDevice + * @param[in] pPacket A pointer to the raw packet + * @param[in] pDevice A pointer to the PcapLiveDevice instance + * @param[in] userCookie A pointer to the object put by the user when packet + * capturing stared + * @return True when main thread should stop blocking or false otherwise + */ +typedef bool (*OnPacketArrivesStopBlocking)(RawPacket* pPacket, + PcapLiveDevice* pDevice, + void* userCookie); + +/** + * @typedef OnStatsUpdateCallback + * A callback that is called periodically for stats collection if user asked to + * start packet capturing with periodic stats collection + * @param[in] stats A reference to the most updated stats + * @param[in] userCookie A pointer to the object put by the user when packet + * capturing stared + */ +typedef void (*OnStatsUpdateCallback)(IPcapDevice::PcapStats& stats, + void* userCookie); + +// for internal use only +typedef void* (*ThreadStart)(void*); + +/** + * @class PcapLiveDevice + * A class that wraps a network interface (each of the interfaces listed in + * ifconfig/ipconfig). This class wraps the libpcap capabilities of capturing + * packets from the network, filtering packets and sending packets back to the + * network. This class is relevant for Linux applications only. On Windows the + * WinPcapLiveDevice (which inherits this class) is used. Both classes are + * almost similar in capabilities, the main difference between them is adapting + * some capabilities to the specific OS. This class cannot be instantiated by + * the user (it has a private constructor), as network interfaces aren't + * dynamic. Instances of this class (one instance per network interface) are + * created by PcapLiveDeviceList singleton on application startup and the user + * can get access to them by using PcapLiveDeviceList public methods such as + * PcapLiveDeviceList#getPcapLiveDeviceByIp()
Main capabilities of this + * class: + * - Get all available information for this network interfaces such as name, IP + * addresses, MAC address, MTU, etc. This information is taken from both libpcap + * and the OS + * - Capture packets from the network. Capturing is always conducted on a + * different thread. PcapPlusPlus creates this thread when capturing starts and + * kills it when capturing ends. This prevents the application from being stuck + * while waiting for packets or processing them. Currently only one capturing + * thread is allowed, so when the interface is in capture mode, no further + * capturing is allowed. In addition to capturing the user can get stats on + * packets that were received by the application, dropped by the NIC (due to + * full NIC buffers), etc. Stats collection can be initiated by the user by + * calling getStatistics() or be pushed to the user periodically by supplying a + * callback and a timeout to startCapture() + * - Send packets back to the network. Sending the packets is done on the caller + * thread. No additional threads are created for this task + */ +class PcapLiveDevice : public IPcapDevice { + friend class PcapLiveDeviceList; + + protected: + // This is a second descriptor for the same device. It is needed because of a + // bug that occurs in libpcap on Linux (on Windows using WinPcap/Npcap it + // works well): It's impossible to capture packets sent by the same descriptor + pcap_t* m_PcapSendDescriptor; + std::string m_Name; + std::string m_Description; + bool m_IsLoopback; + uint32_t m_DeviceMtu; + std::vector m_Addresses; + MacAddress m_MacAddress; + IPv4Address m_DefaultGateway; + std::thread m_CaptureThread; + bool m_CaptureThreadStarted; + std::thread m_StatsThread; + bool m_StatsThreadStarted; + std::atomic m_StopThread; + OnPacketArrivesCallback m_cbOnPacketArrives; + void* m_cbOnPacketArrivesUserCookie; + OnStatsUpdateCallback m_cbOnStatsUpdate; + void* m_cbOnStatsUpdateUserCookie; + OnPacketArrivesStopBlocking m_cbOnPacketArrivesBlockingMode; + void* m_cbOnPacketArrivesBlockingModeUserCookie; + int m_IntervalToUpdateStats; + RawPacketVector* m_CapturedPackets; + bool m_CaptureCallbackMode; + LinkLayerType m_LinkType; + + // c'tor is not public, there should be only one for every interface (created + // by PcapLiveDeviceList) + PcapLiveDevice(pcap_if_t* pInterface, bool calculateMTU, + bool calculateMacAddress, bool calculateDefaultGateway); + // copy c'tor is not public + PcapLiveDevice(const PcapLiveDevice& other); + PcapLiveDevice& operator=(const PcapLiveDevice& other); + + void setDeviceMtu(); + void setDeviceMacAddress(); + void setDefaultGateway(); + + // threads + void captureThreadMain(); + void statsThreadMain(); + + static void onPacketArrives(uint8_t* user, const struct pcap_pkthdr* pkthdr, + const uint8_t* packet); + static void onPacketArrivesNoCallback(uint8_t* user, + const struct pcap_pkthdr* pkthdr, + const uint8_t* packet); + static void onPacketArrivesBlockingMode(uint8_t* user, + const struct pcap_pkthdr* pkthdr, + const uint8_t* packet); + + public: + /** + * The type of the live device + */ + enum LiveDeviceType { + /** libPcap live device */ + LibPcapDevice, + /** WinPcap/Npcap live device */ + WinPcapDevice, + /** WinPcap/Npcap Remote Capture device */ + RemoteDevice + }; + + /** + * Device capturing mode + */ + enum DeviceMode { + /** Only packets that their destination is this NIC are captured */ + Normal = 0, + /** All packets that arrive to the NIC are captured, even packets that their + destination isn't this NIC */ + Promiscuous = 1 + }; + + /** + * Set direction for capturing packets (you can read more here: + * ) + */ + enum PcapDirection { + /** Capture traffics both incoming and outgoing */ + PCPP_INOUT = 0, + /** Only capture incoming traffics */ + PCPP_IN, + /** Only capture outgoing traffics */ + PCPP_OUT + }; + + /** + * @struct DeviceConfiguration + * A struct that contains user configurable parameters for opening a device. + * All parameters have default values so the user isn't expected to set all + * parameters or understand exactly how they work + */ + struct DeviceConfiguration { + /** Indicates whether to open the device in promiscuous or normal mode */ + DeviceMode mode; + + /** Set the packet buffer timeout in milliseconds. You can read more here: + * https://www.tcpdump.org/manpages/pcap.3pcap.html . + * Any value above 0 is considered legal, otherwise a value of 1 or -1 is + * used (depends on the platform) + */ + int packetBufferTimeoutMs; + + /** + * Set the packet buffer size. You can read more about the packet buffer + * here: https://www.tcpdump.org/manpages/pcap.3pcap.html . Any value of 100 + * or above is considered valid, otherwise the default value is used (which + * varies between different OS's). However, please notice that setting + * values which are too low or two high may result in failure to open the + * device. These too low or too high thresholds may vary between OS's, as an + * example please refer to this thread: + * https://stackoverflow.com/questions/11397367/issue-in-pcap-set-buffer-size + */ + int packetBufferSize; + + /** + * Set the direction for capturing packets. You can read more here: + * . + */ + PcapDirection direction; + + /** + * Set the snapshot length. Snapshot length is the amount of data for each + * frame that is actually captured. Note that taking larger snapshots both + * increases the amount of time it takes to process packets and, + * effectively, decreases the amount of packet buffering. This may cause + * packets to be lost. Note also that taking smaller snapshots will discard + * data from protocols above the transport layer, which loses information + * that may be important. You can read more here: + * https://wiki.wireshark.org/SnapLen + */ + int snapshotLength; + + /** + * Set NFLOG group. Which NFLOG group to be listened to when connecting to + * NFLOG device. If device is not of type NFLOG this attribute is ignored. + */ + unsigned int nflogGroup; + + /** + * A c'tor for this struct + * @param[in] mode The mode to open the device: promiscuous or + * non-promiscuous. Default value is promiscuous + * @param[in] packetBufferTimeoutMs Buffer timeout in millisecond. Default + * value is 0 which means set timeout of 1 or -1 (depends on the platform) + * @param[in] packetBufferSize The packet buffer size. Default value is 0 + * which means use the default value (varies between different OS's) + * @param[in] direction Direction for capturing packets. Default value is + * INOUT which means capture both incoming and outgoing packets (not all + * platforms support this) + * @param[in] snapshotLength Snapshot length for capturing packets. Default + * value is 0 which means use the default value. A snapshot length of 262144 + * should be big enough for maximum-size Linux loopback packets (65549) and + * some USB packets captured with USBPcap (> 131072, < 262144). A snapshot + * length of 65535 should be sufficient, on most if not all networks, to + * capture all the data available from the packet. + * @param[in] nflogGroup NFLOG group for NFLOG devices. Default value is 0. + */ + explicit DeviceConfiguration(DeviceMode mode = Promiscuous, + int packetBufferTimeoutMs = 0, + int packetBufferSize = 0, + PcapDirection direction = PCPP_INOUT, + int snapshotLength = 0, + unsigned int nflogGroup = 0) { + this->mode = mode; + this->packetBufferTimeoutMs = packetBufferTimeoutMs; + this->packetBufferSize = packetBufferSize; + this->direction = direction; + this->snapshotLength = snapshotLength; + this->nflogGroup = nflogGroup; + } + }; + + /** + * A destructor for this class + */ + virtual ~PcapLiveDevice(); + + /** + * @return The type of the device (libPcap, WinPcap/Npcap or a remote device) + */ + virtual LiveDeviceType getDeviceType() const { return LibPcapDevice; } + + /** + * @return The name of the device (e.g eth0), taken from pcap_if_t->name + */ + std::string getName() const { return m_Name; } + + /** + * @return A human-readable description of the device, taken from + * pcap_if_t->description. May be NULL in some interfaces + */ + std::string getDesc() const { return m_Description; } + + /** + * @return True if this interface is a loopback interface, false otherwise + */ + bool getLoopback() const { return m_IsLoopback; } + + /** + * @return The device's maximum transmission unit (MTU) in bytes + */ + virtual uint32_t getMtu() const { return m_DeviceMtu; } + + /** + * @return The device's link layer type + */ + virtual LinkLayerType getLinkType() const { return m_LinkType; } + + /** + * @return A vector containing all addresses defined for this interface, each + * in pcap_addr_t struct + */ + const std::vector& getAddresses() const { return m_Addresses; } + + /** + * @return The MAC address for this interface + */ + virtual MacAddress getMacAddress() const { return m_MacAddress; } + + /** + * @return The IPv4 address for this interface. If multiple IPv4 addresses are + * defined for this interface, the first will be picked. If no IPv4 addresses + * are defined, a zeroed IPv4 address (IPv4Address#Zero) will be returned + */ + IPv4Address getIPv4Address() const; + + /** + * @return The IPv6 address for this interface. If multiple IPv6 addresses are + * defined for this interface, the first will be picked. If no IPv6 addresses + * are defined, a zeroed IPv6 address (IPv6Address#Zero) will be returned + */ + IPv6Address getIPv6Address() const; + + /** + * @return The default gateway defined for this interface. If no default + * gateway is defined, if it's not IPv4 or if couldn't extract default gateway + * IPv4Address#Zero will be returned. If multiple gateways were defined the + * first one will be returned + */ + IPv4Address getDefaultGateway() const; + + /** + * @return A list of all DNS servers defined for this machine. If this list is + * empty it means no DNS servers were defined or they couldn't be extracted + * from some reason. This list is created in PcapLiveDeviceList class and can + * be also retrieved from there. This method exists for convenience - so it'll + * be possible to get this list from PcapLiveDevice as well + */ + const std::vector& getDnsServers() const; + + /** + * Start capturing packets on this network interface (device). Each time a + * packet is captured the onPacketArrives callback is called. The capture is + * done on a new thread created by this method, meaning all callback calls are + * done in a thread other than the caller thread. Capture process will stop + * and this capture thread will be terminated when calling stopCapture(). This + * method must be called after the device is opened (i.e the open() method was + * called), otherwise an error will be returned. + * @param[in] onPacketArrives A callback that is called each time a packet is + * captured + * @param[in] onPacketArrivesUserCookie A pointer to a user provided object. + * This object will be transferred to the onPacketArrives callback each time + * it is called. This cookie is very useful for transferring objects that give + * context to the capture callback, for example: objects that counts packets, + * manages flow state or manages the application state according to the packet + * that was captured + * @return True if capture started successfully, false if (relevant log error + * is printed in any case): + * - Capture is already running + * - Device is not opened + * - Capture thread could not be created + */ + virtual bool startCapture(OnPacketArrivesCallback onPacketArrives, + void* onPacketArrivesUserCookie); + + /** + * Start capturing packets on this network interface (device) with periodic + * stats collection. Each time a packet is captured the onPacketArrives + * callback is called. In addition, each intervalInSecondsToUpdateStats + * seconds stats are collected from the device and the onStatsUpdate callback + * is called. Both the capture and periodic stats collection are done on new + * threads created by this method, each on a different thread, meaning all + * callback calls are done in threads other than the caller thread. Capture + * process and stats collection will stop and threads will be terminated when + * calling stopCapture(). This method must be called after the device is + * opened (i.e the open() method was called), otherwise an error will be + * returned. + * @param[in] onPacketArrives A callback that is called each time a packet is + * captured + * @param[in] onPacketArrivesUserCookie A pointer to a user provided object. + * This object will be transferred to the onPacketArrives callback each time + * it is called. This cookie is very useful for transferring objects that give + * context to the capture callback, for example: objects that counts packets, + * manages flow state or manages the application state according to the packet + * that was captured + * @param[in] intervalInSecondsToUpdateStats The interval in seconds to + * activate periodic stats collection + * @param[in] onStatsUpdate A callback that will be called each time + * intervalInSecondsToUpdateStats expires and stats are collected. This + * callback will contain the collected stats + * @param[in] onStatsUpdateUserCookie A pointer to a user provided object. + * This object will be transferred to the onStatsUpdate callback each time it + * is called + * @return True if capture started successfully, false if (relevant log error + * is printed in any case): + * - Capture is already running + * - Device is not opened + * - Capture thread could not be created + * - Stats collection thread could not be created + */ + virtual bool startCapture(OnPacketArrivesCallback onPacketArrives, + void* onPacketArrivesUserCookie, + int intervalInSecondsToUpdateStats, + OnStatsUpdateCallback onStatsUpdate, + void* onStatsUpdateUserCookie); + + /** + * Start capturing packets on this network interface (device) with periodic + * stats collection only. This means that packets arriving to the network + * interface aren't delivered to the user but only counted. Each + * intervalInSecondsToUpdateStats seconds stats are collected from the device + * and the onStatsUpdate callback is called with the updated counters. The + * periodic stats collection is done on a new thread created by this method, + * meaning all callback calls are done in threads other than the caller + * thread. Stats collection will stop and threads will be terminated when + * calling stopCapture(). This method must be called after the device is + * opened (i.e the open() method was called), otherwise an error will be + * returned. + * @param[in] intervalInSecondsToUpdateStats The interval in seconds to + * activate periodic stats collection + * @param[in] onStatsUpdate A callback that will be called each time + * intervalInSecondsToUpdateStats expires and stats are collected. This + * callback will contain the collected stats + * @param[in] onStatsUpdateUserCookie A pointer to a user provided object. + * This object will be transferred to the onStatsUpdate callback each time it + * is called + * @return True if capture started successfully, false if (relevant log error + * is printed in any case): + * - Capture is already running + * - Device is not opened + * - Stats collection thread could not be created + */ + virtual bool startCapture(int intervalInSecondsToUpdateStats, + OnStatsUpdateCallback onStatsUpdate, + void* onStatsUpdateUserCookie); + + /** + * Start capturing packets on this network interface (device). All captured + * packets are added to capturedPacketsVector, so at the end of the capture + * (when calling stopCapture()) this vector contains pointers to all captured + * packets in the form of RawPacket. The capture is done on a new thread + * created by this method, meaning capturedPacketsVector is updated from + * another thread other than the caller thread (so user should avoid changing + * or iterating this vector while capture is on). Capture process will stop + * and this capture thread will be terminated when calling stopCapture(). This + * method must be called after the device is opened (i.e the open() method was + * called), otherwise an error will be returned. + * @param[in] capturedPacketsVector A reference to a RawPacketVector, meaning + * a vector of pointer to RawPacket objects + * @return True if capture started successfully, false if (relevant log error + * is printed in any case): + * - Capture is already running + * - Device is not opened + * - Capture thread could not be created + */ + virtual bool startCapture(RawPacketVector& capturedPacketsVector); + + /** + * Start capturing packets on this network interface (device) in blocking + * mode, meaning this method blocks and won't return until the user frees the + * blocking (via onPacketArrives callback) or until a user defined timeout + * expires. Whenever a packets is captured the onPacketArrives callback is + * called and lets the user handle the packet. In each callback call the user + * should return true if he wants to release the block or false if it wants it + * to keep blocking. Regardless of this callback a timeout is defined when + * start capturing. When this timeout expires the method will return.
+ * Please notice that stopCapture() isn't needed here because when the method + * returns (after timeout or per user decision) capturing on the device is + * stopped + * @param[in] onPacketArrives A callback given by the user for handling + * incoming packets. After handling each packet the user needs to return a + * boolean value. True value indicates stop capturing and stop blocking and + * false value indicates continue capturing and blocking + * @param[in] userCookie A pointer to a user provided object. This object will + * be transferred to the onPacketArrives callback each time it is called. This + * cookie is very useful for transferring objects that give context to the + * capture callback, for example: objects that counts packets, manages flow + * state or manages the application state according to the packet that was + * captured + * @param[in] timeout A timeout in seconds for the blocking to stop even if + * the user didn't return "true" in the onPacketArrives callback If this + * timeout is set to 0 or less the timeout will be ignored, meaning the method + * will keep blocking until the user frees it via the onPacketArrives callback + * @return -1 if timeout expired, 1 if blocking was stopped via + * onPacketArrives callback or 0 if an error occurred (such as device not open + * etc.). When returning 0 an appropriate error message is printed to log + */ + virtual int + startCaptureBlockingMode(OnPacketArrivesStopBlocking onPacketArrives, + void* userCookie, int timeout); + + /** + * Stop a currently running packet capture. This method terminates gracefully + * both packet capture thread and periodic stats collection thread (both if + * exist) + */ + void stopCapture(); + + /** + * Check if a capture thread is running + * @return True if a capture thread is currently running + */ + bool captureActive(); + + /** + * Checks whether the packetPayloadLength is larger than the device MTU. Logs + * an error if check fails + * @param[in] packetPayloadLength The length of the IP layer of the packet + * @return True if the packetPayloadLength is less than or equal to the device + * MTU + */ + bool doMtuCheck(int packetPayloadLength); + + /** + * Send a RawPacket to the network + * @param[in] rawPacket A reference to the raw packet to send. This method + * treats the raw packet as read-only, it doesn't change anything in it + * @param[in] checkMtu Whether the length of the packet's payload should be + * checked against the MTU. If enabled this comes with a small performance + * penalty. Default value is false to avoid performance overhead. Set to true + * if you don't know whether packets fit the live device's MTU and you can + * afford the overhead. + * @return True if packet was sent successfully. False will be returned in the + * following cases (relevant log error is printed in any case): + * - Device is not opened + * - Packet length is 0 + * - Packet length is larger than device MTU + * - Packet could not be sent due to some error in libpcap/WinPcap/Npcap + */ + bool sendPacket(RawPacket const& rawPacket, bool checkMtu = false); + + /** + * Send a buffer containing packet raw data (including all layers) to the + * network. This particular version of the sendPacket method should only be + * used if you already have access to the size of the network layer of the + * packet, since it allows you to check the payload size (see + * packetPayloadLength parameter) MTU of the live device without incurring a + * parsing overhead. If the packetPayloadLength is unknown, please use a + * different implementation of the sendPacket method. + * @param[in] packetData The buffer containing the packet raw data + * @param[in] packetDataLength The length of the buffer (this is the entire + * packet, including link layer) + * @param[in] packetPayloadLength The length of the payload for the data link + * layer. This includes all data apart from the header for the data link + * layer. + * @return True if the packet was sent successfully. False will be returned in + * the following cases (relevant log error is printed in any case): + * - Device is not opened + * - Packet data length is 0 + * - Packet payload length is larger than device MTU + * - Packet could not be sent due to some error in libpcap/WinPcap/Npcap + */ + bool sendPacket(const uint8_t* packetData, int packetDataLength, + int packetPayloadLength); + + /** + * Send a buffer containing packet raw data (including all layers) to the + * network + * @param[in] packetData The buffer containing the packet raw data + * @param[in] packetDataLength The length of the buffer + * @param[in] checkMtu Whether the length of the packet's payload should be + * checked against the MTU. If enabled this comes with a small performance + * penalty. Default value is false to avoid performance overhead. Set to true + * if you don't know whether packets fit the live device's MTU and you can + * afford the overhead. + * @param[in] linkType Only used if checkMtu is true. Defines the layer type + * for parsing the first layer of the packet. Used for parsing the packet to + * perform the MTU check. Default value is pcpp::LINKTYPE_ETHERNET. Ensure + * this parameter matches the linktype of the packet if checkMtu is true. + * @return True if packet was sent successfully. False will be returned in the + * following cases (relevant log error is printed in any case): + * - Device is not opened + * - Packet length is 0 + * - Packet length is larger than device MTU and checkMtu is true + * - Packet could not be sent due to some error in libpcap/WinPcap/Npcap + */ + bool sendPacket(const uint8_t* packetData, int packetDataLength, + bool checkMtu = false, + pcpp::LinkLayerType linkType = pcpp::LINKTYPE_ETHERNET); + + /** + * Send a parsed Packet to the network + * @param[in] packet A pointer to the packet to send. This method treats the + * packet as read-only, it doesn't change anything in it + * @param[in] checkMtu Whether the length of the packet's payload should be + * checked against the MTU. Default value is true, since the packet being + * passed in has already been parsed, so checking the MTU does not incur + * significant processing overhead. + * @return True if packet was sent successfully. False will be returned in the + * following cases (relevant log error is printed in any case): + * - Device is not opened + * - Packet length is 0 + * - Packet length is larger than device MTU and checkMtu is true + * - Packet could not be sent due to some error in libpcap/WinPcap/Npcap + */ + bool sendPacket(Packet* packet, bool checkMtu = true); + + /** + * Send an array of RawPacket objects to the network + * @param[in] rawPacketsArr The array of RawPacket objects to send. This + * method treats all packets as read-only, it doesn't change anything in them + * @param[in] arrLength The length of the array + * @param[in] checkMtu Whether to check the size of the packet payload against + * MTU size. Incurs a parsing overhead. Default value is false to avoid + * performance overhead. Set to true if you don't know whether packets fit the + * live device's MTU and you can afford the overhead. + * @return The number of packets sent successfully. Sending a packet can fail + * if: + * - Device is not opened. In this case no packets will be sent, return value + * will be 0 + * - Packet length is 0 + * - Packet length is larger than device MTU and checkMtu is true + * - Packet could not be sent due to some error in libpcap/WinPcap/Npcap + */ + virtual int sendPackets(RawPacket* rawPacketsArr, int arrLength, + bool checkMtu = false); + + /** + * Send an array of pointers to Packet objects to the network + * @param[in] packetsArr The array of pointers to Packet objects to send. This + * method treats all packets as read-only, it doesn't change anything in them + * @param[in] arrLength The length of the array + * @param[in] checkMtu Whether to check the size of the packet payload against + * MTU size. Default value is true, since the packets being passed in has + * already been parsed, so checking the MTU does not incur significant + * processing overhead. + * @return The number of packets sent successfully. Sending a packet can fail + * if: + * - Device is not opened. In this case no packets will be sent, return value + * will be 0 + * - Packet length is 0 + * - Packet length is larger than device MTU and checkMtu is true + * - Packet could not be sent due to some error in libpcap/WinPcap/Npcap + */ + virtual int sendPackets(Packet** packetsArr, int arrLength, + bool checkMtu = true); + + /** + * Send a vector of pointers to RawPacket objects to the network + * @param[in] rawPackets The array of pointers to RawPacket objects to send. + * This method treats all packets as read-only, it doesn't change anything in + * them + * @param[in] checkMtu Whether to check the size of the packet payload against + * MTU size. Incurs a parsing overhead. Default value is false to avoid + * performance overhead. Set to true if you don't know whether packets fit the + * live device's MTU and you can afford the overhead. + * @return The number of packets sent successfully. Sending a packet can fail + * if: + * - Device is not opened. In this case no packets will be sent, return value + * will be 0 + * - Packet length is 0 + * - Packet length is larger than device MTU and checkMtu is true + * - Packet could not be sent due to some error in libpcap/WinPcap/Npcap + */ + virtual int sendPackets(const RawPacketVector& rawPackets, + bool checkMtu = false); + + // implement abstract methods + + /** + * Open the device using libpcap pcap_open_live. Opening the device only makes + * the device ready for use, it doesn't start packet capturing. For packet + * capturing the user should call startCapture(). This implies that calling + * this method is a must before calling startCapture() (otherwise + * startCapture() will fail with a "device not open" error). The device is + * opened in promiscuous mode + * @return True if the device was opened successfully, false otherwise. When + * opening the device fails an error will be printed to log as well + */ + bool open(); + + /** + * Enables to open a device in a non-default configuration. Configuration has + * parameters like packet buffer timeout & size, open in + * promiscuous/non-promiscuous mode, etc. Please check DeviceConfiguration for + * more details + * @param[in] config The requested configuration + * @return Same as open() + */ + bool open(const DeviceConfiguration& config); + + void close(); + + /** + * Clones the current device class + * @return Pointer to the copied class + */ + PcapLiveDevice* clone(); + + virtual void getStatistics(IPcapDevice::PcapStats& stats) const; + + protected: + pcap_t* doOpen(const DeviceConfiguration& config); +}; } // namespace pcpp diff --git a/Pcap++/header/PcapLiveDeviceList.h b/Pcap++/header/PcapLiveDeviceList.h index 237e9d9ec9..6dc0ceeb8c 100644 --- a/Pcap++/header/PcapLiveDeviceList.h +++ b/Pcap++/header/PcapLiveDeviceList.h @@ -5,116 +5,129 @@ #include "PcapLiveDevice.h" #include - /// @file /** -* \namespace pcpp -* \brief The main namespace for the PcapPlusPlus lib -*/ -namespace pcpp -{ - - /** - * @class PcapLiveDeviceList - * A singleton class that creates, stores and provides access to all PcapLiveDevice (on Linux) or WinPcapLiveDevice (on Windows) instances. All live - * devices are initialized on startup and wrap the network interfaces installed on the machine. This class enables access to them through - * their IP addresses or get a vector of all of them so the user can search them in some other way - */ - class PcapLiveDeviceList - { - private: - std::vector m_LiveDeviceList; - - std::vector m_DnsServers; - - // private c'tor - PcapLiveDeviceList(); - // private copy c'tor - PcapLiveDeviceList( const PcapLiveDeviceList& other ); - PcapLiveDeviceList& operator=(const PcapLiveDeviceList& other); - - void init(); - - void setDnsServers(); - public: - /** - * The access method to the singleton - * @return The singleton instance of this class - */ - static PcapLiveDeviceList& getInstance() - { - static PcapLiveDeviceList instance; - return instance; - } - - /** - * @return A vector containing pointers to all live devices currently installed on the machine - */ - const std::vector& getPcapLiveDevicesList() const { return m_LiveDeviceList; } - - /** - * Get a pointer to the live device by its IP address. IP address can be both IPv4 or IPv6 - * @param[in] ipAddr The IP address defined for the device - * @return A pointer to the live device if this IP address exists. NULL otherwise - */ - PcapLiveDevice* getPcapLiveDeviceByIp(const IPAddress& ipAddr) const; - - /** - * Get a pointer to the live device by its IPv4 address - * @param[in] ipAddr The IPv4 address defined for the device - * @return A pointer to the live device if this IPv4 address exists. NULL otherwise - */ - PcapLiveDevice* getPcapLiveDeviceByIp(const IPv4Address& ipAddr) const; - - /** - * Get a pointer to the live device by its IPv6 address - * @param[in] ip6Addr The IPv6 address defined for the device - * @return A pointer to the live device if this IPv6 address exists. NULL otherwise - */ - PcapLiveDevice* getPcapLiveDeviceByIp(const IPv6Address& ip6Addr) const; - - /** - * Get a pointer to the live device by its IP address represented as string. IP address can be both IPv4 or IPv6 - * @param[in] ipAddrAsString The IP address defined for the device as string - * @return A pointer to the live device if this IP address is valid and exists. NULL otherwise - */ - PcapLiveDevice* getPcapLiveDeviceByIp(const std::string& ipAddrAsString) const; - - /** - * Get a pointer to the live device by its name - * @param[in] name The name of the interface (e.g eth0) - * @return A pointer to the live device if this name exists. NULL otherwise - */ - PcapLiveDevice* getPcapLiveDeviceByName(const std::string& name) const; - - /** - * Get a pointer to the live device by its IP address or name - * @param[in] ipOrName An IP address or name of the interface - * @return A pointer to the live device if exists, NULL otherwise - */ - PcapLiveDevice* getPcapLiveDeviceByIpOrName(const std::string& ipOrName) const; - - /** - * @return A list of all DNS servers defined for this machine. If this list is empty it means no DNS servers were defined or they - * couldn't be extracted from some reason - */ - const std::vector& getDnsServers() const { return m_DnsServers; } - - /** - * Copies the current live device list - * @return A pointer to the cloned device list - */ - PcapLiveDeviceList* clone(); - - /** - * Reset the live device list and DNS server list, meaning clear and refetch them - */ - void reset(); - - // d'tor - ~PcapLiveDeviceList(); - }; + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp { + +/** + * @class PcapLiveDeviceList + * A singleton class that creates, stores and provides access to all + * PcapLiveDevice (on Linux) or WinPcapLiveDevice (on Windows) instances. All + * live devices are initialized on startup and wrap the network interfaces + * installed on the machine. This class enables access to them through their IP + * addresses or get a vector of all of them so the user can search them in some + * other way + */ +class PcapLiveDeviceList { + private: + std::vector m_LiveDeviceList; + + std::vector m_DnsServers; + + // private c'tor + PcapLiveDeviceList(); + // private copy c'tor + PcapLiveDeviceList(const PcapLiveDeviceList& other); + PcapLiveDeviceList& operator=(const PcapLiveDeviceList& other); + + void init(); + + void setDnsServers(); + + public: + /** + * The access method to the singleton + * @return The singleton instance of this class + */ + static PcapLiveDeviceList& getInstance() { + static PcapLiveDeviceList instance; + return instance; + } + + /** + * @return A vector containing pointers to all live devices currently + * installed on the machine + */ + const std::vector& getPcapLiveDevicesList() const { + return m_LiveDeviceList; + } + + /** + * Get a pointer to the live device by its IP address. IP address can be both + * IPv4 or IPv6 + * @param[in] ipAddr The IP address defined for the device + * @return A pointer to the live device if this IP address exists. NULL + * otherwise + */ + PcapLiveDevice* getPcapLiveDeviceByIp(const IPAddress& ipAddr) const; + + /** + * Get a pointer to the live device by its IPv4 address + * @param[in] ipAddr The IPv4 address defined for the device + * @return A pointer to the live device if this IPv4 address exists. NULL + * otherwise + */ + PcapLiveDevice* getPcapLiveDeviceByIp(const IPv4Address& ipAddr) const; + + /** + * Get a pointer to the live device by its IPv6 address + * @param[in] ip6Addr The IPv6 address defined for the device + * @return A pointer to the live device if this IPv6 address exists. NULL + * otherwise + */ + PcapLiveDevice* getPcapLiveDeviceByIp(const IPv6Address& ip6Addr) const; + + /** + * Get a pointer to the live device by its IP address represented as string. + * IP address can be both IPv4 or IPv6 + * @param[in] ipAddrAsString The IP address defined for the device as string + * @return A pointer to the live device if this IP address is valid and + * exists. NULL otherwise + */ + PcapLiveDevice* + getPcapLiveDeviceByIp(const std::string& ipAddrAsString) const; + + /** + * Get a pointer to the live device by its name + * @param[in] name The name of the interface (e.g eth0) + * @return A pointer to the live device if this name exists. NULL otherwise + */ + PcapLiveDevice* getPcapLiveDeviceByName(const std::string& name) const; + + /** + * Get a pointer to the live device by its IP address or name + * @param[in] ipOrName An IP address or name of the interface + * @return A pointer to the live device if exists, NULL otherwise + */ + PcapLiveDevice* + getPcapLiveDeviceByIpOrName(const std::string& ipOrName) const; + + /** + * @return A list of all DNS servers defined for this machine. If this list is + * empty it means no DNS servers were defined or they couldn't be extracted + * from some reason + */ + const std::vector& getDnsServers() const { return m_DnsServers; } + + /** + * Copies the current live device list + * @return A pointer to the cloned device list + */ + PcapLiveDeviceList* clone(); + + /** + * Reset the live device list and DNS server list, meaning clear and refetch + * them + */ + void reset(); + + // d'tor + ~PcapLiveDeviceList(); +}; } // namespace pcpp diff --git a/Pcap++/header/PcapRemoteDevice.h b/Pcap++/header/PcapRemoteDevice.h index 7058fe66cf..c12b675302 100644 --- a/Pcap++/header/PcapRemoteDevice.h +++ b/Pcap++/header/PcapRemoteDevice.h @@ -3,142 +3,173 @@ #if defined(_WIN32) -#include #include "PcapLiveDevice.h" - +#include /// @file struct pcap_rmtauth; /** -* \namespace pcpp -* \brief The main namespace for the PcapPlusPlus lib -*/ -namespace pcpp -{ - - /** - * @struct PcapRemoteAuthentication - * The remote daemon (rpcapd) can be configured to require authentication before allowing a client to connect. This is done for - * security reasons of course. This struct wraps the WinPcap/Npcap authentication object (pcap_rmtauth) and can (but not must) be given to - * PcapRemoteDeviceList when initiating a connection to the remote daemon - */ - struct PcapRemoteAuthentication - { - public: - /** - * A constructor that sets username and password - * @param[in] username The username for authentication with the remote daemon - * @param[in] password The password for authentication with the remote daemon - */ - PcapRemoteAuthentication(const std::string &username, const std::string &password) : userName(username) { this->password = password; } - - /** - * A copy c'tor for this object - * @param[in] other The object to copy from - */ - PcapRemoteAuthentication(const PcapRemoteAuthentication& other) : userName(other.userName), password(other.password) {} - - /** - * The username for authentication - */ - std::string userName; - - /** - * The password for authentication - */ - std::string password; - - /** - * A conversion method from PcapRemoteAuthentication to pcap_rmtauth. Note: the char* pointers of the returned pcap_rmtauth points - * to the same places in memory as PcapRemoteAuthentication::userName and PcapRemoteAuthentication::password so the user should avoid - * freeing this memory - * @return A pcap_rmtauth that is converted from this class - */ - pcap_rmtauth getPcapRmAuth() const; - }; - - /** - * @class PcapRemoteDevice - * A class that provides a C++ wrapper for WinPcap/Npcap Remote Capture feature. This feature allows to interact to a remote machine and capture - * packets that are being transmitted on the remote network interfaces. This requires a remote daemon (called rpcapd) which performs the - * capture and sends data back and the local client (represented by PcapRemoteDevice) that sends the appropriate commands and receives the - * captured data. You can read more about this feature in WinPcap Remote Capture manual: https://www.winpcap.org/docs/docs_412/html/group__remote.html
- * Since this feature is supported in WinPcap and Npcap only and not in libpcap, PcapRemoteDevice can only be used in Windows only.
- * This class provides a wrapper for the local client, meaning it assumes the daemon (rpcapd) is already running on the remote machine and it - * tries to connect to it and start receiving/sending packets from/to it. This class assumes rpcapd is in passive mode, meaning - * PcapRemoteDevice connects to the remote daemon, sends the appropriate commands to it, and starts capturing packets, rather than letting the - * daemon connect to the client by itself. Using PcapRemoteDevice is very similar to using the other live devices (PcapLiveDevice or - * WinPcapLiveDevice), meaning the API's are the same and the same logic is used (for example: capturing is done on a different thread, - * sending packets are done on the same thread, etc.). For the full API and explanations, please refer to PcapLiveDevice. The reason for the - * similar API is that WinPcap/Npcap's API is very similar between Remote Capture and local network interface capture. The things that are different - * are some are some implementation details, mainly in making the connection to the remote daemon, and the way the user can get the instance - * of PcapRemoteDevice. For more details on that please refer to PcapRemoteDeviceList - */ - class PcapRemoteDevice : public PcapLiveDevice - { - friend class PcapRemoteDeviceList; - private: - IPAddress m_RemoteMachineIpAddress; - uint16_t m_RemoteMachinePort; - PcapRemoteAuthentication* m_RemoteAuthentication; - - // c'tor is private, as only PcapRemoteDeviceList should create instances of it, and it'll create only one for every remote interface - PcapRemoteDevice(pcap_if_t* iface, PcapRemoteAuthentication* remoteAuthentication, const IPAddress& remoteMachineIP, uint16_t remoteMachinePort); - - // private copy c'tor - PcapRemoteDevice( const PcapRemoteDevice& other ); - // private assignment operator - PcapRemoteDevice& operator=(const PcapRemoteDevice& other); - - static void* remoteDeviceCaptureThreadMain(void *ptr); - - //overridden methods - ThreadStart getCaptureThreadStart(); - - public: - virtual ~PcapRemoteDevice() {} - - /** - * @return The IP address of the remote machine where packets are transmitted from the remote machine to the client machine - */ - IPAddress getRemoteMachineIpAddress() const { return m_RemoteMachineIpAddress; } - - /** - * @return The port of the remote machine where packets are transmitted from the remote machine to the client machine - */ - uint16_t getRemoteMachinePort() const { return m_RemoteMachinePort; } - - //overridden methods - - virtual LiveDeviceType getDeviceType() const { return RemoteDevice; } - - /** - * MTU isn't supported for remote devices - * @return 0 - */ - virtual uint32_t getMtu() const; - - /** - * MAC address isn't supported for remote devices - * @return MacAddress#Zero - */ - virtual MacAddress getMacAddress() const; - - /** - * Open the device using pcap_open. Opening the device makes the connection to the remote daemon (including authentication if needed - * and provided). If this methods succeeds it means the connection to the remote daemon succeeded and the device is ready for use. - * As in PcapLiveDevice, packet capturing won't start yet. For packet capturing the user should call startCapture(). This implies - * that calling this method is a must before calling startCapture() (otherwise startCapture() will fail with a "device not open" error). - * The remote daemon is asked to capture packets in promiscuous mode - * @return True if the device was opened successfully, false otherwise. When opening the device fails an error will be printed to log - * as well, including the WinPcap/Npcap error if exists - */ - virtual bool open(); - - virtual void getStatistics(IPcapDevice::PcapStats& stats) const; - }; + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp { + +/** + * @struct PcapRemoteAuthentication + * The remote daemon (rpcapd) can be configured to require authentication before + * allowing a client to connect. This is done for security reasons of course. + * This struct wraps the WinPcap/Npcap authentication object (pcap_rmtauth) and + * can (but not must) be given to PcapRemoteDeviceList when initiating a + * connection to the remote daemon + */ +struct PcapRemoteAuthentication { + public: + /** + * A constructor that sets username and password + * @param[in] username The username for authentication with the remote daemon + * @param[in] password The password for authentication with the remote daemon + */ + PcapRemoteAuthentication(const std::string& username, + const std::string& password) + : userName(username) { + this->password = password; + } + + /** + * A copy c'tor for this object + * @param[in] other The object to copy from + */ + PcapRemoteAuthentication(const PcapRemoteAuthentication& other) + : userName(other.userName), password(other.password) {} + + /** + * The username for authentication + */ + std::string userName; + + /** + * The password for authentication + */ + std::string password; + + /** + * A conversion method from PcapRemoteAuthentication to pcap_rmtauth. Note: + * the char* pointers of the returned pcap_rmtauth points to the same places + * in memory as PcapRemoteAuthentication::userName and + * PcapRemoteAuthentication::password so the user should avoid freeing this + * memory + * @return A pcap_rmtauth that is converted from this class + */ + pcap_rmtauth getPcapRmAuth() const; +}; + +/** + * @class PcapRemoteDevice + * A class that provides a C++ wrapper for WinPcap/Npcap Remote Capture feature. + * This feature allows to interact to a remote machine and capture packets that + * are being transmitted on the remote network interfaces. This requires a + * remote daemon (called rpcapd) which performs the capture and sends data back + * and the local client (represented by PcapRemoteDevice) that sends the + * appropriate commands and receives the captured data. You can read more about + * this feature in WinPcap Remote Capture manual: + * https://www.winpcap.org/docs/docs_412/html/group__remote.html
Since this + * feature is supported in WinPcap and Npcap only and not in libpcap, + * PcapRemoteDevice can only be used in Windows only.
This class provides a + * wrapper for the local client, meaning it assumes the daemon (rpcapd) is + * already running on the remote machine and it tries to connect to it and start + * receiving/sending packets from/to it. This class assumes rpcapd is in passive + * mode, meaning PcapRemoteDevice connects to the remote daemon, sends the + * appropriate commands to it, and starts capturing packets, rather than letting + * the daemon connect to the client by itself. Using PcapRemoteDevice is very + * similar to using the other live devices (PcapLiveDevice or + * WinPcapLiveDevice), meaning the API's are the same and the same logic is used + * (for example: capturing is done on a different thread, sending packets are + * done on the same thread, etc.). For the full API and explanations, please + * refer to PcapLiveDevice. The reason for the similar API is that + * WinPcap/Npcap's API is very similar between Remote Capture and local network + * interface capture. The things that are different are some are some + * implementation details, mainly in making the connection to the remote daemon, + * and the way the user can get the instance of PcapRemoteDevice. For more + * details on that please refer to PcapRemoteDeviceList + */ +class PcapRemoteDevice : public PcapLiveDevice { + friend class PcapRemoteDeviceList; + + private: + IPAddress m_RemoteMachineIpAddress; + uint16_t m_RemoteMachinePort; + PcapRemoteAuthentication* m_RemoteAuthentication; + + // c'tor is private, as only PcapRemoteDeviceList should create instances of + // it, and it'll create only one for every remote interface + PcapRemoteDevice(pcap_if_t* iface, + PcapRemoteAuthentication* remoteAuthentication, + const IPAddress& remoteMachineIP, + uint16_t remoteMachinePort); + + // private copy c'tor + PcapRemoteDevice(const PcapRemoteDevice& other); + // private assignment operator + PcapRemoteDevice& operator=(const PcapRemoteDevice& other); + + static void* remoteDeviceCaptureThreadMain(void* ptr); + + // overridden methods + ThreadStart getCaptureThreadStart(); + + public: + virtual ~PcapRemoteDevice() {} + + /** + * @return The IP address of the remote machine where packets are transmitted + * from the remote machine to the client machine + */ + IPAddress getRemoteMachineIpAddress() const { + return m_RemoteMachineIpAddress; + } + + /** + * @return The port of the remote machine where packets are transmitted from + * the remote machine to the client machine + */ + uint16_t getRemoteMachinePort() const { return m_RemoteMachinePort; } + + // overridden methods + + virtual LiveDeviceType getDeviceType() const { return RemoteDevice; } + + /** + * MTU isn't supported for remote devices + * @return 0 + */ + virtual uint32_t getMtu() const; + + /** + * MAC address isn't supported for remote devices + * @return MacAddress#Zero + */ + virtual MacAddress getMacAddress() const; + + /** + * Open the device using pcap_open. Opening the device makes the connection to + * the remote daemon (including authentication if needed and provided). If + * this methods succeeds it means the connection to the remote daemon + * succeeded and the device is ready for use. As in PcapLiveDevice, packet + * capturing won't start yet. For packet capturing the user should call + * startCapture(). This implies that calling this method is a must before + * calling startCapture() (otherwise startCapture() will fail with a "device + * not open" error). The remote daemon is asked to capture packets in + * promiscuous mode + * @return True if the device was opened successfully, false otherwise. When + * opening the device fails an error will be printed to log as well, including + * the WinPcap/Npcap error if exists + */ + virtual bool open(); + + virtual void getStatistics(IPcapDevice::PcapStats& stats) const; +}; } // namespace pcpp diff --git a/Pcap++/header/PcapRemoteDeviceList.h b/Pcap++/header/PcapRemoteDeviceList.h index d5cee37496..6ab10f52d0 100644 --- a/Pcap++/header/PcapRemoteDeviceList.h +++ b/Pcap++/header/PcapRemoteDeviceList.h @@ -9,141 +9,174 @@ /// @file /** -* \namespace pcpp -* \brief The main namespace for the PcapPlusPlus lib -*/ -namespace pcpp -{ - - /** - * @class PcapRemoteDeviceList - * A class that creates, stores and provides access to all instances of PcapRemoteDevice for a certain remote machine. To get an instance - * of this class use one of the static methods of getRemoteDeviceList(). These methods creates a PcapRemoteDeviceList instance for the - * certain remote machine which holds a list of PcapRemoteDevice instances, one for each remote network interface. Note there is - * not a public constructor for this class, so the only way to get an instance of it is through getRemoteDeviceList(). After getting - * this object, this class provides ways to access the PcapRemoteDevice instances: either through IP address of the remote network interface or - * by iterating the PcapRemoteDevice instances (through the PcapRemoteDeviceList#RemoteDeviceListIterator iterator)
- * Since Remote Capture is supported in WinPcap and Npcap only, this class is available in Windows only - */ - class PcapRemoteDeviceList - { - private: - std::vector m_RemoteDeviceList; - IPAddress m_RemoteMachineIpAddress; - uint16_t m_RemoteMachinePort; - PcapRemoteAuthentication* m_RemoteAuthentication; - - // private c'tor. User should create the list via static methods PcapRemoteDeviceList::getRemoteDeviceList() - PcapRemoteDeviceList() : m_RemoteMachinePort(0), m_RemoteAuthentication(NULL) {} - // private copy c'tor - PcapRemoteDeviceList(const PcapRemoteDeviceList& other); - PcapRemoteDeviceList& operator=(const PcapRemoteDeviceList& other); - - void setRemoteMachineIpAddress(const IPAddress& ipAddress); - void setRemoteMachinePort(uint16_t port); - void setRemoteAuthentication(const PcapRemoteAuthentication* remoteAuth); - - public: - /** - * Iterator object that can be used for iterating all PcapRemoteDevice in list - */ - typedef typename std::vector::iterator RemoteDeviceListIterator; - - /** - * Const iterator object that can be used for iterating all PcapRemoteDevice in a constant list - */ - typedef typename std::vector::const_iterator ConstRemoteDeviceListIterator; - - ~PcapRemoteDeviceList(); - - /** - * A static method for creating a PcapRemoteDeviceList instance for a certain remote machine. This methods creates the instance, and also - * creates a list of PcapRemoteDevice instances stored in it, one for each remote network interface. Notice this method allocates - * the PcapRemoteDeviceList instance and returns a pointer to it. It's the user responsibility to free it when done using it
- * This method overload is for remote daemons which don't require authentication for accessing them. For daemons which do require authentication - * use the other method overload - * @param[in] ipAddress The IP address of the remote machine through which clients can connect to the rpcapd daemon - * @param[in] port The port of the remote machine through which clients can connect to the rpcapd daemon - * @return A pointer to the newly created PcapRemoteDeviceList, or NULL if (an appropriate error will be printed to log in each case): - * - IP address provided is NULL or not valid - * - WinPcap/Npcap encountered an error in creating the remote connection string - * - WinPcap/Npcap encountered an error connecting to the rpcapd daemon on the remote machine or retrieving devices on the remote machine - */ - static PcapRemoteDeviceList* getRemoteDeviceList(const IPAddress& ipAddress, uint16_t port); - - /** - * An overload of the previous getRemoteDeviceList() method but with authentication support. This method is suitable for connecting to - * remote daemons which require authentication for accessing them - * @param[in] ipAddress The IP address of the remote machine through which clients can connect to the rpcapd daemon - * @param[in] port The port of the remote machine through which clients can connect to the rpcapd daemon - * @param[in] remoteAuth A pointer to the authentication object which contains the username and password for connecting to the remote - * daemon - * @return A pointer to the newly created PcapRemoteDeviceList, or NULL if (an appropriate error will be printed to log in each case): - * - IP address provided is NULL or not valid - * - WinPcap/Npcap encountered an error in creating the remote connection string - * - WinPcap/Npcap encountered an error connecting to the rpcapd daemon on the remote machine or retrieving devices on the remote machine - */ - static PcapRemoteDeviceList* getRemoteDeviceList(const IPAddress& ipAddress, uint16_t port, PcapRemoteAuthentication* remoteAuth); - - /** - * @return The IP address of the remote machine - */ - IPAddress getRemoteMachineIpAddress() const { return m_RemoteMachineIpAddress; } - - /** - * @return The port of the remote machine where packets are transmitted from the remote machine to the client machine - */ - uint16_t getRemoteMachinePort() const { return m_RemoteMachinePort; } - - /** - * Search a PcapRemoteDevice in the list by its IPv4 address - * @param[in] ip4Addr The IPv4 address - * @return The PcapRemoteDevice if found, NULL otherwise - */ - PcapRemoteDevice* getRemoteDeviceByIP(const IPv4Address& ip4Addr) const; - - /** - * Search a PcapRemoteDevice in the list by its IPv6 address - * @param[in] ip6Addr The IPv6 address - * @return The PcapRemoteDevice if found, NULL otherwise - */ - PcapRemoteDevice* getRemoteDeviceByIP(const IPv6Address& ip6Addr) const; - - /** - * Search a PcapRemoteDevice in the list by its IP address (IPv4 or IPv6) - * @param[in] ipAddr The IP address - * @return The PcapRemoteDevice if found, NULL otherwise - */ - PcapRemoteDevice* getRemoteDeviceByIP(const IPAddress& ipAddr) const; - - /** - * Search a PcapRemoteDevice in the list by its IP address - * @param[in] ipAddrAsString The IP address in string format - * @return The PcapRemoteDevice if found, NULL otherwise - */ - PcapRemoteDevice* getRemoteDeviceByIP(const std::string& ipAddrAsString) const; - - /** - * @return An iterator object pointing to the first PcapRemoteDevice in list - */ - RemoteDeviceListIterator begin() { return m_RemoteDeviceList.begin(); } - - /** - * @return A const iterator object pointing to the first PcapRemoteDevice in list - */ - ConstRemoteDeviceListIterator begin() const { return m_RemoteDeviceList.begin(); } - - /** - * @return An iterator object pointing to the last PcapRemoteDevice in list - */ - RemoteDeviceListIterator end() { return m_RemoteDeviceList.end(); } - - /** - * @return A const iterator object pointing to the last PcapRemoteDevice in list - */ - ConstRemoteDeviceListIterator end() const { return m_RemoteDeviceList.end(); } - - }; + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp { + +/** + * @class PcapRemoteDeviceList + * A class that creates, stores and provides access to all instances of + * PcapRemoteDevice for a certain remote machine. To get an instance of this + * class use one of the static methods of getRemoteDeviceList(). These methods + * creates a PcapRemoteDeviceList instance for the certain remote machine which + * holds a list of PcapRemoteDevice instances, one for each remote network + * interface. Note there is not a public constructor for this class, so the only + * way to get an instance of it is through getRemoteDeviceList(). After getting + * this object, this class provides ways to access the PcapRemoteDevice + * instances: either through IP address of the remote network interface or by + * iterating the PcapRemoteDevice instances (through the + * PcapRemoteDeviceList#RemoteDeviceListIterator iterator)
Since Remote + * Capture is supported in WinPcap and Npcap only, this class is available in + * Windows only + */ +class PcapRemoteDeviceList { + private: + std::vector m_RemoteDeviceList; + IPAddress m_RemoteMachineIpAddress; + uint16_t m_RemoteMachinePort; + PcapRemoteAuthentication* m_RemoteAuthentication; + + // private c'tor. User should create the list via static methods + // PcapRemoteDeviceList::getRemoteDeviceList() + PcapRemoteDeviceList() + : m_RemoteMachinePort(0), m_RemoteAuthentication(NULL) {} + // private copy c'tor + PcapRemoteDeviceList(const PcapRemoteDeviceList& other); + PcapRemoteDeviceList& operator=(const PcapRemoteDeviceList& other); + + void setRemoteMachineIpAddress(const IPAddress& ipAddress); + void setRemoteMachinePort(uint16_t port); + void setRemoteAuthentication(const PcapRemoteAuthentication* remoteAuth); + + public: + /** + * Iterator object that can be used for iterating all PcapRemoteDevice in list + */ + typedef typename std::vector::iterator + RemoteDeviceListIterator; + + /** + * Const iterator object that can be used for iterating all PcapRemoteDevice + * in a constant list + */ + typedef typename std::vector::const_iterator + ConstRemoteDeviceListIterator; + + ~PcapRemoteDeviceList(); + + /** + * A static method for creating a PcapRemoteDeviceList instance for a certain + * remote machine. This methods creates the instance, and also creates a list + * of PcapRemoteDevice instances stored in it, one for each remote network + * interface. Notice this method allocates the PcapRemoteDeviceList instance + * and returns a pointer to it. It's the user responsibility to free it when + * done using it
This method overload is for remote daemons which don't + * require authentication for accessing them. For daemons which do require + * authentication use the other method overload + * @param[in] ipAddress The IP address of the remote machine through which + * clients can connect to the rpcapd daemon + * @param[in] port The port of the remote machine through which clients can + * connect to the rpcapd daemon + * @return A pointer to the newly created PcapRemoteDeviceList, or NULL if (an + * appropriate error will be printed to log in each case): + * - IP address provided is NULL or not valid + * - WinPcap/Npcap encountered an error in creating the remote connection + * string + * - WinPcap/Npcap encountered an error connecting to the rpcapd daemon on the + * remote machine or retrieving devices on the remote machine + */ + static PcapRemoteDeviceList* getRemoteDeviceList(const IPAddress& ipAddress, + uint16_t port); + + /** + * An overload of the previous getRemoteDeviceList() method but with + * authentication support. This method is suitable for connecting to remote + * daemons which require authentication for accessing them + * @param[in] ipAddress The IP address of the remote machine through which + * clients can connect to the rpcapd daemon + * @param[in] port The port of the remote machine through which clients can + * connect to the rpcapd daemon + * @param[in] remoteAuth A pointer to the authentication object which contains + * the username and password for connecting to the remote daemon + * @return A pointer to the newly created PcapRemoteDeviceList, or NULL if (an + * appropriate error will be printed to log in each case): + * - IP address provided is NULL or not valid + * - WinPcap/Npcap encountered an error in creating the remote connection + * string + * - WinPcap/Npcap encountered an error connecting to the rpcapd daemon on the + * remote machine or retrieving devices on the remote machine + */ + static PcapRemoteDeviceList* + getRemoteDeviceList(const IPAddress& ipAddress, uint16_t port, + PcapRemoteAuthentication* remoteAuth); + + /** + * @return The IP address of the remote machine + */ + IPAddress getRemoteMachineIpAddress() const { + return m_RemoteMachineIpAddress; + } + + /** + * @return The port of the remote machine where packets are transmitted from + * the remote machine to the client machine + */ + uint16_t getRemoteMachinePort() const { return m_RemoteMachinePort; } + + /** + * Search a PcapRemoteDevice in the list by its IPv4 address + * @param[in] ip4Addr The IPv4 address + * @return The PcapRemoteDevice if found, NULL otherwise + */ + PcapRemoteDevice* getRemoteDeviceByIP(const IPv4Address& ip4Addr) const; + + /** + * Search a PcapRemoteDevice in the list by its IPv6 address + * @param[in] ip6Addr The IPv6 address + * @return The PcapRemoteDevice if found, NULL otherwise + */ + PcapRemoteDevice* getRemoteDeviceByIP(const IPv6Address& ip6Addr) const; + + /** + * Search a PcapRemoteDevice in the list by its IP address (IPv4 or IPv6) + * @param[in] ipAddr The IP address + * @return The PcapRemoteDevice if found, NULL otherwise + */ + PcapRemoteDevice* getRemoteDeviceByIP(const IPAddress& ipAddr) const; + + /** + * Search a PcapRemoteDevice in the list by its IP address + * @param[in] ipAddrAsString The IP address in string format + * @return The PcapRemoteDevice if found, NULL otherwise + */ + PcapRemoteDevice* + getRemoteDeviceByIP(const std::string& ipAddrAsString) const; + + /** + * @return An iterator object pointing to the first PcapRemoteDevice in list + */ + RemoteDeviceListIterator begin() { return m_RemoteDeviceList.begin(); } + + /** + * @return A const iterator object pointing to the first PcapRemoteDevice in + * list + */ + ConstRemoteDeviceListIterator begin() const { + return m_RemoteDeviceList.begin(); + } + + /** + * @return An iterator object pointing to the last PcapRemoteDevice in list + */ + RemoteDeviceListIterator end() { return m_RemoteDeviceList.end(); } + + /** + * @return A const iterator object pointing to the last PcapRemoteDevice in + * list + */ + ConstRemoteDeviceListIterator end() const { return m_RemoteDeviceList.end(); } +}; } // namespace pcpp diff --git a/Pcap++/header/PfRingDevice.h b/Pcap++/header/PfRingDevice.h index 3f644f63b7..c275119f77 100644 --- a/Pcap++/header/PfRingDevice.h +++ b/Pcap++/header/PfRingDevice.h @@ -5,10 +5,10 @@ #include "Device.h" #include "MacAddress.h" -#include "SystemUtils.h" #include "Packet.h" -#include +#include "SystemUtils.h" #include +#include /// @file @@ -17,334 +17,372 @@ struct __pfring; typedef struct __pfring pfring; /** -* \namespace pcpp -* \brief The main namespace for the PcapPlusPlus lib -*/ -namespace pcpp -{ - - class PfRingDevice; - - typedef void (*OnPfRingPacketsArriveCallback)(RawPacket* packets, uint32_t numOfPackets, uint8_t threadId, PfRingDevice* device, void* userCookie); - - - /** - * @class PfRingDevice - * A class representing a PF_RING port - */ - class PfRingDevice : public IDevice, public IFilterableDevice - { - friend class PfRingDeviceList; - private: - - struct CoreConfiguration - { - std::thread RxThread; - pfring* Channel; - bool IsInUse; - bool IsAffinitySet; - - CoreConfiguration(); - void clear(); - }; - - pfring** m_PfRingDescriptors; - uint8_t m_NumOfOpenedRxChannels; - std::string m_DeviceName; - int m_InterfaceIndex; - MacAddress m_MacAddress; - int m_DeviceMTU; - CoreConfiguration m_CoreConfiguration[MAX_NUM_OF_CORES]; - bool m_StopThread; - OnPfRingPacketsArriveCallback m_OnPacketsArriveCallback; - void* m_OnPacketsArriveUserCookie; - bool m_ReentrantMode; - bool m_HwClockEnabled; - bool m_IsFilterCurrentlySet; - - PfRingDevice(const char* deviceName); - - bool initCoreConfigurationByCoreMask(CoreMask coreMask); - void captureThreadMain(std::condition_variable* startCond, std::mutex* startMutex, const int* startState); - - int openSingleRxChannel(const char* deviceName, pfring** ring); - - bool getIsHwClockEnable() { setPfRingDeviceAttributes(); return m_HwClockEnabled; } - bool setPfRingDeviceClock(pfring* ring); - - void clearCoreConfiguration(); - int getCoresInUseCount() const; - - void setPfRingDeviceAttributes(); - - bool sendData(const uint8_t* packetData, int packetDataLength, bool flushTxQueues); - public: - - /** - * An enum representing the type of packet distribution between different RX channels - */ - enum ChannelDistribution - { - /** - * Packets are distributed between channels in a round-robin manner - */ - RoundRobin, - /** - * Packets are distributed between channels per flow (each flow goes for different channel) - */ - PerFlow - }; - - /** - * @struct PfRingStats - * A container for PfRingDevice statistics - */ - struct PfRingStats - { - /** Number of packets received */ - uint64_t recv; - /** Number of packets dropped */ - uint64_t drop; - }; - - /** - * A destructor for PfRingDevice class - */ - ~PfRingDevice(); - - /** - * Get the MAC address of the current device - * @return The MAC address of the current device - */ - MacAddress getMacAddress() { setPfRingDeviceAttributes(); return m_MacAddress; } - - /** - * Get PF_RING interface index of the current device - * @return PF_RING interface index of the current device - */ - int getInterfaceIndex() { setPfRingDeviceAttributes(); return m_InterfaceIndex; } - - /** - * Get MTU of the current device - * @return Upon success return the device MTU, 0 otherwise - */ - int getMtu() { setPfRingDeviceAttributes(); return m_DeviceMTU; } - - /** - * Return true if device supports hardware timestamping. If it does, this feature will be automatically set - * for this device. You can read more about this in PF_RING documentation - * @return True if device supports hardware timestamping, false otherwise - */ - bool isHwClockEnabledForDevice() { setPfRingDeviceAttributes(); return m_HwClockEnabled; } - - /** - * Gets the interface name (e.g eth0, eth1, etc.) - * @return The interface name - */ - std::string getDeviceName() const { return m_DeviceName; } - - - /** - * Start single-threaded capturing with callback. Works with open() or openSingleRxChannel(). - * @param[in] onPacketsArrive A callback to call whenever a packet arrives - * @param[in] onPacketsArriveUserCookie A cookie that will be delivered to onPacketsArrive callback on every packet - * @return True if this action succeeds, false otherwise - */ - bool startCaptureSingleThread(OnPfRingPacketsArriveCallback onPacketsArrive, void* onPacketsArriveUserCookie); - - /** - * Start multi-threaded (multi-core) capturing with callback. Works with openMultiRxChannels(). - * This method will return an error if the number of opened channels is different than the number of threads/cores - * requested - * @param[in] onPacketsArrive A callback to call whenever a packet arrives - * @param[in] onPacketsArriveUserCookie A cookie that will be delivered to onPacketsArrive callback on every packet - * @param[in] coreMask The cores to be used as mask. For example: - * @return True if this action succeeds, false otherwise - */ - bool startCaptureMultiThread(OnPfRingPacketsArriveCallback onPacketsArrive, void* onPacketsArriveUserCookie, CoreMask coreMask); - - /** - * Stops capturing packets (works will all type of startCapture*) - */ - void stopCapture(); - - - /** - * Opens a single RX channel (=RX queue) on this interface. All packets will be received on a single thread - * without core affinity. If the channel ID requested doesn't exist on this interface, the method will fail - * (return false) - * @param[in] channelId The requested channel ID - * @return True if this action succeeds, false otherwise - */ - bool openSingleRxChannel(uint8_t channelId); - - /** - * Opens a set of RX channels (=RX queues) on this interface, identified by their IDs. All packets will be received on a single thread - * without core affinity. If one of the channel IDs requested doesn't exist on this interface, the method will fail - * (return false) - * @param[in] channelIds An array of channel IDs - * @param[in] numOfChannelIds The channel ID array size - * @return True if this action succeeds, false otherwise - */ - bool openMultiRxChannels(const uint8_t* channelIds, int numOfChannelIds); - - /** - * Opens numOfRxChannelsToOpen RX channels. If numOfRxChannelsToOpen is larger than available RX queues for this - * interface than a number of RX channels will be opened on each RX queue. For example: if the user asks for 10 - * RX channels but the interface has only 4 RX queues, then 3 RX channels will be opened for RX-queue0 and RX-queue2, - * and 2 RX channels will be opened for RX-queue2 and RX-queue3. - * Packets will be distributed between different RX queues on per-flow manner, but within multiple RX channels in - * the same RX queue packet will be distributed according to distribution requested by "dist" - * @param[in] numOfRxChannelsToOpen Number of RX channels to open - * @param[in] dist Distribution method - * @return True if this action succeeds, false otherwise - */ - bool openMultiRxChannels(uint8_t numOfRxChannelsToOpen, ChannelDistribution dist); - - /** - * Gets the number of RX channels currently open. RX channels aren't necessary interface's RX queues - * because in some cases the user asks to open several channels on the same queue. For example: if the user uses - * openMultiRxChannels() and asks to open 8 channels but interface has only 4 RX queues, 2 channels will be - * opened for each RX queue - * @return Number of opened RX channels - */ - uint8_t getNumOfOpenedRxChannels() const { return m_NumOfOpenedRxChannels; } - - /** - * Gets the total number of RX channels (RX queues) this interface has - * @return The number of RX channels (queues) for this interface - */ - uint8_t getTotalNumOfRxChannels() const; - - /** - * Gets the core used in the current thread context - * @return The system core used in the current thread context - */ - SystemCore getCurrentCoreId() const; - - /** - * Get the statistics of a specific thread/core (=RX channel) - * @param[in] core The requested core - * @param[out] stats A reference for the stats object where the stats are written. Current values will be overridden - */ - void getThreadStatistics(SystemCore core, PfRingStats& stats) const; - - /** - * Get the statistics of the current thread/core (=RX channel) - * @param[out] stats A reference for the stats object where the stats are written. Current values will be overridden - */ - void getCurrentThreadStatistics(PfRingStats& stats) const; - - /** - * Get the statistics for the entire device. If more than 1 RX channel is opened, this method aggregates the stats - * of all channels - * @param[out] stats A reference for the stats object where the stats are written. Current values will be overridden - */ - void getStatistics(PfRingStats& stats) const; - - /** - * Return true if filter is currently set - * @return True if filter is currently set, false otherwise - */ - bool isFilterCurrentlySet() const; - - /** - * Send a raw packet. This packet must be fully specified (the MAC address up) - * and it will be transmitted as-is without any further manipulation. - * This method doesn't change or manipulate the data in any way (hence the "const" declaration). - * Note this method flushes the TX queues after the data is sent. So if you want to send several packets - * In the burst please use sendPackets() - * @param[in] rawPacket The raw packet to send - * @return True if raw packet was sent completely, false otherwise - */ - bool sendPacket(const RawPacket& rawPacket); - - /** - * Send raw data. This data must be a valid and fully specified packet (the MAC address up); - * it will be transmitted as-is without any further manipulation. - * This method doesn't change or manipulate the data in any way (hence the "const" declaration). - * Note this method flushes the TX queues after the data is sent. So if you want to send several packets - * in the burst please use sendPackets() - * @param[in] packetData The raw data to send - * @param[in] packetDataLength the length of packetData - * @return True if raw packet was sent completely, false otherwise - * - */ - bool sendPacket(const uint8_t* packetData, int packetDataLength); - - /** - * Send a packet. This packet must be fully specified (the MAC address up) - * and it will be transmitted as-is without any further manipulation. - * This method doesn't change or manipulate the data in any way (hence the "const" declaration). - * Note this method flushes the TX queues after the data is sent. So if you want to send several packets - * In the burst please use sendPackets() - * @param[in] packet The packet to send - * @return True if raw packet was sent completely, false otherwise - */ - bool sendPacket(const Packet& packet); - - /** - * Send raw packets. All raw packets must be fully specified (the MAC address up) - * and it will be transmitted as-is without any further manipulation. - * This method doesn't change or manipulate the raw packets data in any way (hence the "const" declaration). - * This method flushes the TX queues only when the last packet is sent - * @param[in] rawPacketsArr The RawPacket array - * @param[in] arrLength RawPacket array length - * @return Number of packets that were sent completely - */ - int sendPackets(const RawPacket* rawPacketsArr, int arrLength); - - /** - * Send packets. All packets must be fully specified (the MAC address up) - * and it will be transmitted as-is without any further manipulation. - * This method doesn't change or manipulate the packets data in any way (hence the "const" declaration). - * This method flushes the TX queues only when the last packet is sent - * @param[in] packetsArr An array of pointers to Packet objects - * @param[in] arrLength Packet pointers array length - * @return Number of packets that were sent completely - */ - int sendPackets(const Packet** packetsArr, int arrLength); - - /** - * Send all raw packets pointed by the RawPacketVector. All packets must be fully specified (the MAC address up) - * and it will be transmitted as-is without any further manipulation. - * This method doesn't change or manipulate the packets data in any way (hence the "const" declaration). - * This method flushes the TX queues only when the last packet is sent - * @param[in] rawPackets The raw packet vector - * @return Number of raw packets that were sent completely - */ - int sendPackets(const RawPacketVector& rawPackets); - - - // implement abstract methods - - - /** - * Opens the entire device (including all RX channels/queues on this interface). All packets will be received - * on a single thread without core affinity - * @return True if this action succeeds, false otherwise - */ - bool open(); - - /** - * Closes all RX channels currently opened in device - */ - void close(); - - using IFilterableDevice::setFilter; - - /** - * Sets a BPF filter to the device - * @param[in] filterAsString The BPF filter in string format - */ - bool setFilter(std::string filterAsString); - - /** - * Remove a filter if currently set - * @return True if filter was removed successfully or if no filter was set, false otherwise - */ - bool clearFilter(); - }; + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp { + +class PfRingDevice; + +typedef void (*OnPfRingPacketsArriveCallback)(RawPacket* packets, + uint32_t numOfPackets, + uint8_t threadId, + PfRingDevice* device, + void* userCookie); + +/** + * @class PfRingDevice + * A class representing a PF_RING port + */ +class PfRingDevice : public IDevice, public IFilterableDevice { + friend class PfRingDeviceList; + + private: + struct CoreConfiguration { + std::thread RxThread; + pfring* Channel; + bool IsInUse; + bool IsAffinitySet; + + CoreConfiguration(); + void clear(); + }; + + pfring** m_PfRingDescriptors; + uint8_t m_NumOfOpenedRxChannels; + std::string m_DeviceName; + int m_InterfaceIndex; + MacAddress m_MacAddress; + int m_DeviceMTU; + CoreConfiguration m_CoreConfiguration[MAX_NUM_OF_CORES]; + bool m_StopThread; + OnPfRingPacketsArriveCallback m_OnPacketsArriveCallback; + void* m_OnPacketsArriveUserCookie; + bool m_ReentrantMode; + bool m_HwClockEnabled; + bool m_IsFilterCurrentlySet; + + PfRingDevice(const char* deviceName); + + bool initCoreConfigurationByCoreMask(CoreMask coreMask); + void captureThreadMain(std::condition_variable* startCond, + std::mutex* startMutex, const int* startState); + + int openSingleRxChannel(const char* deviceName, pfring** ring); + + bool getIsHwClockEnable() { + setPfRingDeviceAttributes(); + return m_HwClockEnabled; + } + bool setPfRingDeviceClock(pfring* ring); + + void clearCoreConfiguration(); + int getCoresInUseCount() const; + + void setPfRingDeviceAttributes(); + + bool sendData(const uint8_t* packetData, int packetDataLength, + bool flushTxQueues); + + public: + /** + * An enum representing the type of packet distribution between different RX + * channels + */ + enum ChannelDistribution { + /** + * Packets are distributed between channels in a round-robin manner + */ + RoundRobin, + /** + * Packets are distributed between channels per flow (each flow goes for + * different channel) + */ + PerFlow + }; + + /** + * @struct PfRingStats + * A container for PfRingDevice statistics + */ + struct PfRingStats { + /** Number of packets received */ + uint64_t recv; + /** Number of packets dropped */ + uint64_t drop; + }; + + /** + * A destructor for PfRingDevice class + */ + ~PfRingDevice(); + + /** + * Get the MAC address of the current device + * @return The MAC address of the current device + */ + MacAddress getMacAddress() { + setPfRingDeviceAttributes(); + return m_MacAddress; + } + + /** + * Get PF_RING interface index of the current device + * @return PF_RING interface index of the current device + */ + int getInterfaceIndex() { + setPfRingDeviceAttributes(); + return m_InterfaceIndex; + } + + /** + * Get MTU of the current device + * @return Upon success return the device MTU, 0 otherwise + */ + int getMtu() { + setPfRingDeviceAttributes(); + return m_DeviceMTU; + } + + /** + * Return true if device supports hardware timestamping. If it does, this + * feature will be automatically set for this device. You can read more about + * this in PF_RING documentation + * @return True if device supports hardware timestamping, false otherwise + */ + bool isHwClockEnabledForDevice() { + setPfRingDeviceAttributes(); + return m_HwClockEnabled; + } + + /** + * Gets the interface name (e.g eth0, eth1, etc.) + * @return The interface name + */ + std::string getDeviceName() const { return m_DeviceName; } + + /** + * Start single-threaded capturing with callback. Works with open() or + * openSingleRxChannel(). + * @param[in] onPacketsArrive A callback to call whenever a packet arrives + * @param[in] onPacketsArriveUserCookie A cookie that will be delivered to + * onPacketsArrive callback on every packet + * @return True if this action succeeds, false otherwise + */ + bool startCaptureSingleThread(OnPfRingPacketsArriveCallback onPacketsArrive, + void* onPacketsArriveUserCookie); + + /** + * Start multi-threaded (multi-core) capturing with callback. Works with + * openMultiRxChannels(). This method will return an error if the number of + * opened channels is different than the number of threads/cores requested + * @param[in] onPacketsArrive A callback to call whenever a packet arrives + * @param[in] onPacketsArriveUserCookie A cookie that will be delivered to + * onPacketsArrive callback on every packet + * @param[in] coreMask The cores to be used as mask. For example: + * @return True if this action succeeds, false otherwise + */ + bool startCaptureMultiThread(OnPfRingPacketsArriveCallback onPacketsArrive, + void* onPacketsArriveUserCookie, + CoreMask coreMask); + + /** + * Stops capturing packets (works will all type of startCapture*) + */ + void stopCapture(); + + /** + * Opens a single RX channel (=RX queue) on this interface. All packets will + * be received on a single thread without core affinity. If the channel ID + * requested doesn't exist on this interface, the method will fail (return + * false) + * @param[in] channelId The requested channel ID + * @return True if this action succeeds, false otherwise + */ + bool openSingleRxChannel(uint8_t channelId); + + /** + * Opens a set of RX channels (=RX queues) on this interface, identified by + * their IDs. All packets will be received on a single thread without core + * affinity. If one of the channel IDs requested doesn't exist on this + * interface, the method will fail (return false) + * @param[in] channelIds An array of channel IDs + * @param[in] numOfChannelIds The channel ID array size + * @return True if this action succeeds, false otherwise + */ + bool openMultiRxChannels(const uint8_t* channelIds, int numOfChannelIds); + + /** + * Opens numOfRxChannelsToOpen RX channels. If numOfRxChannelsToOpen is larger + * than available RX queues for this interface than a number of RX channels + * will be opened on each RX queue. For example: if the user asks for 10 RX + * channels but the interface has only 4 RX queues, then 3 RX channels will be + * opened for RX-queue0 and RX-queue2, and 2 RX channels will be opened for + * RX-queue2 and RX-queue3. Packets will be distributed between different RX + * queues on per-flow manner, but within multiple RX channels in the same RX + * queue packet will be distributed according to distribution requested by + * "dist" + * @param[in] numOfRxChannelsToOpen Number of RX channels to open + * @param[in] dist Distribution method + * @return True if this action succeeds, false otherwise + */ + bool openMultiRxChannels(uint8_t numOfRxChannelsToOpen, + ChannelDistribution dist); + + /** + * Gets the number of RX channels currently open. RX channels aren't necessary + * interface's RX queues because in some cases the user asks to open several + * channels on the same queue. For example: if the user uses + * openMultiRxChannels() and asks to open 8 channels but interface has only 4 + * RX queues, 2 channels will be opened for each RX queue + * @return Number of opened RX channels + */ + uint8_t getNumOfOpenedRxChannels() const { return m_NumOfOpenedRxChannels; } + + /** + * Gets the total number of RX channels (RX queues) this interface has + * @return The number of RX channels (queues) for this interface + */ + uint8_t getTotalNumOfRxChannels() const; + + /** + * Gets the core used in the current thread context + * @return The system core used in the current thread context + */ + SystemCore getCurrentCoreId() const; + + /** + * Get the statistics of a specific thread/core (=RX channel) + * @param[in] core The requested core + * @param[out] stats A reference for the stats object where the stats are + * written. Current values will be overridden + */ + void getThreadStatistics(SystemCore core, PfRingStats& stats) const; + + /** + * Get the statistics of the current thread/core (=RX channel) + * @param[out] stats A reference for the stats object where the stats are + * written. Current values will be overridden + */ + void getCurrentThreadStatistics(PfRingStats& stats) const; + + /** + * Get the statistics for the entire device. If more than 1 RX channel is + * opened, this method aggregates the stats of all channels + * @param[out] stats A reference for the stats object where the stats are + * written. Current values will be overridden + */ + void getStatistics(PfRingStats& stats) const; + + /** + * Return true if filter is currently set + * @return True if filter is currently set, false otherwise + */ + bool isFilterCurrentlySet() const; + + /** + * Send a raw packet. This packet must be fully specified (the MAC address up) + * and it will be transmitted as-is without any further manipulation. + * This method doesn't change or manipulate the data in any way (hence the + * "const" declaration). Note this method flushes the TX queues after the data + * is sent. So if you want to send several packets In the burst please use + * sendPackets() + * @param[in] rawPacket The raw packet to send + * @return True if raw packet was sent completely, false otherwise + */ + bool sendPacket(const RawPacket& rawPacket); + + /** + * Send raw data. This data must be a valid and fully specified packet (the + * MAC address up); it will be transmitted as-is without any further + * manipulation. This method doesn't change or manipulate the data in any way + * (hence the "const" declaration). Note this method flushes the TX queues + * after the data is sent. So if you want to send several packets in the burst + * please use sendPackets() + * @param[in] packetData The raw data to send + * @param[in] packetDataLength the length of packetData + * @return True if raw packet was sent completely, false otherwise + * + */ + bool sendPacket(const uint8_t* packetData, int packetDataLength); + + /** + * Send a packet. This packet must be fully specified (the MAC address up) + * and it will be transmitted as-is without any further manipulation. + * This method doesn't change or manipulate the data in any way (hence the + * "const" declaration). Note this method flushes the TX queues after the data + * is sent. So if you want to send several packets In the burst please use + * sendPackets() + * @param[in] packet The packet to send + * @return True if raw packet was sent completely, false otherwise + */ + bool sendPacket(const Packet& packet); + + /** + * Send raw packets. All raw packets must be fully specified (the MAC address + * up) and it will be transmitted as-is without any further manipulation. This + * method doesn't change or manipulate the raw packets data in any way (hence + * the "const" declaration). This method flushes the TX queues only when the + * last packet is sent + * @param[in] rawPacketsArr The RawPacket array + * @param[in] arrLength RawPacket array length + * @return Number of packets that were sent completely + */ + int sendPackets(const RawPacket* rawPacketsArr, int arrLength); + + /** + * Send packets. All packets must be fully specified (the MAC address up) + * and it will be transmitted as-is without any further manipulation. + * This method doesn't change or manipulate the packets data in any way (hence + * the "const" declaration). This method flushes the TX queues only when the + * last packet is sent + * @param[in] packetsArr An array of pointers to Packet objects + * @param[in] arrLength Packet pointers array length + * @return Number of packets that were sent completely + */ + int sendPackets(const Packet** packetsArr, int arrLength); + + /** + * Send all raw packets pointed by the RawPacketVector. All packets must be + * fully specified (the MAC address up) and it will be transmitted as-is + * without any further manipulation. This method doesn't change or manipulate + * the packets data in any way (hence the "const" declaration). This method + * flushes the TX queues only when the last packet is sent + * @param[in] rawPackets The raw packet vector + * @return Number of raw packets that were sent completely + */ + int sendPackets(const RawPacketVector& rawPackets); + + // implement abstract methods + + /** + * Opens the entire device (including all RX channels/queues on this + * interface). All packets will be received on a single thread without core + * affinity + * @return True if this action succeeds, false otherwise + */ + bool open(); + + /** + * Closes all RX channels currently opened in device + */ + void close(); + + using IFilterableDevice::setFilter; + + /** + * Sets a BPF filter to the device + * @param[in] filterAsString The BPF filter in string format + */ + bool setFilter(std::string filterAsString); + + /** + * Remove a filter if currently set + * @return True if filter was removed successfully or if no filter was set, + * false otherwise + */ + bool clearFilter(); +}; } // namespace pcpp diff --git a/Pcap++/header/PfRingDeviceList.h b/Pcap++/header/PfRingDeviceList.h index 3684b0e577..e37bc02f5d 100644 --- a/Pcap++/header/PfRingDeviceList.h +++ b/Pcap++/header/PfRingDeviceList.h @@ -8,61 +8,62 @@ /// @file /** -* \namespace pcpp -* \brief The main namespace for the PcapPlusPlus lib -*/ -namespace pcpp -{ + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp { - /** - * @class PfRingDeviceList - * A singleton class that holds all available PF_RING devices. Through this class the user can iterate all PF_RING devices or find a specific - * device by name - */ - class PfRingDeviceList - { - private: - std::vector m_PfRingDeviceList; - std::string m_PfRingVersion; +/** + * @class PfRingDeviceList + * A singleton class that holds all available PF_RING devices. Through this + * class the user can iterate all PF_RING devices or find a specific device by + * name + */ +class PfRingDeviceList { + private: + std::vector m_PfRingDeviceList; + std::string m_PfRingVersion; + + PfRingDeviceList(); + // private copy c'tor + PfRingDeviceList(const PfRingDeviceList& other); + PfRingDeviceList& operator=(const PfRingDeviceList& other); + // private d'tor + ~PfRingDeviceList(); - PfRingDeviceList(); - // private copy c'tor - PfRingDeviceList(const PfRingDeviceList& other); - PfRingDeviceList& operator=(const PfRingDeviceList& other); - // private d'tor - ~PfRingDeviceList(); + void calcPfRingVersion(void* ring); - void calcPfRingVersion(void* ring); - public: - /** - * A static method that returns the singleton object for PfRingDeviceList - * @return PfRingDeviceList singleton - */ - static PfRingDeviceList& getInstance() - { - static PfRingDeviceList instance; - return instance; - } + public: + /** + * A static method that returns the singleton object for PfRingDeviceList + * @return PfRingDeviceList singleton + */ + static PfRingDeviceList& getInstance() { + static PfRingDeviceList instance; + return instance; + } - /** - * Return a list of all available PF_RING devices - * @return a list of all available PF_RING devices - */ - const std::vector& getPfRingDevicesList() const { return m_PfRingDeviceList; } + /** + * Return a list of all available PF_RING devices + * @return a list of all available PF_RING devices + */ + const std::vector& getPfRingDevicesList() const { + return m_PfRingDeviceList; + } - /** - * Get a PF_RING device by name. The name is the Linux interface name which appears in ifconfig - * (e.g eth0, eth1, etc.) - * @return A pointer to the PF_RING device - */ - PfRingDevice* getPfRingDeviceByName(const std::string &devName) const; + /** + * Get a PF_RING device by name. The name is the Linux interface name which + * appears in ifconfig (e.g eth0, eth1, etc.) + * @return A pointer to the PF_RING device + */ + PfRingDevice* getPfRingDeviceByName(const std::string& devName) const; - /** - * Get installed PF_RING version - * @return A string representing PF_RING version - */ - std::string getPfRingVersion() const { return m_PfRingVersion; } - }; + /** + * Get installed PF_RING version + * @return A string representing PF_RING version + */ + std::string getPfRingVersion() const { return m_PfRingVersion; } +}; } // namespace pcpp diff --git a/Pcap++/header/RawSocketDevice.h b/Pcap++/header/RawSocketDevice.h index e298a16888..247be5bacf 100644 --- a/Pcap++/header/RawSocketDevice.h +++ b/Pcap++/header/RawSocketDevice.h @@ -3,162 +3,190 @@ /// @file -#include "IpAddress.h" #include "Device.h" +#include "IpAddress.h" /** -* \namespace pcpp -* \brief The main namespace for the PcapPlusPlus lib -*/ -namespace pcpp -{ - /** - * @class RawSocketDevice - * A class that wraps the raw socket functionality. A raw socket is a network socket that allows direct sending and receiving - * of IP packets without any protocol-specific transport layer formatting - * (taken from Wikipedia: https://en.wikipedia.org/wiki/Network_socket#Raw_socket). - * This wrapper class enables creation of a raw socket, binding it to a network interface, and then receiving and sending - * packets on it. Current implementation supports only Windows and Linux because other platforms provide poor support for raw - * sockets making them practically unusable. There are also major differences between Linux and Windows in raw socket - * implementation, let's mention some of the: - * - On Windows administrative privileges are required for raw sockets creation, meaning the process running the code - * has to have these privileges. In Linux 'sudo' is required - * - On Windows raw sockets are implemented in L3, meaning the L2 (Ethernet) layer is omitted by the socket and only L3 and - * up are visible to the user. On Linux raw sockets are implemented on L2, meaning all layers (including the Ethernet - * data) are visible to the user. - * - On Windows sending packets is not supported, a raw socket can only receive packets. On Linux both send and receive are - * supported - * - Linux doesn't require binding to a specific network interface for receiving packets, but it does require binding - * for sending packets. Windows requires binding for receiving packets. For the sake of keeping a unified and simple cross-platform interface - * this class requires binding for both Linux and Windows, on both send and receive - * - * More details about opening the raw socket, receiving and sending packets are explained in the corresponding class methods. - * Raw sockets are supported for both IPv4 and IPv6, so you can create and bind raw sockets to each of the two. - * Also, there is no limit on the number of sockets opened for a specific IP address or network interface, so you can - * create multiple instances of this class and bind all of them to the same interface and IP address. - */ - class RawSocketDevice : public IDevice - { - public: - - /** - * An enum for reporting packet receive results - */ - enum RecvPacketResult - { - /** Receive success */ - RecvSuccess = 0, - /** Receive timeout - timeout expired without any packets being captured */ - RecvTimeout = 1, - /** Receive would block - in non-blocking mode if there are no packets in the rx queue the receive method will return immediately with this return value */ - RecvWouldBlock = 2, - /** Receive error, usually will be followed by an error log */ - RecvError = 3 - }; - - /* - * A c'tor for this class. This c'tor doesn't create the raw socket, but rather initializes internal structures. The actual - * raw socket creation is done in the open() method. Each raw socket is bound to a network interface which means - * packets will be received and sent from only from this network interface only - * @param[in] interfaceIP The network interface IP to bind the raw socket to. It can be either an IPv4 or IPv6 address - * (both are supported in raw sockets) - */ - explicit RawSocketDevice(const IPAddress& interfaceIP); - - /** - * A d'tor for this class. It closes the raw socket if not previously closed by calling close() - */ - ~RawSocketDevice(); - - /** - * Receive a packet on the raw socket. This method has several modes of operation: - * - Blocking/non-blocking - in blocking mode the method will not return until a packet is received on the socket - * or until the timeout expires. In non-blocking mode it will return immediately and in case no packets are on the - * receive queue RawSocketDevice#RecvWouldBlock will be returned. Unless specified otherwise, the default value is - * blocking mode - * - Receive timeout - in blocking mode, the user can set a timeout to wait until a packet is received. If the timeout - * expires and no packets were received, the method will return RawSocketDevice#RecvTimeout. The default value is a - * negative value which means no timeout - * - * There is a slight difference on this method's behavior between Windows and Linux around how packets are received. - * On Linux the received packet contains all layers starting from the L2 (Ethernet). However on Windows raw socket are - * integrated in L3 level so the received packet contains only L3 (IP) layer and up. - * @param[out] rawPacket An empty packet instance where the received packet data will be written to - * @param[in] blocking Indicates whether to run in blocking or non-blocking mode. Default value is blocking - * @param[in] timeout When in blocking mode, specifies the timeout [in seconds] to wait for a packet. If timeout expired - * and no packets were captured the method will return RawSocketDevice#RecvTimeout. Zero or negative values mean no - * timeout. The default value is no timeout - * @return The method returns one on the following values: - * - RawSocketDevice#RecvSuccess is returned if a packet was received successfully - * - RawSocketDevice#RecvTimeout is returned if in blocking mode and timeout expired - * - RawSocketDevice#RecvWouldBlock is returned if in non-blocking mode and no packets were captured - * - RawSocketDevice#RecvError is returned if an error occurred such as device is not opened or the recv operation - * returned some error. A log message will be followed specifying the error and error code - */ - RecvPacketResult receivePacket(RawPacket& rawPacket, bool blocking = true, int timeout = -1); - - /** - * Receive packets into a packet vector for a certain amount of time. This method starts a timer and invokes the - * receivePacket() method in blocking mode repeatedly until the timeout expires. All packets received successfully are - * put into a packet vector - * @param[out] packetVec The packet vector to add the received packet to - * @param[in] timeout Timeout in seconds to receive packets on the raw socket - * @param[out] failedRecv Number of receive attempts that failed - * @return The number of packets received successfully - */ - int receivePackets(RawPacketVector& packetVec, int timeout, int& failedRecv); - - /** - * Send an Ethernet packet to the network. L2 protocols other than Ethernet are not supported in raw sockets. - * The entire packet is sent as is, including the original Ethernet and IP data. - * This method is only supported in Linux as Windows doesn't allow sending packets from raw sockets. Using - * it from other platforms will also return "false" with a corresponding error log message - * @param[in] rawPacket The packet to send - * @return True if packet was sent successfully or false if the socket is not open, if the packet is not Ethernet or - * if there was a failure sending the packet - */ - bool sendPacket(const RawPacket* rawPacket); - - /** - * Send a set of Ethernet packets to the network. L2 protocols other than Ethernet are not supported by raw sockets. - * The entire packet is sent as is, including the original Ethernet and IP data. - * This method is only supported in Linux as Windows doesn't allow sending packets from raw sockets. Using it from - * other platforms will return "false" with an appropriate error log message - * @param[in] packetVec The set of packets to send - * @return The number of packets sent successfully. For packets that weren't sent successfully there will be a - * corresponding error message printed to log - */ - int sendPackets(const RawPacketVector& packetVec); - - // overridden methods - - /** - * Open the device by creating a raw socket and binding it to the network interface specified in the c'tor - * @return True if device was opened successfully, false otherwise with a corresponding error log message - */ - virtual bool open(); - - /** - * Close the raw socket - */ - virtual void close(); - - private: - - enum SocketFamily - { - Ethernet = 0, - IPv4 = 1, - IPv6 = 2 - }; - - SocketFamily m_SockFamily; - void* m_Socket; - IPAddress m_InterfaceIP; - - RecvPacketResult getError(int& errorCode) const; - - }; -} + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp { +/** + * @class RawSocketDevice + * A class that wraps the raw socket functionality. A raw socket is a network + * socket that allows direct sending and receiving of IP packets without any + * protocol-specific transport layer formatting (taken from Wikipedia: + * https://en.wikipedia.org/wiki/Network_socket#Raw_socket). This wrapper class + * enables creation of a raw socket, binding it to a network interface, and then + * receiving and sending packets on it. Current implementation supports only + * Windows and Linux because other platforms provide poor support for raw + * sockets making them practically unusable. There are also major differences + * between Linux and Windows in raw socket implementation, let's mention some of + * the: + * - On Windows administrative privileges are required for raw sockets + * creation, meaning the process running the code has to have these privileges. + * In Linux 'sudo' is required + * - On Windows raw sockets are implemented in L3, meaning the L2 (Ethernet) + * layer is omitted by the socket and only L3 and up are visible to the user. On + * Linux raw sockets are implemented on L2, meaning all layers (including the + * Ethernet data) are visible to the user. + * - On Windows sending packets is not supported, a raw socket can only receive + * packets. On Linux both send and receive are supported + * - Linux doesn't require binding to a specific network interface for + * receiving packets, but it does require binding for sending packets. Windows + * requires binding for receiving packets. For the sake of keeping a unified and + * simple cross-platform interface this class requires binding for both Linux + * and Windows, on both send and receive + * + * More details about opening the raw socket, receiving and sending packets are + * explained in the corresponding class methods. Raw sockets are supported for + * both IPv4 and IPv6, so you can create and bind raw sockets to each of the + * two. Also, there is no limit on the number of sockets opened for a specific + * IP address or network interface, so you can create multiple instances of this + * class and bind all of them to the same interface and IP address. + */ +class RawSocketDevice : public IDevice { + public: + /** + * An enum for reporting packet receive results + */ + enum RecvPacketResult { + /** Receive success */ + RecvSuccess = 0, + /** Receive timeout - timeout expired without any packets being captured */ + RecvTimeout = 1, + /** Receive would block - in non-blocking mode if there are no packets in + the rx queue the receive method will return immediately with this return + value */ + RecvWouldBlock = 2, + /** Receive error, usually will be followed by an error log */ + RecvError = 3 + }; + + /* + * A c'tor for this class. This c'tor doesn't create the raw socket, but + * rather initializes internal structures. The actual raw socket creation is + * done in the open() method. Each raw socket is bound to a network interface + * which means packets will be received and sent from only from this network + * interface only + * @param[in] interfaceIP The network interface IP to bind the raw socket to. + * It can be either an IPv4 or IPv6 address (both are supported in raw + * sockets) + */ + explicit RawSocketDevice(const IPAddress& interfaceIP); + + /** + * A d'tor for this class. It closes the raw socket if not previously closed + * by calling close() + */ + ~RawSocketDevice(); + + /** + * Receive a packet on the raw socket. This method has several modes of + * operation: + * - Blocking/non-blocking - in blocking mode the method will not return + * until a packet is received on the socket or until the timeout expires. In + * non-blocking mode it will return immediately and in case no packets are on + * the receive queue RawSocketDevice#RecvWouldBlock will be returned. Unless + * specified otherwise, the default value is blocking mode + * - Receive timeout - in blocking mode, the user can set a timeout to wait + * until a packet is received. If the timeout expires and no packets were + * received, the method will return RawSocketDevice#RecvTimeout. The default + * value is a negative value which means no timeout + * + * There is a slight difference on this method's behavior between Windows and + * Linux around how packets are received. On Linux the received packet + * contains all layers starting from the L2 (Ethernet). However on Windows raw + * socket are integrated in L3 level so the received packet contains only L3 + * (IP) layer and up. + * @param[out] rawPacket An empty packet instance where the received packet + * data will be written to + * @param[in] blocking Indicates whether to run in blocking or non-blocking + * mode. Default value is blocking + * @param[in] timeout When in blocking mode, specifies the timeout [in + * seconds] to wait for a packet. If timeout expired and no packets were + * captured the method will return RawSocketDevice#RecvTimeout. Zero or + * negative values mean no timeout. The default value is no timeout + * @return The method returns one on the following values: + * - RawSocketDevice#RecvSuccess is returned if a packet was received + * successfully + * - RawSocketDevice#RecvTimeout is returned if in blocking mode and timeout + * expired + * - RawSocketDevice#RecvWouldBlock is returned if in non-blocking mode and + * no packets were captured + * - RawSocketDevice#RecvError is returned if an error occurred such as + * device is not opened or the recv operation returned some error. A log + * message will be followed specifying the error and error code + */ + RecvPacketResult receivePacket(RawPacket& rawPacket, bool blocking = true, + int timeout = -1); + + /** + * Receive packets into a packet vector for a certain amount of time. This + * method starts a timer and invokes the receivePacket() method in blocking + * mode repeatedly until the timeout expires. All packets received + * successfully are put into a packet vector + * @param[out] packetVec The packet vector to add the received packet to + * @param[in] timeout Timeout in seconds to receive packets on the raw socket + * @param[out] failedRecv Number of receive attempts that failed + * @return The number of packets received successfully + */ + int receivePackets(RawPacketVector& packetVec, int timeout, int& failedRecv); + + /** + * Send an Ethernet packet to the network. L2 protocols other than Ethernet + * are not supported in raw sockets. The entire packet is sent as is, + * including the original Ethernet and IP data. This method is only supported + * in Linux as Windows doesn't allow sending packets from raw sockets. Using + * it from other platforms will also return "false" with a corresponding error + * log message + * @param[in] rawPacket The packet to send + * @return True if packet was sent successfully or false if the socket is not + * open, if the packet is not Ethernet or if there was a failure sending the + * packet + */ + bool sendPacket(const RawPacket* rawPacket); + + /** + * Send a set of Ethernet packets to the network. L2 protocols other than + * Ethernet are not supported by raw sockets. The entire packet is sent as is, + * including the original Ethernet and IP data. This method is only supported + * in Linux as Windows doesn't allow sending packets from raw sockets. Using + * it from other platforms will return "false" with an appropriate error log + * message + * @param[in] packetVec The set of packets to send + * @return The number of packets sent successfully. For packets that weren't + * sent successfully there will be a corresponding error message printed to + * log + */ + int sendPackets(const RawPacketVector& packetVec); + + // overridden methods + + /** + * Open the device by creating a raw socket and binding it to the network + * interface specified in the c'tor + * @return True if device was opened successfully, false otherwise with a + * corresponding error log message + */ + virtual bool open(); + + /** + * Close the raw socket + */ + virtual void close(); + + private: + enum SocketFamily { Ethernet = 0, + IPv4 = 1, + IPv6 = 2 }; + + SocketFamily m_SockFamily; + void* m_Socket; + IPAddress m_InterfaceIP; + + RecvPacketResult getError(int& errorCode) const; +}; +} // namespace pcpp #endif // PCAPPP_RAW_SOCKET_DEVICE diff --git a/Pcap++/header/WinPcapLiveDevice.h b/Pcap++/header/WinPcapLiveDevice.h index b843b58283..4f9cd0e6e3 100644 --- a/Pcap++/header/WinPcapLiveDevice.h +++ b/Pcap++/header/WinPcapLiveDevice.h @@ -8,55 +8,73 @@ #include "PcapLiveDevice.h" /** -* \namespace pcpp -* \brief The main namespace for the PcapPlusPlus lib -*/ -namespace pcpp -{ + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp { - /** - * @class WinPcapLiveDevice - * A class that wraps a Windows network interface (each of the interfaces listed in ipconfig). - * This class is almost similar in its capabilities to PcapLiveDevice (its parent class) with some small changes that mainly result from - * differences between libpcap and WinPcap/Npcap. Please see the reference for PcapLiveDevice for more details - */ - class WinPcapLiveDevice : public PcapLiveDevice - { - friend class PcapLiveDeviceList; - protected: - int m_MinAmountOfDataToCopyFromKernelToApplication; +/** + * @class WinPcapLiveDevice + * A class that wraps a Windows network interface (each of the interfaces listed + * in ipconfig). This class is almost similar in its capabilities to + * PcapLiveDevice (its parent class) with some small changes that mainly result + * from differences between libpcap and WinPcap/Npcap. Please see the reference + * for PcapLiveDevice for more details + */ +class WinPcapLiveDevice : public PcapLiveDevice { + friend class PcapLiveDeviceList; + + protected: + int m_MinAmountOfDataToCopyFromKernelToApplication; - // c'tor is not public, there should be only one for every interface (created by PcapLiveDeviceList) - WinPcapLiveDevice(pcap_if_t* iface, bool calculateMTU, bool calculateMacAddress, bool calculateDefaultGateway); - // copy c'tor is not public - WinPcapLiveDevice( const WinPcapLiveDevice& other ); - WinPcapLiveDevice& operator=(const WinPcapLiveDevice& other); + // c'tor is not public, there should be only one for every interface (created + // by PcapLiveDeviceList) + WinPcapLiveDevice(pcap_if_t* iface, bool calculateMTU, + bool calculateMacAddress, bool calculateDefaultGateway); + // copy c'tor is not public + WinPcapLiveDevice(const WinPcapLiveDevice& other); + WinPcapLiveDevice& operator=(const WinPcapLiveDevice& other); - public: - virtual LiveDeviceType getDeviceType() const { return WinPcapDevice; } + public: + virtual LiveDeviceType getDeviceType() const { return WinPcapDevice; } - bool startCapture(OnPacketArrivesCallback onPacketArrives, void* onPacketArrivesUserCookie, int intervalInSecondsToUpdateStats, OnStatsUpdateCallback onStatsUpdate, void* onStatsUpdateUserCookie); - bool startCapture(int intervalInSecondsToUpdateStats, OnStatsUpdateCallback onStatsUpdate, void* onStatsUpdateUserCookie); - bool startCapture(RawPacketVector& capturedPacketsVector) { return PcapLiveDevice::startCapture(capturedPacketsVector); } + bool startCapture(OnPacketArrivesCallback onPacketArrives, + void* onPacketArrivesUserCookie, + int intervalInSecondsToUpdateStats, + OnStatsUpdateCallback onStatsUpdate, + void* onStatsUpdateUserCookie); + bool startCapture(int intervalInSecondsToUpdateStats, + OnStatsUpdateCallback onStatsUpdate, + void* onStatsUpdateUserCookie); + bool startCapture(RawPacketVector& capturedPacketsVector) { + return PcapLiveDevice::startCapture(capturedPacketsVector); + } - using PcapLiveDevice::sendPackets; - virtual int sendPackets(RawPacket* rawPacketsArr, int arrLength); + using PcapLiveDevice::sendPackets; + virtual int sendPackets(RawPacket* rawPacketsArr, int arrLength); - /** - * WinPcap/Npcap have a feature (that doesn't exist in libpcap) to change the minimum amount of data in the kernel buffer that causes a read - * from the application to return (unless the timeout expires). Please see documentation for pcap_setmintocopy for more info. This method - * enables the user to change this size. Note the device must be open for this method to work - * @param[in] size The size to set in bytes - * @return True if set succeeded, false if the device is closed or if pcap_setmintocopy failed - */ - bool setMinAmountOfDataToCopyFromKernelToApplication(int size); + /** + * WinPcap/Npcap have a feature (that doesn't exist in libpcap) to change the + * minimum amount of data in the kernel buffer that causes a read from the + * application to return (unless the timeout expires). Please see + * documentation for pcap_setmintocopy for more info. This method enables the + * user to change this size. Note the device must be open for this method to + * work + * @param[in] size The size to set in bytes + * @return True if set succeeded, false if the device is closed or if + * pcap_setmintocopy failed + */ + bool setMinAmountOfDataToCopyFromKernelToApplication(int size); - /** - * @return The current amount of data in the kernel buffer that causes a read from the application to return (see also - * setMinAmountOfDataToCopyFromKernelToApplication()) - */ - int getMinAmountOfDataToCopyFromKernelToApplication() const { return m_MinAmountOfDataToCopyFromKernelToApplication; } - }; + /** + * @return The current amount of data in the kernel buffer that causes a read + * from the application to return (see also + * setMinAmountOfDataToCopyFromKernelToApplication()) + */ + int getMinAmountOfDataToCopyFromKernelToApplication() const { + return m_MinAmountOfDataToCopyFromKernelToApplication; + } +}; } // namespace pcpp diff --git a/Pcap++/header/XdpDevice.h b/Pcap++/header/XdpDevice.h index f3ab70c51e..d87c287e4c 100644 --- a/Pcap++/header/XdpDevice.h +++ b/Pcap++/header/XdpDevice.h @@ -4,338 +4,374 @@ /// @file #include "Device.h" -#include #include +#include /** -* \namespace pcpp -* \brief The main namespace for the PcapPlusPlus lib + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp { +/** + * @class XdpDevice + * A class wrapping the main functionality of using AF_XDP (XSK) sockets + * which are optimized for high performance packet processing. + * + * It provides methods for configuring and initializing an AF_XDP socket, and + * then send and receive packets through it. It also provides a method for + * gathering statistics from the socket. */ -namespace pcpp -{ - /** - * @class XdpDevice - * A class wrapping the main functionality of using AF_XDP (XSK) sockets - * which are optimized for high performance packet processing. - * - * It provides methods for configuring and initializing an AF_XDP socket, and then send and receive packets through it. - * It also provides a method for gathering statistics from the socket. - */ - class XdpDevice : public IDevice - { - public: - - /** - * @typedef OnPacketsArrive - * The callback that is called whenever packets are received on the socket - * @param[in] packets An array of the raw packets received - * @param[in] packetCount The number of packets received - * @param[in] device The XdpDevice packets are received from (represents the AF_XDP socket) - * @param[in] userCookie A pointer to an object set by the user when receivePackets() started - */ - typedef void (*OnPacketsArrive)(RawPacket packets[], uint32_t packetCount, XdpDevice* device, void* userCookie); - - /** - * @struct XdpDeviceConfiguration - * A struct containing the configuration parameters available for opening an XDP device - */ - struct XdpDeviceConfiguration - { - /** - * @enum AttachMode - * AF_XDP operation mode - */ - enum AttachMode - { - /** A fallback mode that works for any network device. Use it if the network driver doesn't have support for XDP */ - SkbMode = 1, - /** Use this mode if the network driver has support for XDP */ - DriverMode = 2, - /** Automatically detect whether driver mode is supported, otherwise fallback to SKB mode */ - AutoMode = 3 - }; - - /** AF_XDP operation mode */ - AttachMode attachMode; - - /** - * UMEM is a region of virtual contiguous memory, divided into equal-sized frames. - * This parameter determines the number of frames that will be allocated as pert of the UMEM. - **/ - uint16_t umemNumFrames; - - /** - * UMEM is a region of virtual contiguous memory, divided into equal-sized frames. - * This parameter determines the frame size that will be allocated. - * NOTE: the frame size should be equal to the memory page size (use getpagesize() to determine this size) - **/ - uint16_t umemFrameSize; - - /** - * The size of the fill ring used by the AF_XDP socket. This size should be a power of two - * and less or equal to the total number of UMEM frames - */ - uint32_t fillRingSize; - - /** - * The size of the completion ring used by the AF_XDP socket. This size should be a power of two - * and less or equal to the total number of UMEM frames - */ - uint32_t completionRingSize; - - /** - * The size of the RX ring used by the AF_XDP socket. This size should be a power of two - * and less or equal to the total number of UMEM frames - */ - uint32_t rxSize; - - /** - * The size of the TX ring used by the AF_XDP socket. This size should be a power of two - * and less or equal to the total number of UMEM frames - */ - uint32_t txSize; - - /** - * The max number of packets to be received or sent in one batch - */ - uint16_t rxTxBatchSize; - - /** - * A c'tor for this struct. Each parameter has a default value described below. - * @param[in] attachMode AF_XDP operation mode. The default value is auto mode - * @param[in] umemNumFrames Number of UMEM frames to allocate. The default value is 4096 - * @param[in] umemFrameSize The size of each UMEM frame. The default value is equal to getpagesize() - * @param[in] fillRingSize The size of the fill ring used by the AF_XDP socket. The default value is 4096 - * @param[in] completionRingSize The size of the completion ring used by the AF_XDP socket. The default value is 2048 - * @param[in] rxSize The size of the RX ring used by the AF_XDP socket. The default value is 2048 - * @param[in] txSize The size of the TX ring used by the AF_XDP socket. The default value is 2048 - * @param[in] rxTxBatchSize The max number of packets to be received or sent in one batch. The default value is 64 - */ - explicit XdpDeviceConfiguration(AttachMode attachMode = AutoMode, - uint16_t umemNumFrames = 0, - uint16_t umemFrameSize = 0, - uint32_t fillRingSize = 0, - uint32_t completionRingSize = 0, - uint32_t rxSize = 0, - uint32_t txSize = 0, - uint16_t rxTxBatchSize = 0) - { - this->attachMode = attachMode; - this->umemNumFrames = umemNumFrames; - this->umemFrameSize = umemFrameSize; - this->fillRingSize = fillRingSize; - this->completionRingSize = completionRingSize; - this->rxSize = rxSize; - this->txSize = txSize; - this->rxTxBatchSize = rxTxBatchSize; - } - }; - - /** - * @struct XdpDeviceStats - * A container for XDP device statistics - */ - struct XdpDeviceStats - { - /** The timestamp when the stats were collected */ - timespec timestamp; - /** Number of packets received */ - uint64_t rxPackets; - /** Packets received per second. Measured from to the previous time stats were collected */ - uint64_t rxPacketsPerSec; - /** Number of bytes received */ - uint64_t rxBytes; - /** Bytes per second received. Measured from to the previous time stats were collected */ - uint64_t rxBytesPerSec; - /** Total number of dropped RX packets */ - uint64_t rxDroppedTotalPackets; - /** RX packets dropped due to invalid descriptor */ - uint64_t rxDroppedInvalidPackets; - /** RX packets dropped due to RX ring being full */ - uint64_t rxDroppedRxRingFullPackets; - /** Failed RX packets to retrieve item from fill ring */ - uint64_t rxDroppedFillRingPackets; - /** Number of poll() timeouts */ - uint64_t rxPollTimeout; - /** Number of packets sent from the application */ - uint64_t txSentPackets; - /** Packets sent from the app per second. Measured from to the previous time stats were collected */ - uint64_t txSentPacketsPerSec; - /** Number of bytes sent from the application */ - uint64_t txSentBytes; - /** Bytes per second sent from the app. Measured from to the previous time stats were collected */ - uint64_t txSentBytesPerSec; - /** Number of completed sent packets, meaning packets that were confirmed as sent by the kernel */ - uint64_t txCompletedPackets; - /** Completed sent packets per second. Measured from to the previous time stats were collected */ - uint64_t txCompletedPacketsPerSec; - /** TX packets dropped due to invalid descriptor */ - uint64_t txDroppedInvalidPackets; - /** Current RX ring ID */ - uint64_t rxRingId; - /** Current TX ring ID */ - uint64_t txRingId; - /** Current fill ring ID */ - uint64_t fqRingId; - /** Current completion ring ID */ - uint64_t cqRingId; - /** Number of UMEM frames that are currently in-use (allocated) */ - uint64_t umemAllocatedFrames; - /** Number of UMEM frames that are currently free (not allocated) */ - uint64_t umemFreeFrames; - }; - - /** - * A c'tor for this class. Please note that calling this c'tor doesn't initialize the AF_XDP socket. In order to - * set up the socket call open(). - * @param[in] interfaceName The interface name to open the AF_XDP socket on - */ - explicit XdpDevice(std::string interfaceName); - - /** - * A d'tor for this class. It closes the device if it's open. - */ - ~XdpDevice() override; - - /** - * Open the device with default configuration. Call getConfig() after opening the device to get the - * current configuration. - * This method initializes the UMEM, and then creates and configures the AF_XDP socket. If it succeeds the - * socket is ready to receive and send packets. - * @return True if device was opened successfully, false otherwise - */ - bool open() override; - - /** - * Open the device with custom configuration set by the user. - * This method initializes the UMEM, and then creates and configures the AF_XDP socket. If it succeeds the - * socket is ready to receive and send packets. - * @param[in] config The configuration to use for opening the device - * @return True if device was opened successfully, false otherwise - */ - bool open(const XdpDeviceConfiguration& config); - - /** - * Close the device. This method closes the AF_XDP socket and frees the UMEM that was allocated for it. - */ - void close() override; - - /** - * Start receiving packets. In order to use this method the device should be open. Note that this method is - * blocking and will return if: - * - stopReceivePackets() was called from within the user callback - * - timeoutMS passed without receiving any packets - * - Some error occurred (an error log will be printed) - * @param[in] onPacketsArrive A callback to be called when packets are received - * @param[in] onPacketsArriveUserCookie The callback is invoked with this cookie as a parameter. It can be used - * to pass information from the user application to the callback - * @param[in] timeoutMS Timeout in milliseconds to stop if no packets are received. The default value is 5000 ms - * @return True if stopped receiving packets because stopReceivePackets() was called or because timeoutMS passed, - * or false if an error occurred. - */ - bool receivePackets(OnPacketsArrive onPacketsArrive, void* onPacketsArriveUserCookie, int timeoutMS = 5000); - - /** - * Stop receiving packets. Call this method from within the callback passed to receivePackets() whenever you - * want to stop receiving packets. - */ - void stopReceivePackets(); - - /** - * Send a vector of packet pointers. - * @param[in] packets A vector of packet pointers to send - * @param[in] waitForTxCompletion Wait for confirmation from the kernel that packets were sent. If set to true - * this method will wait until the number of packets in the completion ring is equal or greater to the number - * of packets that were sent. The default value is false - * @param[in] waitForTxCompletionTimeoutMS If waitForTxCompletion is set to true, poll the completion ring with - * this timeout. The default value is 5000 ms - * @return True if all packets were sent, or if waitForTxCompletion is true - all sent packets were confirmed. - * Returns false if an error occurred or if poll timed out. - */ - bool sendPackets(const RawPacketVector& packets, bool waitForTxCompletion = false, int waitForTxCompletionTimeoutMS = 5000); - - /** - * Send an array of packets. - * @param[in] packets An array of raw packets to send - * @param[in] packetCount The length of the packet array - * @param[in] waitForTxCompletion Wait for confirmation from the kernel that packets were sent. If set to true - * this method will wait until the number of packets in the completion ring is equal or greater to the number - * of packets sent. The default value is false - * @param[in] waitForTxCompletionTimeoutMS If waitForTxCompletion is set to true, poll the completion ring with - * this timeout. The default value is 5000 ms - * @return True if all packets were sent, or if waitForTxCompletion is true - all sent packets were confirmed. - * Returns false if an error occurred or if poll timed out. - */ - bool sendPackets(RawPacket packets[], size_t packetCount, bool waitForTxCompletion = false, int waitForTxCompletionTimeoutMS = 5000); - - /** - * @return A pointer to the current device configuration. If the device is not open this method returns nullptr - */ - XdpDeviceConfiguration* getConfig() const { return m_Config; } - - /** - * @return Current device statistics - */ - XdpDeviceStats getStatistics(); - - private: - class XdpUmem - { - public: - explicit XdpUmem(uint16_t numFrames, uint16_t frameSize, uint32_t fillRingSize, uint32_t completionRingSize); - - virtual ~XdpUmem(); - - inline uint16_t getFrameSize() const { return m_FrameSize; } - inline uint16_t getFrameCount() const { return m_FrameCount; } - - std::pair> allocateFrames(uint32_t count); - - void freeFrame(uint64_t addr); - - const uint8_t* getDataPtr(uint64_t addr) const; - - void setData(uint64_t addr, const uint8_t* data, size_t dataLen); - - inline size_t getFreeFrameCount() { return m_FreeFrames.size(); } - - inline void* getInfo() { return m_UmemInfo; } - - private: - void* m_UmemInfo; - void* m_Buffer; - uint16_t m_FrameSize; - uint16_t m_FrameCount; - std::vector m_FreeFrames; - }; - - struct XdpPrevDeviceStats - { - timespec timestamp; - uint64_t rxPackets; - uint64_t rxBytes; - uint64_t txSentPackets; - uint64_t txSentBytes; - uint64_t txCompletedPackets; - }; - - std::string m_InterfaceName; - XdpDeviceConfiguration* m_Config; - bool m_ReceivingPackets; - XdpUmem* m_Umem; - void* m_SocketInfo; - XdpDeviceStats m_Stats; - XdpPrevDeviceStats m_PrevStats; - - bool sendPackets(const std::function& getPacketAt, const std::function& getPacketCount, bool waitForTxCompletion = false, int waitForTxCompletionTimeoutMS = 5000); - bool populateFillRing(uint32_t count, uint32_t rxId = 0); - bool populateFillRing(const std::vector& addresses, uint32_t rxId); - uint32_t checkCompletionRing(); - bool configureSocket(); - bool initUmem(); - bool initConfig(); - bool getSocketStats(); - }; -} +class XdpDevice : public IDevice { + public: + /** + * @typedef OnPacketsArrive + * The callback that is called whenever packets are received on the socket + * @param[in] packets An array of the raw packets received + * @param[in] packetCount The number of packets received + * @param[in] device The XdpDevice packets are received from (represents the + * AF_XDP socket) + * @param[in] userCookie A pointer to an object set by the user when + * receivePackets() started + */ + typedef void (*OnPacketsArrive)(RawPacket packets[], uint32_t packetCount, + XdpDevice* device, void* userCookie); + + /** + * @struct XdpDeviceConfiguration + * A struct containing the configuration parameters available for opening an + * XDP device + */ + struct XdpDeviceConfiguration { + /** + * @enum AttachMode + * AF_XDP operation mode + */ + enum AttachMode { + /** A fallback mode that works for any network device. Use it if the + network driver doesn't have support for XDP */ + SkbMode = 1, + /** Use this mode if the network driver has support for XDP */ + DriverMode = 2, + /** Automatically detect whether driver mode is supported, otherwise + fallback to SKB mode */ + AutoMode = 3 + }; + + /** AF_XDP operation mode */ + AttachMode attachMode; + + /** + * UMEM is a region of virtual contiguous memory, divided into equal-sized + *frames. This parameter determines the number of frames that will be + *allocated as pert of the UMEM. + **/ + uint16_t umemNumFrames; + + /** + * UMEM is a region of virtual contiguous memory, divided into equal-sized + *frames. This parameter determines the frame size that will be allocated. + * NOTE: the frame size should be equal to the memory page size (use + *getpagesize() to determine this size) + **/ + uint16_t umemFrameSize; + + /** + * The size of the fill ring used by the AF_XDP socket. This size should be + * a power of two and less or equal to the total number of UMEM frames + */ + uint32_t fillRingSize; + + /** + * The size of the completion ring used by the AF_XDP socket. This size + * should be a power of two and less or equal to the total number of UMEM + * frames + */ + uint32_t completionRingSize; + + /** + * The size of the RX ring used by the AF_XDP socket. This size should be a + * power of two and less or equal to the total number of UMEM frames + */ + uint32_t rxSize; + + /** + * The size of the TX ring used by the AF_XDP socket. This size should be a + * power of two and less or equal to the total number of UMEM frames + */ + uint32_t txSize; + + /** + * The max number of packets to be received or sent in one batch + */ + uint16_t rxTxBatchSize; + + /** + * A c'tor for this struct. Each parameter has a default value described + * below. + * @param[in] attachMode AF_XDP operation mode. The default value is auto + * mode + * @param[in] umemNumFrames Number of UMEM frames to allocate. The default + * value is 4096 + * @param[in] umemFrameSize The size of each UMEM frame. The default value + * is equal to getpagesize() + * @param[in] fillRingSize The size of the fill ring used by the AF_XDP + * socket. The default value is 4096 + * @param[in] completionRingSize The size of the completion ring used by the + * AF_XDP socket. The default value is 2048 + * @param[in] rxSize The size of the RX ring used by the AF_XDP socket. The + * default value is 2048 + * @param[in] txSize The size of the TX ring used by the AF_XDP socket. The + * default value is 2048 + * @param[in] rxTxBatchSize The max number of packets to be received or sent + * in one batch. The default value is 64 + */ + explicit XdpDeviceConfiguration(AttachMode attachMode = AutoMode, + uint16_t umemNumFrames = 0, + uint16_t umemFrameSize = 0, + uint32_t fillRingSize = 0, + uint32_t completionRingSize = 0, + uint32_t rxSize = 0, uint32_t txSize = 0, + uint16_t rxTxBatchSize = 0) { + this->attachMode = attachMode; + this->umemNumFrames = umemNumFrames; + this->umemFrameSize = umemFrameSize; + this->fillRingSize = fillRingSize; + this->completionRingSize = completionRingSize; + this->rxSize = rxSize; + this->txSize = txSize; + this->rxTxBatchSize = rxTxBatchSize; + } + }; + + /** + * @struct XdpDeviceStats + * A container for XDP device statistics + */ + struct XdpDeviceStats { + /** The timestamp when the stats were collected */ + timespec timestamp; + /** Number of packets received */ + uint64_t rxPackets; + /** Packets received per second. Measured from to the previous time stats + * were collected */ + uint64_t rxPacketsPerSec; + /** Number of bytes received */ + uint64_t rxBytes; + /** Bytes per second received. Measured from to the previous time stats were + * collected */ + uint64_t rxBytesPerSec; + /** Total number of dropped RX packets */ + uint64_t rxDroppedTotalPackets; + /** RX packets dropped due to invalid descriptor */ + uint64_t rxDroppedInvalidPackets; + /** RX packets dropped due to RX ring being full */ + uint64_t rxDroppedRxRingFullPackets; + /** Failed RX packets to retrieve item from fill ring */ + uint64_t rxDroppedFillRingPackets; + /** Number of poll() timeouts */ + uint64_t rxPollTimeout; + /** Number of packets sent from the application */ + uint64_t txSentPackets; + /** Packets sent from the app per second. Measured from to the previous time + * stats were collected */ + uint64_t txSentPacketsPerSec; + /** Number of bytes sent from the application */ + uint64_t txSentBytes; + /** Bytes per second sent from the app. Measured from to the previous time + * stats were collected */ + uint64_t txSentBytesPerSec; + /** Number of completed sent packets, meaning packets that were confirmed as + * sent by the kernel */ + uint64_t txCompletedPackets; + /** Completed sent packets per second. Measured from to the previous time + * stats were collected */ + uint64_t txCompletedPacketsPerSec; + /** TX packets dropped due to invalid descriptor */ + uint64_t txDroppedInvalidPackets; + /** Current RX ring ID */ + uint64_t rxRingId; + /** Current TX ring ID */ + uint64_t txRingId; + /** Current fill ring ID */ + uint64_t fqRingId; + /** Current completion ring ID */ + uint64_t cqRingId; + /** Number of UMEM frames that are currently in-use (allocated) */ + uint64_t umemAllocatedFrames; + /** Number of UMEM frames that are currently free (not allocated) */ + uint64_t umemFreeFrames; + }; + + /** + * A c'tor for this class. Please note that calling this c'tor doesn't + * initialize the AF_XDP socket. In order to set up the socket call open(). + * @param[in] interfaceName The interface name to open the AF_XDP socket on + */ + explicit XdpDevice(std::string interfaceName); + + /** + * A d'tor for this class. It closes the device if it's open. + */ + ~XdpDevice() override; + + /** + * Open the device with default configuration. Call getConfig() after opening + * the device to get the current configuration. This method initializes the + * UMEM, and then creates and configures the AF_XDP socket. If it succeeds the + * socket is ready to receive and send packets. + * @return True if device was opened successfully, false otherwise + */ + bool open() override; + + /** + * Open the device with custom configuration set by the user. + * This method initializes the UMEM, and then creates and configures the + * AF_XDP socket. If it succeeds the socket is ready to receive and send + * packets. + * @param[in] config The configuration to use for opening the device + * @return True if device was opened successfully, false otherwise + */ + bool open(const XdpDeviceConfiguration& config); + + /** + * Close the device. This method closes the AF_XDP socket and frees the UMEM + * that was allocated for it. + */ + void close() override; + + /** + * Start receiving packets. In order to use this method the device should be + * open. Note that this method is blocking and will return if: + * - stopReceivePackets() was called from within the user callback + * - timeoutMS passed without receiving any packets + * - Some error occurred (an error log will be printed) + * @param[in] onPacketsArrive A callback to be called when packets are + * received + * @param[in] onPacketsArriveUserCookie The callback is invoked with this + * cookie as a parameter. It can be used to pass information from the user + * application to the callback + * @param[in] timeoutMS Timeout in milliseconds to stop if no packets are + * received. The default value is 5000 ms + * @return True if stopped receiving packets because stopReceivePackets() was + * called or because timeoutMS passed, or false if an error occurred. + */ + bool receivePackets(OnPacketsArrive onPacketsArrive, + void* onPacketsArriveUserCookie, int timeoutMS = 5000); + + /** + * Stop receiving packets. Call this method from within the callback passed to + * receivePackets() whenever you want to stop receiving packets. + */ + void stopReceivePackets(); + + /** + * Send a vector of packet pointers. + * @param[in] packets A vector of packet pointers to send + * @param[in] waitForTxCompletion Wait for confirmation from the kernel that + * packets were sent. If set to true this method will wait until the number of + * packets in the completion ring is equal or greater to the number of packets + * that were sent. The default value is false + * @param[in] waitForTxCompletionTimeoutMS If waitForTxCompletion is set to + * true, poll the completion ring with this timeout. The default value is 5000 + * ms + * @return True if all packets were sent, or if waitForTxCompletion is true - + * all sent packets were confirmed. Returns false if an error occurred or if + * poll timed out. + */ + bool sendPackets(const RawPacketVector& packets, + bool waitForTxCompletion = false, + int waitForTxCompletionTimeoutMS = 5000); + + /** + * Send an array of packets. + * @param[in] packets An array of raw packets to send + * @param[in] packetCount The length of the packet array + * @param[in] waitForTxCompletion Wait for confirmation from the kernel that + * packets were sent. If set to true this method will wait until the number of + * packets in the completion ring is equal or greater to the number of packets + * sent. The default value is false + * @param[in] waitForTxCompletionTimeoutMS If waitForTxCompletion is set to + * true, poll the completion ring with this timeout. The default value is 5000 + * ms + * @return True if all packets were sent, or if waitForTxCompletion is true - + * all sent packets were confirmed. Returns false if an error occurred or if + * poll timed out. + */ + bool sendPackets(RawPacket packets[], size_t packetCount, + bool waitForTxCompletion = false, + int waitForTxCompletionTimeoutMS = 5000); + + /** + * @return A pointer to the current device configuration. If the device is not + * open this method returns nullptr + */ + XdpDeviceConfiguration* getConfig() const { return m_Config; } + + /** + * @return Current device statistics + */ + XdpDeviceStats getStatistics(); + + private: + class XdpUmem { + public: + explicit XdpUmem(uint16_t numFrames, uint16_t frameSize, + uint32_t fillRingSize, uint32_t completionRingSize); + + virtual ~XdpUmem(); + + inline uint16_t getFrameSize() const { return m_FrameSize; } + inline uint16_t getFrameCount() const { return m_FrameCount; } + + std::pair> allocateFrames(uint32_t count); + + void freeFrame(uint64_t addr); + + const uint8_t* getDataPtr(uint64_t addr) const; + + void setData(uint64_t addr, const uint8_t* data, size_t dataLen); + + inline size_t getFreeFrameCount() { return m_FreeFrames.size(); } + + inline void* getInfo() { return m_UmemInfo; } + + private: + void* m_UmemInfo; + void* m_Buffer; + uint16_t m_FrameSize; + uint16_t m_FrameCount; + std::vector m_FreeFrames; + }; + + struct XdpPrevDeviceStats { + timespec timestamp; + uint64_t rxPackets; + uint64_t rxBytes; + uint64_t txSentPackets; + uint64_t txSentBytes; + uint64_t txCompletedPackets; + }; + + std::string m_InterfaceName; + XdpDeviceConfiguration* m_Config; + bool m_ReceivingPackets; + XdpUmem* m_Umem; + void* m_SocketInfo; + XdpDeviceStats m_Stats; + XdpPrevDeviceStats m_PrevStats; + + bool sendPackets(const std::function& getPacketAt, + const std::function& getPacketCount, + bool waitForTxCompletion = false, + int waitForTxCompletionTimeoutMS = 5000); + bool populateFillRing(uint32_t count, uint32_t rxId = 0); + bool populateFillRing(const std::vector& addresses, uint32_t rxId); + uint32_t checkCompletionRing(); + bool configureSocket(); + bool initUmem(); + bool initConfig(); + bool getSocketStats(); +}; +} // namespace pcpp #endif // PCAPPP_XDP_DEVICE diff --git a/Pcap++/src/DpdkDevice.cpp b/Pcap++/src/DpdkDevice.cpp index c732abbe0b..5961a6519d 100644 --- a/Pcap++/src/DpdkDevice.cpp +++ b/Pcap++/src/DpdkDevice.cpp @@ -14,14 +14,14 @@ #if (RTE_VER_YEAR > 17) || (RTE_VER_YEAR == 17 && RTE_VER_MONTH >= 11) #include "rte_bus_pci.h" #endif -#include "rte_pci.h" #include "rte_config.h" -#include "rte_ethdev.h" +#include "rte_cycles.h" #include "rte_errno.h" +#include "rte_ethdev.h" #include "rte_malloc.h" -#include "rte_cycles.h" -#include +#include "rte_pci.h" #include +#include #include #define MAX_BURST_SIZE 64 @@ -34,8 +34,7 @@ #define GET_MASTER_CORE rte_get_main_lcore #endif -namespace pcpp -{ +namespace pcpp { /** * ================ @@ -43,1461 +42,1487 @@ namespace pcpp * ================ */ -#define DPDK_COFIG_HEADER_SPLIT 0 /**< Header Split disabled */ -#define DPDK_COFIG_SPLIT_HEADER_SIZE 0 -#define DPDK_COFIG_HW_IP_CHECKSUM 0 /**< IP checksum offload disabled */ -#define DPDK_COFIG_HW_VLAN_FILTER 0 /**< VLAN filtering disabled */ -#define DPDK_COFIG_JUMBO_FRAME 0 /**< Jumbo Frame Support disabled */ -#define DPDK_COFIG_HW_STRIP_CRC 0 /**< CRC stripped by hardware disabled */ +#define DPDK_COFIG_HEADER_SPLIT 0 /**< Header Split disabled */ +#define DPDK_COFIG_SPLIT_HEADER_SIZE 0 +#define DPDK_COFIG_HW_IP_CHECKSUM 0 /**< IP checksum offload disabled */ +#define DPDK_COFIG_HW_VLAN_FILTER 0 /**< VLAN filtering disabled */ +#define DPDK_COFIG_JUMBO_FRAME 0 /**< Jumbo Frame Support disabled */ +#define DPDK_COFIG_HW_STRIP_CRC 0 /**< CRC stripped by hardware disabled */ #if (RTE_VER_YEAR < 21) || (RTE_VER_YEAR == 21 && RTE_VER_MONTH < 11) -#define DPDK_CONFIG_ETH_LINK_FULL_DUPLEX ETH_LINK_FULL_DUPLEX -#define DPDK_CONFIG_MQ_RSS ETH_RSS -#define DPDK_CONFIG_MQ_NO_RSS ETH_MQ_RX_NONE +#define DPDK_CONFIG_ETH_LINK_FULL_DUPLEX ETH_LINK_FULL_DUPLEX +#define DPDK_CONFIG_MQ_RSS ETH_RSS +#define DPDK_CONFIG_MQ_NO_RSS ETH_MQ_RX_NONE #else -#define DPDK_CONFIG_ETH_LINK_FULL_DUPLEX RTE_ETH_LINK_FULL_DUPLEX -#define DPDK_CONFIG_MQ_RSS RTE_ETH_MQ_RX_RSS -#define DPDK_CONFIG_MQ_NO_RSS RTE_ETH_MQ_RX_NONE +#define DPDK_CONFIG_ETH_LINK_FULL_DUPLEX RTE_ETH_LINK_FULL_DUPLEX +#define DPDK_CONFIG_MQ_RSS RTE_ETH_MQ_RX_RSS +#define DPDK_CONFIG_MQ_NO_RSS RTE_ETH_MQ_RX_NONE #endif #if (RTE_VER_YEAR < 22) || (RTE_VER_YEAR == 22 && RTE_VER_MONTH < 11) -#define DPDK_CONFIG_ETH_RSS_IPV4 ETH_RSS_IPV4 -#define DPDK_CONFIG_ETH_RSS_FRAG_IPV4 ETH_RSS_FRAG_IPV4 -#define DPDK_CONFIG_ETH_RSS_NONFRAG_IPV4_TCP ETH_RSS_NONFRAG_IPV4_TCP -#define DPDK_CONFIG_ETH_RSS_NONFRAG_IPV4_UDP ETH_RSS_NONFRAG_IPV4_UDP -#define DPDK_CONFIG_ETH_RSS_NONFRAG_IPV4_SCTP ETH_RSS_NONFRAG_IPV4_SCTP -#define DPDK_CONFIG_ETH_RSS_NONFRAG_IPV4_OTHER ETH_RSS_NONFRAG_IPV4_OTHER -#define DPDK_CONFIG_ETH_RSS_IPV6 ETH_RSS_IPV6 -#define DPDK_CONFIG_ETH_RSS_FRAG_IPV6 ETH_RSS_FRAG_IPV6 -#define DPDK_CONFIG_ETH_RSS_NONFRAG_IPV6_TCP ETH_RSS_NONFRAG_IPV6_TCP -#define DPDK_CONFIG_ETH_RSS_NONFRAG_IPV6_UDP ETH_RSS_NONFRAG_IPV6_UDP -#define DPDK_CONFIG_ETH_RSS_NONFRAG_IPV6_SCTP ETH_RSS_NONFRAG_IPV6_SCTP -#define DPDK_CONFIG_ETH_RSS_NONFRAG_IPV6_OTHER ETH_RSS_NONFRAG_IPV6_OTHER -#define DPDK_CONFIG_ETH_RSS_L2_PAYLOAD ETH_RSS_L2_PAYLOAD -#define DPDK_CONFIG_ETH_RSS_IPV6_EX ETH_RSS_IPV6_EX -#define DPDK_CONFIG_ETH_RSS_IPV6_TCP_EX ETH_RSS_IPV6_TCP_EX -#define DPDK_CONFIG_ETH_RSS_IPV6_UDP_EX ETH_RSS_IPV6_UDP_EX -#define DPDK_CONFIG_ETH_RSS_PORT ETH_RSS_PORT -#define DPDK_CONFIG_ETH_RSS_VXLAN ETH_RSS_VXLAN -#define DPDK_CONFIG_ETH_RSS_GENEVE ETH_RSS_GENEVE -#define DPDK_CONFIG_ETH_RSS_NVGRE ETH_RSS_NVGRE +#define DPDK_CONFIG_ETH_RSS_IPV4 ETH_RSS_IPV4 +#define DPDK_CONFIG_ETH_RSS_FRAG_IPV4 ETH_RSS_FRAG_IPV4 +#define DPDK_CONFIG_ETH_RSS_NONFRAG_IPV4_TCP ETH_RSS_NONFRAG_IPV4_TCP +#define DPDK_CONFIG_ETH_RSS_NONFRAG_IPV4_UDP ETH_RSS_NONFRAG_IPV4_UDP +#define DPDK_CONFIG_ETH_RSS_NONFRAG_IPV4_SCTP ETH_RSS_NONFRAG_IPV4_SCTP +#define DPDK_CONFIG_ETH_RSS_NONFRAG_IPV4_OTHER ETH_RSS_NONFRAG_IPV4_OTHER +#define DPDK_CONFIG_ETH_RSS_IPV6 ETH_RSS_IPV6 +#define DPDK_CONFIG_ETH_RSS_FRAG_IPV6 ETH_RSS_FRAG_IPV6 +#define DPDK_CONFIG_ETH_RSS_NONFRAG_IPV6_TCP ETH_RSS_NONFRAG_IPV6_TCP +#define DPDK_CONFIG_ETH_RSS_NONFRAG_IPV6_UDP ETH_RSS_NONFRAG_IPV6_UDP +#define DPDK_CONFIG_ETH_RSS_NONFRAG_IPV6_SCTP ETH_RSS_NONFRAG_IPV6_SCTP +#define DPDK_CONFIG_ETH_RSS_NONFRAG_IPV6_OTHER ETH_RSS_NONFRAG_IPV6_OTHER +#define DPDK_CONFIG_ETH_RSS_L2_PAYLOAD ETH_RSS_L2_PAYLOAD +#define DPDK_CONFIG_ETH_RSS_IPV6_EX ETH_RSS_IPV6_EX +#define DPDK_CONFIG_ETH_RSS_IPV6_TCP_EX ETH_RSS_IPV6_TCP_EX +#define DPDK_CONFIG_ETH_RSS_IPV6_UDP_EX ETH_RSS_IPV6_UDP_EX +#define DPDK_CONFIG_ETH_RSS_PORT ETH_RSS_PORT +#define DPDK_CONFIG_ETH_RSS_VXLAN ETH_RSS_VXLAN +#define DPDK_CONFIG_ETH_RSS_GENEVE ETH_RSS_GENEVE +#define DPDK_CONFIG_ETH_RSS_NVGRE ETH_RSS_NVGRE #else -#define DPDK_CONFIG_ETH_RSS_IPV4 RTE_ETH_RSS_IPV4 -#define DPDK_CONFIG_ETH_RSS_FRAG_IPV4 RTE_ETH_RSS_FRAG_IPV4 -#define DPDK_CONFIG_ETH_RSS_NONFRAG_IPV4_TCP RTE_ETH_RSS_NONFRAG_IPV4_TCP -#define DPDK_CONFIG_ETH_RSS_NONFRAG_IPV4_UDP RTE_ETH_RSS_NONFRAG_IPV4_UDP -#define DPDK_CONFIG_ETH_RSS_NONFRAG_IPV4_SCTP RTE_ETH_RSS_NONFRAG_IPV4_SCTP -#define DPDK_CONFIG_ETH_RSS_NONFRAG_IPV4_OTHER RTE_ETH_RSS_NONFRAG_IPV4_OTHER -#define DPDK_CONFIG_ETH_RSS_IPV6 RTE_ETH_RSS_IPV6 -#define DPDK_CONFIG_ETH_RSS_FRAG_IPV6 RTE_ETH_RSS_FRAG_IPV6 -#define DPDK_CONFIG_ETH_RSS_NONFRAG_IPV6_TCP RTE_ETH_RSS_NONFRAG_IPV6_TCP -#define DPDK_CONFIG_ETH_RSS_NONFRAG_IPV6_UDP RTE_ETH_RSS_NONFRAG_IPV6_UDP -#define DPDK_CONFIG_ETH_RSS_NONFRAG_IPV6_SCTP RTE_ETH_RSS_NONFRAG_IPV6_SCTP -#define DPDK_CONFIG_ETH_RSS_NONFRAG_IPV6_OTHER RTE_ETH_RSS_NONFRAG_IPV6_OTHER -#define DPDK_CONFIG_ETH_RSS_L2_PAYLOAD RTE_ETH_RSS_L2_PAYLOAD -#define DPDK_CONFIG_ETH_RSS_IPV6_EX RTE_ETH_RSS_IPV6_EX -#define DPDK_CONFIG_ETH_RSS_IPV6_TCP_EX RTE_ETH_RSS_IPV6_TCP_EX -#define DPDK_CONFIG_ETH_RSS_IPV6_UDP_EX RTE_ETH_RSS_IPV6_UDP_EX -#define DPDK_CONFIG_ETH_RSS_PORT RTE_ETH_RSS_PORT -#define DPDK_CONFIG_ETH_RSS_VXLAN RTE_ETH_RSS_VXLAN -#define DPDK_CONFIG_ETH_RSS_GENEVE RTE_ETH_RSS_GENEVE -#define DPDK_CONFIG_ETH_RSS_NVGRE RTE_ETH_RSS_NVGRE +#define DPDK_CONFIG_ETH_RSS_IPV4 RTE_ETH_RSS_IPV4 +#define DPDK_CONFIG_ETH_RSS_FRAG_IPV4 RTE_ETH_RSS_FRAG_IPV4 +#define DPDK_CONFIG_ETH_RSS_NONFRAG_IPV4_TCP RTE_ETH_RSS_NONFRAG_IPV4_TCP +#define DPDK_CONFIG_ETH_RSS_NONFRAG_IPV4_UDP RTE_ETH_RSS_NONFRAG_IPV4_UDP +#define DPDK_CONFIG_ETH_RSS_NONFRAG_IPV4_SCTP RTE_ETH_RSS_NONFRAG_IPV4_SCTP +#define DPDK_CONFIG_ETH_RSS_NONFRAG_IPV4_OTHER RTE_ETH_RSS_NONFRAG_IPV4_OTHER +#define DPDK_CONFIG_ETH_RSS_IPV6 RTE_ETH_RSS_IPV6 +#define DPDK_CONFIG_ETH_RSS_FRAG_IPV6 RTE_ETH_RSS_FRAG_IPV6 +#define DPDK_CONFIG_ETH_RSS_NONFRAG_IPV6_TCP RTE_ETH_RSS_NONFRAG_IPV6_TCP +#define DPDK_CONFIG_ETH_RSS_NONFRAG_IPV6_UDP RTE_ETH_RSS_NONFRAG_IPV6_UDP +#define DPDK_CONFIG_ETH_RSS_NONFRAG_IPV6_SCTP RTE_ETH_RSS_NONFRAG_IPV6_SCTP +#define DPDK_CONFIG_ETH_RSS_NONFRAG_IPV6_OTHER RTE_ETH_RSS_NONFRAG_IPV6_OTHER +#define DPDK_CONFIG_ETH_RSS_L2_PAYLOAD RTE_ETH_RSS_L2_PAYLOAD +#define DPDK_CONFIG_ETH_RSS_IPV6_EX RTE_ETH_RSS_IPV6_EX +#define DPDK_CONFIG_ETH_RSS_IPV6_TCP_EX RTE_ETH_RSS_IPV6_TCP_EX +#define DPDK_CONFIG_ETH_RSS_IPV6_UDP_EX RTE_ETH_RSS_IPV6_UDP_EX +#define DPDK_CONFIG_ETH_RSS_PORT RTE_ETH_RSS_PORT +#define DPDK_CONFIG_ETH_RSS_VXLAN RTE_ETH_RSS_VXLAN +#define DPDK_CONFIG_ETH_RSS_GENEVE RTE_ETH_RSS_GENEVE +#define DPDK_CONFIG_ETH_RSS_NVGRE RTE_ETH_RSS_NVGRE #endif - -//RSS random key: +// RSS random key: uint8_t DpdkDevice::m_RSSKey[40] = { - 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, - 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, - 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, - 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, - 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, - }; + 0x6D, + 0x5A, + 0x6D, + 0x5A, + 0x6D, + 0x5A, + 0x6D, + 0x5A, + 0x6D, + 0x5A, + 0x6D, + 0x5A, + 0x6D, + 0x5A, + 0x6D, + 0x5A, + 0x6D, + 0x5A, + 0x6D, + 0x5A, + 0x6D, + 0x5A, + 0x6D, + 0x5A, + 0x6D, + 0x5A, + 0x6D, + 0x5A, + 0x6D, + 0x5A, + 0x6D, + 0x5A, + 0x6D, + 0x5A, + 0x6D, + 0x5A, + 0x6D, + 0x5A, + 0x6D, + 0x5A, +}; DpdkDevice::DpdkDevice(int port, uint32_t mBufPoolSize) - : m_Id(port), m_MacAddress(MacAddress::Zero) -{ - std::ostringstream deviceNameStream; - deviceNameStream << "DPDK_" << m_Id; - m_DeviceName = deviceNameStream.str(); + : m_Id(port), m_MacAddress(MacAddress::Zero) { + std::ostringstream deviceNameStream; + deviceNameStream << "DPDK_" << m_Id; + m_DeviceName = deviceNameStream.str(); #if (RTE_VER_YEAR > 19) || (RTE_VER_YEAR == 19 && RTE_VER_MONTH >= 8) - struct rte_ether_addr etherAddr; + struct rte_ether_addr etherAddr; #else - struct ether_addr etherAddr; + struct ether_addr etherAddr; #endif - rte_eth_macaddr_get((uint8_t) m_Id, ðerAddr); - m_MacAddress = MacAddress(etherAddr.addr_bytes[0], etherAddr.addr_bytes[1], - etherAddr.addr_bytes[2], etherAddr.addr_bytes[3], - etherAddr.addr_bytes[4], etherAddr.addr_bytes[5]); + rte_eth_macaddr_get((uint8_t)m_Id, ðerAddr); + m_MacAddress = MacAddress(etherAddr.addr_bytes[0], etherAddr.addr_bytes[1], + etherAddr.addr_bytes[2], etherAddr.addr_bytes[3], + etherAddr.addr_bytes[4], etherAddr.addr_bytes[5]); - rte_eth_dev_get_mtu((uint8_t) m_Id, &m_DeviceMtu); + rte_eth_dev_get_mtu((uint8_t)m_Id, &m_DeviceMtu); - char mBufMemPoolName[32]; - sprintf(mBufMemPoolName, "MBufMemPool%d", m_Id); - if (!initMemPool(m_MBufMempool, mBufMemPoolName, mBufPoolSize)) - { - PCPP_LOG_ERROR("Could not initialize mBuf mempool. Device not initialized"); - return; - } + char mBufMemPoolName[32]; + sprintf(mBufMemPoolName, "MBufMemPool%d", m_Id); + if (!initMemPool(m_MBufMempool, mBufMemPoolName, mBufPoolSize)) { + PCPP_LOG_ERROR("Could not initialize mBuf mempool. Device not initialized"); + return; + } - m_NumOfRxQueuesOpened = 0; - m_NumOfTxQueuesOpened = 0; + m_NumOfRxQueuesOpened = 0; + m_NumOfTxQueuesOpened = 0; - setDeviceInfo(); + setDeviceInfo(); - memset(&m_PrevStats, 0 ,sizeof(m_PrevStats)); + memset(&m_PrevStats, 0, sizeof(m_PrevStats)); - m_TxBuffers = NULL; - m_TxBufferLastDrainTsc = NULL; + m_TxBuffers = NULL; + m_TxBufferLastDrainTsc = NULL; - m_DeviceOpened = false; - m_WasOpened = false; - m_StopThread = true; + m_DeviceOpened = false; + m_WasOpened = false; + m_StopThread = true; } -DpdkDevice::~DpdkDevice() -{ - if (m_TxBuffers != NULL) - delete [] m_TxBuffers; +DpdkDevice::~DpdkDevice() { + if (m_TxBuffers != NULL) + delete[] m_TxBuffers; - if (m_TxBufferLastDrainTsc != NULL) - delete [] m_TxBufferLastDrainTsc; + if (m_TxBufferLastDrainTsc != NULL) + delete[] m_TxBufferLastDrainTsc; } -uint32_t DpdkDevice::getCurrentCoreId() const -{ - return rte_lcore_id(); -} +uint32_t DpdkDevice::getCurrentCoreId() const { return rte_lcore_id(); } -bool DpdkDevice::setMtu(uint16_t newMtu) -{ - int res = rte_eth_dev_set_mtu(m_Id, newMtu); - if (res != 0) - { - PCPP_LOG_ERROR("Couldn't set device MTU. DPDK error: " << res); - return false; - } - - PCPP_LOG_DEBUG("Managed to set MTU from " << m_DeviceMtu << " to " << newMtu); - m_DeviceMtu = newMtu; - return true; -} +bool DpdkDevice::setMtu(uint16_t newMtu) { + int res = rte_eth_dev_set_mtu(m_Id, newMtu); + if (res != 0) { + PCPP_LOG_ERROR("Couldn't set device MTU. DPDK error: " << res); + return false; + } -bool DpdkDevice::openMultiQueues(uint16_t numOfRxQueuesToOpen, uint16_t numOfTxQueuesToOpen, const DpdkDeviceConfiguration& config) -{ - if (m_DeviceOpened) - { - PCPP_LOG_ERROR("Device already opened"); - return false; - } - - // There is a VMXNET3 limitation that when opening a device with a certain number of RX+TX queues - // it's impossible to close it and open it again with a larger number of RX+TX queues. So for this - // PMD I made a patch to open the device in the first time with maximum RX & TX queue, close it - // immediately and open it again with number of queues the user wanted to - if (!m_WasOpened && m_PMDType == PMD_VMXNET3) - { - m_WasOpened = true; - openMultiQueues(getTotalNumOfRxQueues(), getTotalNumOfTxQueues(), config); - close(); - } - - m_Config = config; - - if (!configurePort(numOfRxQueuesToOpen, numOfTxQueuesToOpen)) - { - m_DeviceOpened = false; - return false; - } - - clearCoreConfiguration(); - - if (!initQueues(numOfRxQueuesToOpen, numOfTxQueuesToOpen)) - return false; - - if (!startDevice()) - { - PCPP_LOG_ERROR("failed to start device " << m_Id); - m_DeviceOpened = false; - return false; - } - - m_NumOfRxQueuesOpened = numOfRxQueuesToOpen; - m_NumOfTxQueuesOpened = numOfTxQueuesToOpen; - - rte_eth_stats_reset(m_Id); - - m_DeviceOpened = true; - return m_DeviceOpened; + PCPP_LOG_DEBUG("Managed to set MTU from " << m_DeviceMtu << " to " << newMtu); + m_DeviceMtu = newMtu; + return true; } - -void DpdkDevice::close() -{ - if (!m_DeviceOpened) - { - PCPP_LOG_DEBUG("Trying to close device [" << m_DeviceName << "] but device is already closed"); - return; - } - stopCapture(); - clearCoreConfiguration(); - m_NumOfRxQueuesOpened = 0; - m_NumOfTxQueuesOpened = 0; - rte_eth_dev_stop(m_Id); - PCPP_LOG_DEBUG("Called rte_eth_dev_stop for device [" << m_DeviceName << "]"); - - if (m_TxBuffers != NULL) - { - delete [] m_TxBuffers; - m_TxBuffers = NULL; - } - - if (m_TxBufferLastDrainTsc != NULL) - { - delete [] m_TxBufferLastDrainTsc; - m_TxBufferLastDrainTsc = NULL; - } - - m_DeviceOpened = false; +bool DpdkDevice::openMultiQueues(uint16_t numOfRxQueuesToOpen, + uint16_t numOfTxQueuesToOpen, + const DpdkDeviceConfiguration& config) { + if (m_DeviceOpened) { + PCPP_LOG_ERROR("Device already opened"); + return false; + } + + // There is a VMXNET3 limitation that when opening a device with a certain + // number of RX+TX queues it's impossible to close it and open it again with a + // larger number of RX+TX queues. So for this PMD I made a patch to open the + // device in the first time with maximum RX & TX queue, close it immediately + // and open it again with number of queues the user wanted to + if (!m_WasOpened && m_PMDType == PMD_VMXNET3) { + m_WasOpened = true; + openMultiQueues(getTotalNumOfRxQueues(), getTotalNumOfTxQueues(), config); + close(); + } + + m_Config = config; + + if (!configurePort(numOfRxQueuesToOpen, numOfTxQueuesToOpen)) { + m_DeviceOpened = false; + return false; + } + + clearCoreConfiguration(); + + if (!initQueues(numOfRxQueuesToOpen, numOfTxQueuesToOpen)) + return false; + + if (!startDevice()) { + PCPP_LOG_ERROR("failed to start device " << m_Id); + m_DeviceOpened = false; + return false; + } + + m_NumOfRxQueuesOpened = numOfRxQueuesToOpen; + m_NumOfTxQueuesOpened = numOfTxQueuesToOpen; + + rte_eth_stats_reset(m_Id); + + m_DeviceOpened = true; + return m_DeviceOpened; } +void DpdkDevice::close() { + if (!m_DeviceOpened) { + PCPP_LOG_DEBUG("Trying to close device [" + << m_DeviceName << "] but device is already closed"); + return; + } + stopCapture(); + clearCoreConfiguration(); + m_NumOfRxQueuesOpened = 0; + m_NumOfTxQueuesOpened = 0; + rte_eth_dev_stop(m_Id); + PCPP_LOG_DEBUG("Called rte_eth_dev_stop for device [" << m_DeviceName << "]"); + + if (m_TxBuffers != NULL) { + delete[] m_TxBuffers; + m_TxBuffers = NULL; + } + + if (m_TxBufferLastDrainTsc != NULL) { + delete[] m_TxBufferLastDrainTsc; + m_TxBufferLastDrainTsc = NULL; + } + + m_DeviceOpened = false; +} -bool DpdkDevice::configurePort(uint8_t numOfRxQueues, uint8_t numOfTxQueues) -{ - if (numOfRxQueues > getTotalNumOfRxQueues()) - { - PCPP_LOG_ERROR("Could not open more than " << getTotalNumOfRxQueues() << " RX queues"); - return false; - } - - if (numOfTxQueues > getTotalNumOfTxQueues()) - { - PCPP_LOG_ERROR("Could not open more than " << getTotalNumOfTxQueues() << " TX queues"); - return false; - } - - // if PMD doesn't support RSS, set RSS HF to 0 - if (getSupportedRssHashFunctions() == 0 && getConfiguredRssHashFunction() != 0) - { - PCPP_LOG_DEBUG("PMD '" << m_PMDName << "' doesn't support RSS, setting RSS hash functions to 0"); - m_Config.rssHashFunction = RSS_NONE; - } - - if (!isDeviceSupportRssHashFunction(getConfiguredRssHashFunction())) - { - PCPP_LOG_ERROR("PMD '" << m_PMDName << "' doesn't support the request RSS hash functions 0x" << std::hex << getConfiguredRssHashFunction()); - return false; - } - - // verify num of RX queues is power of 2 - bool isRxQueuePowerOfTwo = !(numOfRxQueues == 0) && !(numOfRxQueues & (numOfRxQueues - 1)); - if (!isRxQueuePowerOfTwo) - { - PCPP_LOG_ERROR("Num of RX queues must be power of 2 (because of DPDK limitation). Attempted to open device with " << numOfRxQueues << " RX queues"); - return false; - } - - struct rte_eth_conf portConf; - memset(&portConf,0,sizeof(rte_eth_conf)); +bool DpdkDevice::configurePort(uint8_t numOfRxQueues, uint8_t numOfTxQueues) { + if (numOfRxQueues > getTotalNumOfRxQueues()) { + PCPP_LOG_ERROR("Could not open more than " << getTotalNumOfRxQueues() + << " RX queues"); + return false; + } + + if (numOfTxQueues > getTotalNumOfTxQueues()) { + PCPP_LOG_ERROR("Could not open more than " << getTotalNumOfTxQueues() + << " TX queues"); + return false; + } + + // if PMD doesn't support RSS, set RSS HF to 0 + if (getSupportedRssHashFunctions() == 0 && + getConfiguredRssHashFunction() != 0) { + PCPP_LOG_DEBUG( + "PMD '" << m_PMDName + << "' doesn't support RSS, setting RSS hash functions to 0"); + m_Config.rssHashFunction = RSS_NONE; + } + + if (!isDeviceSupportRssHashFunction(getConfiguredRssHashFunction())) { + PCPP_LOG_ERROR( + "PMD '" << m_PMDName + << "' doesn't support the request RSS hash functions 0x" + << std::hex << getConfiguredRssHashFunction()); + return false; + } + + // verify num of RX queues is power of 2 + bool isRxQueuePowerOfTwo = + !(numOfRxQueues == 0) && !(numOfRxQueues & (numOfRxQueues - 1)); + if (!isRxQueuePowerOfTwo) { + PCPP_LOG_ERROR("Num of RX queues must be power of 2 (because of DPDK " + "limitation). Attempted to open device with " + << numOfRxQueues << " RX queues"); + return false; + } + + struct rte_eth_conf portConf; + memset(&portConf, 0, sizeof(rte_eth_conf)); #if (RTE_VER_YEAR < 22) || (RTE_VER_YEAR == 22 && RTE_VER_MONTH < 11) - portConf.rxmode.split_hdr_size = DPDK_COFIG_SPLIT_HEADER_SIZE; + portConf.rxmode.split_hdr_size = DPDK_COFIG_SPLIT_HEADER_SIZE; #endif #if (RTE_VER_YEAR < 18) || (RTE_VER_YEAR == 18 && RTE_VER_MONTH < 8) - portConf.rxmode.header_split = DPDK_COFIG_HEADER_SPLIT; - portConf.rxmode.hw_ip_checksum = DPDK_COFIG_HW_IP_CHECKSUM; - portConf.rxmode.hw_vlan_filter = DPDK_COFIG_HW_VLAN_FILTER; - portConf.rxmode.jumbo_frame = DPDK_COFIG_JUMBO_FRAME; - portConf.rxmode.hw_strip_crc = DPDK_COFIG_HW_STRIP_CRC; + portConf.rxmode.header_split = DPDK_COFIG_HEADER_SPLIT; + portConf.rxmode.hw_ip_checksum = DPDK_COFIG_HW_IP_CHECKSUM; + portConf.rxmode.hw_vlan_filter = DPDK_COFIG_HW_VLAN_FILTER; + portConf.rxmode.jumbo_frame = DPDK_COFIG_JUMBO_FRAME; + portConf.rxmode.hw_strip_crc = DPDK_COFIG_HW_STRIP_CRC; #endif - // Enable RSS only if hardware supports it and the user wants to use it - if (m_Config.rssHashFunction == RSS_NONE) - { - portConf.rxmode.mq_mode = DPDK_CONFIG_MQ_NO_RSS; - } - else - { - portConf.rxmode.mq_mode = DPDK_CONFIG_MQ_RSS; - } - - portConf.rx_adv_conf.rss_conf.rss_key = m_Config.rssKey; - portConf.rx_adv_conf.rss_conf.rss_key_len = m_Config.rssKeyLength; - portConf.rx_adv_conf.rss_conf.rss_hf = convertRssHfToDpdkRssHf(getConfiguredRssHashFunction()); - - int res = rte_eth_dev_configure((uint8_t) m_Id, numOfRxQueues, numOfTxQueues, &portConf); - if (res < 0) - { - PCPP_LOG_ERROR("Failed to configure device [" << m_DeviceName << "]. error is: '" << rte_strerror(res) << "' [Error code: " << res << "]"); - return false; - } - - PCPP_LOG_DEBUG("Successfully called rte_eth_dev_configure for device [" << m_DeviceName << "] with " << numOfRxQueues << " RX queues and " << numOfTxQueues << " TX queues"); - - return true; + // Enable RSS only if hardware supports it and the user wants to use it + if (m_Config.rssHashFunction == RSS_NONE) { + portConf.rxmode.mq_mode = DPDK_CONFIG_MQ_NO_RSS; + } else { + portConf.rxmode.mq_mode = DPDK_CONFIG_MQ_RSS; + } + + portConf.rx_adv_conf.rss_conf.rss_key = m_Config.rssKey; + portConf.rx_adv_conf.rss_conf.rss_key_len = m_Config.rssKeyLength; + portConf.rx_adv_conf.rss_conf.rss_hf = + convertRssHfToDpdkRssHf(getConfiguredRssHashFunction()); + + int res = rte_eth_dev_configure((uint8_t)m_Id, numOfRxQueues, numOfTxQueues, + &portConf); + if (res < 0) { + PCPP_LOG_ERROR("Failed to configure device [" + << m_DeviceName << "]. error is: '" << rte_strerror(res) + << "' [Error code: " << res << "]"); + return false; + } + + PCPP_LOG_DEBUG("Successfully called rte_eth_dev_configure for device [" + << m_DeviceName << "] with " << numOfRxQueues + << " RX queues and " << numOfTxQueues << " TX queues"); + + return true; } -bool DpdkDevice::initQueues(uint8_t numOfRxQueuesToInit, uint8_t numOfTxQueuesToInit) -{ - rte_eth_dev_info devInfo; - rte_eth_dev_info_get(m_Id, &devInfo); - if (numOfRxQueuesToInit > devInfo.max_rx_queues) - { - PCPP_LOG_ERROR("Num of RX queues requested for open [" << numOfRxQueuesToInit << "] is larger than RX queues available in NIC [" << devInfo.max_rx_queues << "]"); - return false; - } - - if (numOfTxQueuesToInit > devInfo.max_tx_queues) - { - PCPP_LOG_ERROR("Num of TX queues requested for open [" << numOfTxQueuesToInit << "] is larger than TX queues available in NIC [" << devInfo.max_tx_queues << "]"); - return false; - } - - for (uint8_t i = 0; i < numOfRxQueuesToInit; i++) - { - int ret = rte_eth_rx_queue_setup((uint8_t) m_Id, i, - m_Config.receiveDescriptorsNumber, 0, - NULL, m_MBufMempool); - - if (ret < 0) - { - PCPP_LOG_ERROR("Failed to init RX queue for device [" << m_DeviceName << "]. Error was: '" << rte_strerror(ret) << "' [Error code: " << ret << "]"); - return false; - } - } - - PCPP_LOG_DEBUG("Successfully initialized " << numOfRxQueuesToInit << " RX queues for device [" << m_DeviceName << "]"); - - for (uint8_t i = 0; i < numOfTxQueuesToInit; i++) - { - int ret = rte_eth_tx_queue_setup((uint8_t) m_Id, i, - m_Config.transmitDescriptorsNumber, - 0, NULL); - if (ret < 0) - { - PCPP_LOG_ERROR("Failed to init TX queue #" << i << " for port " << m_Id << ". Error was: '" << rte_strerror(ret) << "' [Error code: " << ret << "]"); - return false; - } - } - - if (m_TxBuffers != NULL) - delete [] m_TxBuffers; - - if (m_TxBufferLastDrainTsc != NULL) - delete [] m_TxBufferLastDrainTsc; - - m_TxBuffers = new rte_eth_dev_tx_buffer*[numOfTxQueuesToInit]; - m_TxBufferLastDrainTsc = new uint64_t[numOfTxQueuesToInit]; - memset(m_TxBufferLastDrainTsc, 0, sizeof(uint64_t)*numOfTxQueuesToInit); - - for (uint8_t i = 0; i < numOfTxQueuesToInit; i++) - { - m_TxBuffers[i] = (rte_eth_dev_tx_buffer*)rte_zmalloc_socket("tx_buffer", RTE_ETH_TX_BUFFER_SIZE(MAX_BURST_SIZE), 0, rte_eth_dev_socket_id(m_Id)); - - if (m_TxBuffers[i] == NULL) - { - PCPP_LOG_ERROR("Failed to allocate TX buffer for port " << m_Id << " TX queue " << (int)i); - return false; - } - - int res = rte_eth_tx_buffer_init(m_TxBuffers[i], MAX_BURST_SIZE); - - if (res != 0) - { - PCPP_LOG_ERROR("Failed to init TX buffer for port " << m_Id << " TX queue " << (int)i); - return false; - } - } - - m_TxBufferDrainTsc = (rte_get_tsc_hz() + US_PER_S - 1) / US_PER_S * m_Config.flushTxBufferTimeout; - - memset(m_TxBufferLastDrainTsc, 0, sizeof(uint64_t)*numOfTxQueuesToInit); - - PCPP_LOG_DEBUG("Successfully initialized " << numOfTxQueuesToInit << " TX queues for device [" << m_DeviceName << "]"); - - return true; +bool DpdkDevice::initQueues(uint8_t numOfRxQueuesToInit, + uint8_t numOfTxQueuesToInit) { + rte_eth_dev_info devInfo; + rte_eth_dev_info_get(m_Id, &devInfo); + if (numOfRxQueuesToInit > devInfo.max_rx_queues) { + PCPP_LOG_ERROR("Num of RX queues requested for open [" + << numOfRxQueuesToInit + << "] is larger than RX queues available in NIC [" + << devInfo.max_rx_queues << "]"); + return false; + } + + if (numOfTxQueuesToInit > devInfo.max_tx_queues) { + PCPP_LOG_ERROR("Num of TX queues requested for open [" + << numOfTxQueuesToInit + << "] is larger than TX queues available in NIC [" + << devInfo.max_tx_queues << "]"); + return false; + } + + for (uint8_t i = 0; i < numOfRxQueuesToInit; i++) { + int ret = rte_eth_rx_queue_setup((uint8_t)m_Id, i, + m_Config.receiveDescriptorsNumber, 0, NULL, + m_MBufMempool); + + if (ret < 0) { + PCPP_LOG_ERROR("Failed to init RX queue for device [" + << m_DeviceName << "]. Error was: '" << rte_strerror(ret) + << "' [Error code: " << ret << "]"); + return false; + } + } + + PCPP_LOG_DEBUG("Successfully initialized " << numOfRxQueuesToInit + << " RX queues for device [" + << m_DeviceName << "]"); + + for (uint8_t i = 0; i < numOfTxQueuesToInit; i++) { + int ret = rte_eth_tx_queue_setup( + (uint8_t)m_Id, i, m_Config.transmitDescriptorsNumber, 0, NULL); + if (ret < 0) { + PCPP_LOG_ERROR("Failed to init TX queue #" + << i << " for port " << m_Id << ". Error was: '" + << rte_strerror(ret) << "' [Error code: " << ret << "]"); + return false; + } + } + + if (m_TxBuffers != NULL) + delete[] m_TxBuffers; + + if (m_TxBufferLastDrainTsc != NULL) + delete[] m_TxBufferLastDrainTsc; + + m_TxBuffers = new rte_eth_dev_tx_buffer*[numOfTxQueuesToInit]; + m_TxBufferLastDrainTsc = new uint64_t[numOfTxQueuesToInit]; + memset(m_TxBufferLastDrainTsc, 0, sizeof(uint64_t) * numOfTxQueuesToInit); + + for (uint8_t i = 0; i < numOfTxQueuesToInit; i++) { + m_TxBuffers[i] = (rte_eth_dev_tx_buffer*)rte_zmalloc_socket( + "tx_buffer", RTE_ETH_TX_BUFFER_SIZE(MAX_BURST_SIZE), 0, + rte_eth_dev_socket_id(m_Id)); + + if (m_TxBuffers[i] == NULL) { + PCPP_LOG_ERROR("Failed to allocate TX buffer for port " + << m_Id << " TX queue " << (int)i); + return false; + } + + int res = rte_eth_tx_buffer_init(m_TxBuffers[i], MAX_BURST_SIZE); + + if (res != 0) { + PCPP_LOG_ERROR("Failed to init TX buffer for port " + << m_Id << " TX queue " << (int)i); + return false; + } + } + + m_TxBufferDrainTsc = (rte_get_tsc_hz() + US_PER_S - 1) / US_PER_S * + m_Config.flushTxBufferTimeout; + + memset(m_TxBufferLastDrainTsc, 0, sizeof(uint64_t) * numOfTxQueuesToInit); + + PCPP_LOG_DEBUG("Successfully initialized " << numOfTxQueuesToInit + << " TX queues for device [" + << m_DeviceName << "]"); + + return true; } - -bool DpdkDevice::initMemPool(struct rte_mempool*& memPool, const char* mempoolName, uint32_t mBufPoolSize) -{ - bool ret = false; - - // create mbuf pool - memPool = rte_pktmbuf_pool_create(mempoolName, mBufPoolSize, MEMPOOL_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id()); - if (memPool == NULL) - { - PCPP_LOG_ERROR("Failed to create packets memory pool for port " << m_Id << ", pool name: " << mempoolName << ". Error was: '" << rte_strerror(rte_errno) << "' [Error code: " << rte_errno << "]"); - } - else - { - PCPP_LOG_DEBUG("Successfully initialized packets pool of size [" << mBufPoolSize << "] for device [" << m_DeviceName << "]"); - ret = true; - } - return ret; +bool DpdkDevice::initMemPool(struct rte_mempool*& memPool, + const char* mempoolName, uint32_t mBufPoolSize) { + bool ret = false; + + // create mbuf pool + memPool = + rte_pktmbuf_pool_create(mempoolName, mBufPoolSize, MEMPOOL_CACHE_SIZE, 0, + RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id()); + if (memPool == NULL) { + PCPP_LOG_ERROR("Failed to create packets memory pool for port " + << m_Id << ", pool name: " << mempoolName << ". Error was: '" + << rte_strerror(rte_errno) << "' [Error code: " << rte_errno + << "]"); + } else { + PCPP_LOG_DEBUG("Successfully initialized packets pool of size [" + << mBufPoolSize << "] for device [" << m_DeviceName << "]"); + ret = true; + } + return ret; } -bool DpdkDevice::startDevice() -{ - int ret = rte_eth_dev_start((uint8_t) m_Id); - if (ret < 0) - { - PCPP_LOG_ERROR("Failed to start device " << m_Id << ". Error is " << ret); - return false; - } - - LinkStatus status; - getLinkStatus(status); - if (Logger::getInstance().isDebugEnabled(PcapLogModuleDpdkDevice)) - { - std::string linkStatus = (status.linkUp ? "up" : "down"); - std::string linkDuplex = (status.linkDuplex == LinkStatus::FULL_DUPLEX ? "full-duplex" : "half-duplex"); - PCPP_LOG_DEBUG("Device [" << m_DeviceName << "] : Link " << linkStatus << "; Speed: " << status.linkSpeedMbps << " Mbps; " << linkDuplex); - } - - rte_eth_promiscuous_enable((uint8_t) m_Id); - PCPP_LOG_DEBUG("Started device [" << m_DeviceName << "]"); - - return true; +bool DpdkDevice::startDevice() { + int ret = rte_eth_dev_start((uint8_t)m_Id); + if (ret < 0) { + PCPP_LOG_ERROR("Failed to start device " << m_Id << ". Error is " << ret); + return false; + } + + LinkStatus status; + getLinkStatus(status); + if (Logger::getInstance().isDebugEnabled(PcapLogModuleDpdkDevice)) { + std::string linkStatus = (status.linkUp ? "up" : "down"); + std::string linkDuplex = + (status.linkDuplex == LinkStatus::FULL_DUPLEX ? "full-duplex" + : "half-duplex"); + PCPP_LOG_DEBUG("Device [" << m_DeviceName << "] : Link " << linkStatus + << "; Speed: " << status.linkSpeedMbps + << " Mbps; " << linkDuplex); + } + + rte_eth_promiscuous_enable((uint8_t)m_Id); + PCPP_LOG_DEBUG("Started device [" << m_DeviceName << "]"); + + return true; } - -void DpdkDevice::clearCoreConfiguration() -{ - for (int i = 0; i < MAX_NUM_OF_CORES; i++) - { - m_CoreConfiguration[i].IsCoreInUse = false; - } +void DpdkDevice::clearCoreConfiguration() { + for (int i = 0; i < MAX_NUM_OF_CORES; i++) { + m_CoreConfiguration[i].IsCoreInUse = false; + } } -int DpdkDevice::getCoresInUseCount() const -{ - int res = 0; - for (int i = 0; i < MAX_NUM_OF_CORES; i++) - if (m_CoreConfiguration[i].IsCoreInUse) - res++; +int DpdkDevice::getCoresInUseCount() const { + int res = 0; + for (int i = 0; i < MAX_NUM_OF_CORES; i++) + if (m_CoreConfiguration[i].IsCoreInUse) + res++; - return res; + return res; } -void DpdkDevice::setDeviceInfo() -{ - rte_eth_dev_info portInfo; - rte_eth_dev_info_get(m_Id, &portInfo); - m_PMDName = std::string(portInfo.driver_name); - - if (m_PMDName == "eth_bond") - m_PMDType = PMD_BOND; - else if (m_PMDName == "rte_em_pmd") - m_PMDType = PMD_E1000EM; - else if (m_PMDName == "rte_igb_pmd") - m_PMDType = PMD_IGB; - else if (m_PMDName == "rte_igbvf_pmd") - m_PMDType = PMD_IGBVF; - else if (m_PMDName == "rte_enic_pmd") - m_PMDType = PMD_ENIC; - else if (m_PMDName == "rte_pmd_fm10k") - m_PMDType = PMD_FM10K; - else if (m_PMDName == "rte_i40e_pmd" || m_PMDName == "net_i40e") - m_PMDType = PMD_I40E; - else if (m_PMDName == "rte_i40evf_pmd") - m_PMDType = PMD_I40EVF; - else if (m_PMDName == "rte_ixgbe_pmd") - m_PMDType = PMD_IXGBE; - else if (m_PMDName == "rte_ixgbevf_pmd") - m_PMDType = PMD_IXGBEVF; - else if (m_PMDName == "librte_pmd_mlx4") - m_PMDType = PMD_MLX4; - else if (m_PMDName == "eth_null") - m_PMDType = PMD_NULL; - else if (m_PMDName == "eth_pcap") - m_PMDType = PMD_PCAP; - else if (m_PMDName == "eth_ring") - m_PMDType = PMD_RING; - else if (m_PMDName == "rte_virtio_pmd") - m_PMDType = PMD_VIRTIO; - else if (m_PMDName == "rte_vmxnet3_pmd") - m_PMDType = PMD_VMXNET3; - else if (m_PMDName == "eth_xenvirt") - m_PMDType = PMD_XENVIRT; - else - m_PMDType = PMD_UNKNOWN; - -#if (RTE_VER_YEAR < 18) || (RTE_VER_YEAR == 18 && RTE_VER_MONTH < 5) // before 18.05 - char pciName[30]; - #if (RTE_VER_YEAR > 17) || (RTE_VER_YEAR == 17 && RTE_VER_MONTH >= 11) // 17.11 - 18.02 - rte_pci_device_name(&(portInfo.pci_dev->addr), pciName, 30); - #else // 16.11 - 17.11 - rte_eal_pci_device_name(&(portInfo.pci_dev->addr), pciName, 30); - #endif - m_PciAddress = std::string(pciName); -#elif (RTE_VER_YEAR < 22) || (RTE_VER_YEAR == 22 && RTE_VER_MONTH < 11) // before 22.11 - m_PciAddress = std::string(portInfo.device->name); -#else // 22.11 forward - m_PciAddress = std::string(rte_dev_name(portInfo.device)); +void DpdkDevice::setDeviceInfo() { + rte_eth_dev_info portInfo; + rte_eth_dev_info_get(m_Id, &portInfo); + m_PMDName = std::string(portInfo.driver_name); + + if (m_PMDName == "eth_bond") + m_PMDType = PMD_BOND; + else if (m_PMDName == "rte_em_pmd") + m_PMDType = PMD_E1000EM; + else if (m_PMDName == "rte_igb_pmd") + m_PMDType = PMD_IGB; + else if (m_PMDName == "rte_igbvf_pmd") + m_PMDType = PMD_IGBVF; + else if (m_PMDName == "rte_enic_pmd") + m_PMDType = PMD_ENIC; + else if (m_PMDName == "rte_pmd_fm10k") + m_PMDType = PMD_FM10K; + else if (m_PMDName == "rte_i40e_pmd" || m_PMDName == "net_i40e") + m_PMDType = PMD_I40E; + else if (m_PMDName == "rte_i40evf_pmd") + m_PMDType = PMD_I40EVF; + else if (m_PMDName == "rte_ixgbe_pmd") + m_PMDType = PMD_IXGBE; + else if (m_PMDName == "rte_ixgbevf_pmd") + m_PMDType = PMD_IXGBEVF; + else if (m_PMDName == "librte_pmd_mlx4") + m_PMDType = PMD_MLX4; + else if (m_PMDName == "eth_null") + m_PMDType = PMD_NULL; + else if (m_PMDName == "eth_pcap") + m_PMDType = PMD_PCAP; + else if (m_PMDName == "eth_ring") + m_PMDType = PMD_RING; + else if (m_PMDName == "rte_virtio_pmd") + m_PMDType = PMD_VIRTIO; + else if (m_PMDName == "rte_vmxnet3_pmd") + m_PMDType = PMD_VMXNET3; + else if (m_PMDName == "eth_xenvirt") + m_PMDType = PMD_XENVIRT; + else + m_PMDType = PMD_UNKNOWN; + +#if (RTE_VER_YEAR < 18) || \ + (RTE_VER_YEAR == 18 && RTE_VER_MONTH < 5) // before 18.05 + char pciName[30]; +#if (RTE_VER_YEAR > 17) || \ + (RTE_VER_YEAR == 17 && RTE_VER_MONTH >= 11) // 17.11 - 18.02 + rte_pci_device_name(&(portInfo.pci_dev->addr), pciName, 30); +#else // 16.11 - 17.11 + rte_eal_pci_device_name(&(portInfo.pci_dev->addr), pciName, 30); +#endif + m_PciAddress = std::string(pciName); +#elif (RTE_VER_YEAR < 22) || \ + (RTE_VER_YEAR == 22 && RTE_VER_MONTH < 11) // before 22.11 + m_PciAddress = std::string(portInfo.device->name); +#else // 22.11 forward + m_PciAddress = std::string(rte_dev_name(portInfo.device)); #endif - PCPP_LOG_DEBUG("Device [" << m_DeviceName << "] has " << portInfo.max_rx_queues << " RX queues"); - PCPP_LOG_DEBUG("Device [" << m_DeviceName << "] has " << portInfo.max_tx_queues << " TX queues"); + PCPP_LOG_DEBUG("Device [" << m_DeviceName << "] has " + << portInfo.max_rx_queues << " RX queues"); + PCPP_LOG_DEBUG("Device [" << m_DeviceName << "] has " + << portInfo.max_tx_queues << " TX queues"); - m_TotalAvailableRxQueues = portInfo.max_rx_queues; - m_TotalAvailableTxQueues = portInfo.max_tx_queues; + m_TotalAvailableRxQueues = portInfo.max_rx_queues; + m_TotalAvailableTxQueues = portInfo.max_tx_queues; } - -bool DpdkDevice::isVirtual() const -{ - switch (m_PMDType) - { - case PMD_IGBVF: - case PMD_I40EVF: - case PMD_IXGBEVF: - case PMD_PCAP: - case PMD_RING: - case PMD_VIRTIO: - case PMD_VMXNET3: - case PMD_XENVIRT: - return true; - default: - return false; - } +bool DpdkDevice::isVirtual() const { + switch (m_PMDType) { + case PMD_IGBVF: + case PMD_I40EVF: + case PMD_IXGBEVF: + case PMD_PCAP: + case PMD_RING: + case PMD_VIRTIO: + case PMD_VMXNET3: + case PMD_XENVIRT: + return true; + default: + return false; + } } - -void DpdkDevice::getLinkStatus(LinkStatus& linkStatus) const -{ - struct rte_eth_link link; - rte_eth_link_get((uint8_t) m_Id, &link); - linkStatus.linkUp = link.link_status; - linkStatus.linkSpeedMbps = (unsigned) link.link_speed; - linkStatus.linkDuplex = (link.link_duplex == DPDK_CONFIG_ETH_LINK_FULL_DUPLEX) ? LinkStatus::FULL_DUPLEX : LinkStatus::HALF_DUPLEX; +void DpdkDevice::getLinkStatus(LinkStatus& linkStatus) const { + struct rte_eth_link link; + rte_eth_link_get((uint8_t)m_Id, &link); + linkStatus.linkUp = link.link_status; + linkStatus.linkSpeedMbps = (unsigned)link.link_speed; + linkStatus.linkDuplex = (link.link_duplex == DPDK_CONFIG_ETH_LINK_FULL_DUPLEX) + ? LinkStatus::FULL_DUPLEX + : LinkStatus::HALF_DUPLEX; } - -bool DpdkDevice::initCoreConfigurationByCoreMask(CoreMask coreMask) -{ - int i = 0; - int numOfCores = getNumOfCores(); - clearCoreConfiguration(); - while ((coreMask != 0) && (i < numOfCores)) - { - if (coreMask & 1) - { - if (i == DpdkDeviceList::getInstance().getDpdkMasterCore().Id) - { - PCPP_LOG_ERROR("Core " << i << " is the master core, you can't use it for capturing threads"); - clearCoreConfiguration(); - return false; - } - - if (!rte_lcore_is_enabled(i)) - { - PCPP_LOG_ERROR("Trying to use core #" << i << " which isn't initialized by DPDK"); - clearCoreConfiguration(); - return false; - } - m_CoreConfiguration[i].IsCoreInUse = true; - } - - coreMask = coreMask >> 1; - i++; - } - - if (coreMask != 0) // this mean coreMask contains a core that doesn't exist - { - PCPP_LOG_ERROR("Trying to use a core [" << i << "] that doesn't exist while machine has " << numOfCores << " cores"); - clearCoreConfiguration(); - return false; - } - - return true; +bool DpdkDevice::initCoreConfigurationByCoreMask(CoreMask coreMask) { + int i = 0; + int numOfCores = getNumOfCores(); + clearCoreConfiguration(); + while ((coreMask != 0) && (i < numOfCores)) { + if (coreMask & 1) { + if (i == DpdkDeviceList::getInstance().getDpdkMasterCore().Id) { + PCPP_LOG_ERROR( + "Core " + << i + << " is the master core, you can't use it for capturing threads"); + clearCoreConfiguration(); + return false; + } + + if (!rte_lcore_is_enabled(i)) { + PCPP_LOG_ERROR("Trying to use core #" + << i << " which isn't initialized by DPDK"); + clearCoreConfiguration(); + return false; + } + m_CoreConfiguration[i].IsCoreInUse = true; + } + + coreMask = coreMask >> 1; + i++; + } + + if (coreMask != 0) // this mean coreMask contains a core that doesn't exist + { + PCPP_LOG_ERROR("Trying to use a core [" + << i << "] that doesn't exist while machine has " + << numOfCores << " cores"); + clearCoreConfiguration(); + return false; + } + + return true; } - -bool DpdkDevice::startCaptureSingleThread(OnDpdkPacketsArriveCallback onPacketsArrive, void* onPacketsArriveUserCookie) -{ - if (!m_StopThread) - { - PCPP_LOG_ERROR("Device already capturing. Cannot start 2 capture sessions at the same time"); - return false; - } - - if (m_NumOfRxQueuesOpened != 1) - { - PCPP_LOG_ERROR("Cannot start capturing on a single thread when more than 1 RX queue is opened"); - return false; - } - - PCPP_LOG_DEBUG("Trying to start capturing on a single thread for device [" << m_DeviceName << "]"); - - clearCoreConfiguration(); - - m_OnPacketsArriveCallback = onPacketsArrive; - m_OnPacketsArriveUserCookie = onPacketsArriveUserCookie; - - m_StopThread = false; - - for (int coreId = 0; coreId < MAX_NUM_OF_CORES; coreId++) - { - if (coreId == (int)GET_MASTER_CORE() || !rte_lcore_is_enabled(coreId)) - continue; - - m_CoreConfiguration[coreId].IsCoreInUse = true; - m_CoreConfiguration[coreId].RxQueueId = 0; - - PCPP_LOG_DEBUG("Trying to start capturing on core " << coreId); - int err = rte_eal_remote_launch(dpdkCaptureThreadMain, (void*)this, coreId); - if (err != 0) - { - PCPP_LOG_ERROR("Cannot create capture thread for device '" << m_DeviceName << "'"); - m_CoreConfiguration[coreId].IsCoreInUse = false; - return false; - } - - PCPP_LOG_DEBUG("Capturing started for device [" << m_DeviceName << "]"); - return true; - } - - PCPP_LOG_ERROR("Could not find initialized core so capturing thread cannot be initialized"); - return false; +bool DpdkDevice::startCaptureSingleThread( + OnDpdkPacketsArriveCallback onPacketsArrive, + void* onPacketsArriveUserCookie) { + if (!m_StopThread) { + PCPP_LOG_ERROR("Device already capturing. Cannot start 2 capture sessions " + "at the same time"); + return false; + } + + if (m_NumOfRxQueuesOpened != 1) { + PCPP_LOG_ERROR("Cannot start capturing on a single thread when more than 1 " + "RX queue is opened"); + return false; + } + + PCPP_LOG_DEBUG("Trying to start capturing on a single thread for device [" + << m_DeviceName << "]"); + + clearCoreConfiguration(); + + m_OnPacketsArriveCallback = onPacketsArrive; + m_OnPacketsArriveUserCookie = onPacketsArriveUserCookie; + + m_StopThread = false; + + for (int coreId = 0; coreId < MAX_NUM_OF_CORES; coreId++) { + if (coreId == (int)GET_MASTER_CORE() || !rte_lcore_is_enabled(coreId)) + continue; + + m_CoreConfiguration[coreId].IsCoreInUse = true; + m_CoreConfiguration[coreId].RxQueueId = 0; + + PCPP_LOG_DEBUG("Trying to start capturing on core " << coreId); + int err = + rte_eal_remote_launch(dpdkCaptureThreadMain, (void*)this, coreId); + if (err != 0) { + PCPP_LOG_ERROR("Cannot create capture thread for device '" << m_DeviceName + << "'"); + m_CoreConfiguration[coreId].IsCoreInUse = false; + return false; + } + + PCPP_LOG_DEBUG("Capturing started for device [" << m_DeviceName << "]"); + return true; + } + + PCPP_LOG_ERROR("Could not find initialized core so capturing thread cannot " + "be initialized"); + return false; } -bool DpdkDevice::startCaptureMultiThreads(OnDpdkPacketsArriveCallback onPacketsArrive, void* onPacketsArriveUserCookie, CoreMask coreMask) -{ - if (!m_DeviceOpened) - { - PCPP_LOG_ERROR("Device not opened"); - return false; - } - - if (!initCoreConfigurationByCoreMask(coreMask)) - return false; - - if (m_NumOfRxQueuesOpened != getCoresInUseCount()) - { - PCPP_LOG_ERROR("Cannot use a different number of queues and cores. Opened " << m_NumOfRxQueuesOpened << " queues but set " << getCoresInUseCount() << " cores in core mask"); - clearCoreConfiguration(); - return false; - } - - m_StopThread = false; - int rxQueue = 0; - for (int coreId = 0; coreId < MAX_NUM_OF_CORES; coreId++) - { - if (!m_CoreConfiguration[coreId].IsCoreInUse) - continue; - - // create a new thread - m_CoreConfiguration[coreId].RxQueueId = rxQueue++; - int err = rte_eal_remote_launch(dpdkCaptureThreadMain, (void*)this, coreId); - if (err != 0) - { - PCPP_LOG_ERROR("Cannot create capture thread #" << coreId << " for device '" << m_DeviceName << "': [" << strerror(err) << "]"); - m_CoreConfiguration[coreId].clear(); - return false; - } - } - - m_OnPacketsArriveCallback = onPacketsArrive; - m_OnPacketsArriveUserCookie = onPacketsArriveUserCookie; - - return true; +bool DpdkDevice::startCaptureMultiThreads( + OnDpdkPacketsArriveCallback onPacketsArrive, + void* onPacketsArriveUserCookie, CoreMask coreMask) { + if (!m_DeviceOpened) { + PCPP_LOG_ERROR("Device not opened"); + return false; + } + + if (!initCoreConfigurationByCoreMask(coreMask)) + return false; + + if (m_NumOfRxQueuesOpened != getCoresInUseCount()) { + PCPP_LOG_ERROR("Cannot use a different number of queues and cores. Opened " + << m_NumOfRxQueuesOpened << " queues but set " + << getCoresInUseCount() << " cores in core mask"); + clearCoreConfiguration(); + return false; + } + + m_StopThread = false; + int rxQueue = 0; + for (int coreId = 0; coreId < MAX_NUM_OF_CORES; coreId++) { + if (!m_CoreConfiguration[coreId].IsCoreInUse) + continue; + + // create a new thread + m_CoreConfiguration[coreId].RxQueueId = rxQueue++; + int err = + rte_eal_remote_launch(dpdkCaptureThreadMain, (void*)this, coreId); + if (err != 0) { + PCPP_LOG_ERROR("Cannot create capture thread #" + << coreId << " for device '" << m_DeviceName << "': [" + << strerror(err) << "]"); + m_CoreConfiguration[coreId].clear(); + return false; + } + } + + m_OnPacketsArriveCallback = onPacketsArrive; + m_OnPacketsArriveUserCookie = onPacketsArriveUserCookie; + + return true; } -void DpdkDevice::stopCapture() -{ - PCPP_LOG_DEBUG("Trying to stop capturing on device [" << m_DeviceName << "]"); - m_StopThread = true; - for (int coreId = 0; coreId < MAX_NUM_OF_CORES; coreId++) - { - if (!m_CoreConfiguration[coreId].IsCoreInUse) - continue; - rte_eal_wait_lcore(coreId); - PCPP_LOG_DEBUG("Thread on core [" << coreId << "] stopped"); - } - - PCPP_LOG_DEBUG("All capturing threads stopped"); +void DpdkDevice::stopCapture() { + PCPP_LOG_DEBUG("Trying to stop capturing on device [" << m_DeviceName << "]"); + m_StopThread = true; + for (int coreId = 0; coreId < MAX_NUM_OF_CORES; coreId++) { + if (!m_CoreConfiguration[coreId].IsCoreInUse) + continue; + rte_eal_wait_lcore(coreId); + PCPP_LOG_DEBUG("Thread on core [" << coreId << "] stopped"); + } + + PCPP_LOG_DEBUG("All capturing threads stopped"); } -int DpdkDevice::dpdkCaptureThreadMain(void *ptr) -{ - DpdkDevice* pThis = (DpdkDevice*)ptr; - struct rte_mbuf* mBufArray[MAX_BURST_SIZE]; +int DpdkDevice::dpdkCaptureThreadMain(void* ptr) { + DpdkDevice* pThis = (DpdkDevice*)ptr; + struct rte_mbuf* mBufArray[MAX_BURST_SIZE]; - if (pThis == NULL) - { - PCPP_LOG_ERROR("Failed to retrieve DPDK device in capture thread main loop"); - return 1; - } + if (pThis == NULL) { + PCPP_LOG_ERROR( + "Failed to retrieve DPDK device in capture thread main loop"); + return 1; + } - uint32_t coreId = pThis->getCurrentCoreId(); - PCPP_LOG_DEBUG("Starting capture thread " << coreId); + uint32_t coreId = pThis->getCurrentCoreId(); + PCPP_LOG_DEBUG("Starting capture thread " << coreId); - int queueId = pThis->m_CoreConfiguration[coreId].RxQueueId; + int queueId = pThis->m_CoreConfiguration[coreId].RxQueueId; - while (likely(!pThis->m_StopThread)) - { - uint32_t numOfPktsReceived = rte_eth_rx_burst(pThis->m_Id, queueId, mBufArray, MAX_BURST_SIZE); + while (likely(!pThis->m_StopThread)) { + uint32_t numOfPktsReceived = + rte_eth_rx_burst(pThis->m_Id, queueId, mBufArray, MAX_BURST_SIZE); - if (unlikely(numOfPktsReceived == 0)) - continue; + if (unlikely(numOfPktsReceived == 0)) + continue; - timespec time; - clock_gettime(CLOCK_REALTIME, &time); + timespec time; + clock_gettime(CLOCK_REALTIME, &time); - if (likely(pThis->m_OnPacketsArriveCallback != NULL)) - { - MBufRawPacket rawPackets[MAX_BURST_SIZE]; - for (uint32_t index = 0; index < numOfPktsReceived; ++index) - { - rawPackets[index].setMBuf(mBufArray[index], time); - } + if (likely(pThis->m_OnPacketsArriveCallback != NULL)) { + MBufRawPacket rawPackets[MAX_BURST_SIZE]; + for (uint32_t index = 0; index < numOfPktsReceived; ++index) { + rawPackets[index].setMBuf(mBufArray[index], time); + } - pThis->m_OnPacketsArriveCallback(rawPackets, numOfPktsReceived, coreId, pThis, pThis->m_OnPacketsArriveUserCookie); - } - } + pThis->m_OnPacketsArriveCallback(rawPackets, numOfPktsReceived, coreId, + pThis, + pThis->m_OnPacketsArriveUserCookie); + } + } - PCPP_LOG_DEBUG("Exiting capture thread " << coreId); + PCPP_LOG_DEBUG("Exiting capture thread " << coreId); - return 0; + return 0; } -#define nanosec_gap(begin, end) ((end.tv_sec - begin.tv_sec) * 1000000000.0 + (end.tv_nsec - begin.tv_nsec)) - -void DpdkDevice::getStatistics(DpdkDeviceStats& stats) const -{ - timespec timestamp; - clock_gettime(CLOCK_MONOTONIC, ×tamp); - struct rte_eth_stats rteStats; - rte_eth_stats_get(m_Id, &rteStats); - - double secsElapsed = (double)nanosec_gap(m_PrevStats.timestamp, timestamp) / 1000000000.0; - - stats.devId = m_Id; - stats.timestamp = timestamp; - stats.rxErroneousPackets = rteStats.ierrors; - stats.rxMbufAlocFailed = rteStats.rx_nombuf; - stats.rxPacketsDroppedByHW = rteStats.imissed; - stats.aggregatedRxStats.packets = rteStats.ipackets; - stats.aggregatedRxStats.bytes = rteStats.ibytes; - stats.aggregatedRxStats.packetsPerSec = (stats.aggregatedRxStats.packets - m_PrevStats.aggregatedRxStats.packets) / secsElapsed; - stats.aggregatedRxStats.bytesPerSec = (stats.aggregatedRxStats.bytes - m_PrevStats.aggregatedRxStats.bytes) / secsElapsed; - stats.aggregatedTxStats.packets = rteStats.opackets; - stats.aggregatedTxStats.bytes = rteStats.obytes; - stats.aggregatedTxStats.packetsPerSec = (stats.aggregatedTxStats.packets - m_PrevStats.aggregatedTxStats.packets) / secsElapsed; - stats.aggregatedTxStats.bytesPerSec = (stats.aggregatedTxStats.bytes - m_PrevStats.aggregatedTxStats.bytes) / secsElapsed; - - int numRxQs = std::min(DPDK_MAX_RX_QUEUES, RTE_ETHDEV_QUEUE_STAT_CNTRS); - int numTxQs = std::min(DPDK_MAX_TX_QUEUES, RTE_ETHDEV_QUEUE_STAT_CNTRS); - - for (int i = 0; i < numRxQs; i++) - { - stats.rxStats[i].packets = rteStats.q_ipackets[i]; - stats.rxStats[i].bytes = rteStats.q_ibytes[i]; - stats.rxStats[i].packetsPerSec = (stats.rxStats[i].packets - m_PrevStats.rxStats[i].packets) / secsElapsed; - stats.rxStats[i].bytesPerSec = (stats.rxStats[i].bytes - m_PrevStats.rxStats[i].bytes) / secsElapsed; - } - - for (int i = 0; i < numTxQs; i++) - { - stats.txStats[i].packets = rteStats.q_opackets[i]; - stats.txStats[i].bytes = rteStats.q_obytes[i]; - stats.txStats[i].packetsPerSec = (stats.txStats[i].packets - m_PrevStats.txStats[i].packets) / secsElapsed; - stats.txStats[i].bytesPerSec = (stats.txStats[i].bytes - m_PrevStats.txStats[i].bytes) / secsElapsed; - } - - //m_PrevStats = stats; - memcpy(&m_PrevStats, &stats, sizeof(m_PrevStats)); +#define nanosec_gap(begin, end) \ + ((end.tv_sec - begin.tv_sec) * 1000000000.0 + (end.tv_nsec - begin.tv_nsec)) + +void DpdkDevice::getStatistics(DpdkDeviceStats& stats) const { + timespec timestamp; + clock_gettime(CLOCK_MONOTONIC, ×tamp); + struct rte_eth_stats rteStats; + rte_eth_stats_get(m_Id, &rteStats); + + double secsElapsed = + (double)nanosec_gap(m_PrevStats.timestamp, timestamp) / 1000000000.0; + + stats.devId = m_Id; + stats.timestamp = timestamp; + stats.rxErroneousPackets = rteStats.ierrors; + stats.rxMbufAlocFailed = rteStats.rx_nombuf; + stats.rxPacketsDroppedByHW = rteStats.imissed; + stats.aggregatedRxStats.packets = rteStats.ipackets; + stats.aggregatedRxStats.bytes = rteStats.ibytes; + stats.aggregatedRxStats.packetsPerSec = + (stats.aggregatedRxStats.packets - + m_PrevStats.aggregatedRxStats.packets) / + secsElapsed; + stats.aggregatedRxStats.bytesPerSec = + (stats.aggregatedRxStats.bytes - m_PrevStats.aggregatedRxStats.bytes) / + secsElapsed; + stats.aggregatedTxStats.packets = rteStats.opackets; + stats.aggregatedTxStats.bytes = rteStats.obytes; + stats.aggregatedTxStats.packetsPerSec = + (stats.aggregatedTxStats.packets - + m_PrevStats.aggregatedTxStats.packets) / + secsElapsed; + stats.aggregatedTxStats.bytesPerSec = + (stats.aggregatedTxStats.bytes - m_PrevStats.aggregatedTxStats.bytes) / + secsElapsed; + + int numRxQs = std::min(DPDK_MAX_RX_QUEUES, RTE_ETHDEV_QUEUE_STAT_CNTRS); + int numTxQs = std::min(DPDK_MAX_TX_QUEUES, RTE_ETHDEV_QUEUE_STAT_CNTRS); + + for (int i = 0; i < numRxQs; i++) { + stats.rxStats[i].packets = rteStats.q_ipackets[i]; + stats.rxStats[i].bytes = rteStats.q_ibytes[i]; + stats.rxStats[i].packetsPerSec = + (stats.rxStats[i].packets - m_PrevStats.rxStats[i].packets) / + secsElapsed; + stats.rxStats[i].bytesPerSec = + (stats.rxStats[i].bytes - m_PrevStats.rxStats[i].bytes) / secsElapsed; + } + + for (int i = 0; i < numTxQs; i++) { + stats.txStats[i].packets = rteStats.q_opackets[i]; + stats.txStats[i].bytes = rteStats.q_obytes[i]; + stats.txStats[i].packetsPerSec = + (stats.txStats[i].packets - m_PrevStats.txStats[i].packets) / + secsElapsed; + stats.txStats[i].bytesPerSec = + (stats.txStats[i].bytes - m_PrevStats.txStats[i].bytes) / secsElapsed; + } + + // m_PrevStats = stats; + memcpy(&m_PrevStats, &stats, sizeof(m_PrevStats)); } -void DpdkDevice::clearStatistics() -{ - rte_eth_stats_reset(m_Id); - memset(&m_PrevStats, 0 ,sizeof(m_PrevStats)); +void DpdkDevice::clearStatistics() { + rte_eth_stats_reset(m_Id); + memset(&m_PrevStats, 0, sizeof(m_PrevStats)); } - -bool DpdkDevice::setFilter(GeneralFilter& filter) -{ - //TODO: I think DPDK supports filters - PCPP_LOG_ERROR("Filters aren't supported in DPDK device"); - return false; +bool DpdkDevice::setFilter(GeneralFilter& filter) { + // TODO: I think DPDK supports filters + PCPP_LOG_ERROR("Filters aren't supported in DPDK device"); + return false; } -bool DpdkDevice::setFilter(std::string filterAsString) -{ - //TODO: I think DPDK supports filters - PCPP_LOG_ERROR("Filters aren't supported in DPDK device"); - return false; +bool DpdkDevice::setFilter(std::string filterAsString) { + // TODO: I think DPDK supports filters + PCPP_LOG_ERROR("Filters aren't supported in DPDK device"); + return false; } -uint16_t DpdkDevice::receivePackets(MBufRawPacketVector& rawPacketsArr, uint16_t rxQueueId) const -{ - if (!m_DeviceOpened) - { - PCPP_LOG_ERROR("Device not opened"); - return 0; - } - - if (!m_StopThread) - { - PCPP_LOG_ERROR("DpdkDevice capture mode is currently running. Cannot receive packets in parallel"); - return 0; - } - - if (rxQueueId >= m_TotalAvailableRxQueues) - { - PCPP_LOG_ERROR("RX queue ID #" << rxQueueId << " not available for this device"); - return 0; - } - - struct rte_mbuf* mBufArray[MAX_BURST_SIZE]; - uint32_t numOfPktsReceived = rte_eth_rx_burst(m_Id, rxQueueId, mBufArray, MAX_BURST_SIZE); - - //the following line trashes the log with many messages. Uncomment only if necessary - //PCPP_LOG_DEBUG("Captured %d packets", numOfPktsReceived); - - if (unlikely(!numOfPktsReceived)) - { - return 0; - } - - timespec time; - clock_gettime(CLOCK_REALTIME, &time); - - for (uint32_t index = 0; index < numOfPktsReceived; ++index) - { - struct rte_mbuf* mBuf = mBufArray[index]; - MBufRawPacket* newRawPacket = new MBufRawPacket(); - newRawPacket->setMBuf(mBuf, time); - rawPacketsArr.pushBack(newRawPacket); - } - - return numOfPktsReceived; +uint16_t DpdkDevice::receivePackets(MBufRawPacketVector& rawPacketsArr, + uint16_t rxQueueId) const { + if (!m_DeviceOpened) { + PCPP_LOG_ERROR("Device not opened"); + return 0; + } + + if (!m_StopThread) { + PCPP_LOG_ERROR("DpdkDevice capture mode is currently running. Cannot " + "receive packets in parallel"); + return 0; + } + + if (rxQueueId >= m_TotalAvailableRxQueues) { + PCPP_LOG_ERROR("RX queue ID #" << rxQueueId + << " not available for this device"); + return 0; + } + + struct rte_mbuf* mBufArray[MAX_BURST_SIZE]; + uint32_t numOfPktsReceived = + rte_eth_rx_burst(m_Id, rxQueueId, mBufArray, MAX_BURST_SIZE); + + // the following line trashes the log with many messages. Uncomment only if + // necessary PCPP_LOG_DEBUG("Captured %d packets", numOfPktsReceived); + + if (unlikely(!numOfPktsReceived)) { + return 0; + } + + timespec time; + clock_gettime(CLOCK_REALTIME, &time); + + for (uint32_t index = 0; index < numOfPktsReceived; ++index) { + struct rte_mbuf* mBuf = mBufArray[index]; + MBufRawPacket* newRawPacket = new MBufRawPacket(); + newRawPacket->setMBuf(mBuf, time); + rawPacketsArr.pushBack(newRawPacket); + } + + return numOfPktsReceived; } -uint16_t DpdkDevice::receivePackets(MBufRawPacket** rawPacketsArr, uint16_t rawPacketArrLength, uint16_t rxQueueId) const -{ - if (unlikely(!m_DeviceOpened)) - { - PCPP_LOG_ERROR("Device not opened"); - return 0; - } - - if (unlikely(!m_StopThread)) - { - PCPP_LOG_ERROR("DpdkDevice capture mode is currently running. Cannot receive packets in parallel"); - return 0; - } - - if (unlikely(rxQueueId >= m_TotalAvailableRxQueues)) - { - PCPP_LOG_ERROR("RX queue ID #" << rxQueueId << " not available for this device"); - return 0; - } - - if (unlikely(rawPacketsArr == NULL)) - { - PCPP_LOG_ERROR("Provided address of array to store packets is NULL"); - return 0; - } - - struct rte_mbuf* mBufArray[rawPacketArrLength]; - uint16_t packetsReceived = rte_eth_rx_burst(m_Id, rxQueueId, mBufArray, rawPacketArrLength); - - if (unlikely(!packetsReceived)) - { - return 0; - } - - timespec time; - clock_gettime(CLOCK_REALTIME, &time); - - for (size_t index = 0; index < packetsReceived; ++index) - { - struct rte_mbuf* mBuf = mBufArray[index]; - if (rawPacketsArr[index] == NULL) - rawPacketsArr[index] = new MBufRawPacket(); - - rawPacketsArr[index]->setMBuf(mBuf, time); - } - - return packetsReceived; +uint16_t DpdkDevice::receivePackets(MBufRawPacket** rawPacketsArr, + uint16_t rawPacketArrLength, + uint16_t rxQueueId) const { + if (unlikely(!m_DeviceOpened)) { + PCPP_LOG_ERROR("Device not opened"); + return 0; + } + + if (unlikely(!m_StopThread)) { + PCPP_LOG_ERROR("DpdkDevice capture mode is currently running. Cannot " + "receive packets in parallel"); + return 0; + } + + if (unlikely(rxQueueId >= m_TotalAvailableRxQueues)) { + PCPP_LOG_ERROR("RX queue ID #" << rxQueueId + << " not available for this device"); + return 0; + } + + if (unlikely(rawPacketsArr == NULL)) { + PCPP_LOG_ERROR("Provided address of array to store packets is NULL"); + return 0; + } + + struct rte_mbuf* mBufArray[rawPacketArrLength]; + uint16_t packetsReceived = + rte_eth_rx_burst(m_Id, rxQueueId, mBufArray, rawPacketArrLength); + + if (unlikely(!packetsReceived)) { + return 0; + } + + timespec time; + clock_gettime(CLOCK_REALTIME, &time); + + for (size_t index = 0; index < packetsReceived; ++index) { + struct rte_mbuf* mBuf = mBufArray[index]; + if (rawPacketsArr[index] == NULL) + rawPacketsArr[index] = new MBufRawPacket(); + + rawPacketsArr[index]->setMBuf(mBuf, time); + } + + return packetsReceived; } -uint16_t DpdkDevice::receivePackets(Packet** packetsArr, uint16_t packetsArrLength, uint16_t rxQueueId) const -{ - if (unlikely(!m_DeviceOpened)) - { - PCPP_LOG_ERROR("Device not opened"); - return 0; - } - - if (unlikely(!m_StopThread)) - { - PCPP_LOG_ERROR("DpdkDevice capture mode is currently running. Cannot receive packets in parallel"); - return 0; - } - - if (unlikely(rxQueueId >= m_TotalAvailableRxQueues)) - { - PCPP_LOG_ERROR("RX queue ID #" << rxQueueId << " not available for this device"); - return 0; - } - - struct rte_mbuf* mBufArray[packetsArrLength]; - uint16_t packetsReceived = rte_eth_rx_burst(m_Id, rxQueueId, mBufArray, packetsArrLength); - - if (unlikely(!packetsReceived)) - { - return 0; - } - - timespec time; - clock_gettime(CLOCK_REALTIME, &time); - - for (size_t index = 0; index < packetsReceived; ++index) - { - struct rte_mbuf* mBuf = mBufArray[index]; - MBufRawPacket* newRawPacket = new MBufRawPacket(); - newRawPacket->setMBuf(mBuf, time); - if (packetsArr[index] == NULL) - packetsArr[index] = new Packet(); - - packetsArr[index]->setRawPacket(newRawPacket, true); - } - - return packetsReceived; +uint16_t DpdkDevice::receivePackets(Packet** packetsArr, + uint16_t packetsArrLength, + uint16_t rxQueueId) const { + if (unlikely(!m_DeviceOpened)) { + PCPP_LOG_ERROR("Device not opened"); + return 0; + } + + if (unlikely(!m_StopThread)) { + PCPP_LOG_ERROR("DpdkDevice capture mode is currently running. Cannot " + "receive packets in parallel"); + return 0; + } + + if (unlikely(rxQueueId >= m_TotalAvailableRxQueues)) { + PCPP_LOG_ERROR("RX queue ID #" << rxQueueId + << " not available for this device"); + return 0; + } + + struct rte_mbuf* mBufArray[packetsArrLength]; + uint16_t packetsReceived = + rte_eth_rx_burst(m_Id, rxQueueId, mBufArray, packetsArrLength); + + if (unlikely(!packetsReceived)) { + return 0; + } + + timespec time; + clock_gettime(CLOCK_REALTIME, &time); + + for (size_t index = 0; index < packetsReceived; ++index) { + struct rte_mbuf* mBuf = mBufArray[index]; + MBufRawPacket* newRawPacket = new MBufRawPacket(); + newRawPacket->setMBuf(mBuf, time); + if (packetsArr[index] == NULL) + packetsArr[index] = new Packet(); + + packetsArr[index]->setRawPacket(newRawPacket, true); + } + + return packetsReceived; } -uint16_t DpdkDevice::flushTxBuffer(bool flushOnlyIfTimeoutExpired, uint16_t txQueueId) -{ - bool flush = true; +uint16_t DpdkDevice::flushTxBuffer(bool flushOnlyIfTimeoutExpired, + uint16_t txQueueId) { + bool flush = true; - if (flushOnlyIfTimeoutExpired) - { - uint64_t curTsc = rte_rdtsc(); + if (flushOnlyIfTimeoutExpired) { + uint64_t curTsc = rte_rdtsc(); - if (curTsc - m_TxBufferLastDrainTsc[txQueueId] > m_TxBufferDrainTsc) - m_TxBufferLastDrainTsc[txQueueId] = curTsc; - else - flush = false; - } + if (curTsc - m_TxBufferLastDrainTsc[txQueueId] > m_TxBufferDrainTsc) + m_TxBufferLastDrainTsc[txQueueId] = curTsc; + else + flush = false; + } - if (flush) - return rte_eth_tx_buffer_flush(m_Id, txQueueId, m_TxBuffers[txQueueId]); + if (flush) + return rte_eth_tx_buffer_flush(m_Id, txQueueId, m_TxBuffers[txQueueId]); - return 0; + return 0; } -static rte_mbuf* getNextPacketFromMBufRawPacketArray(void* packetStorage, int index) -{ - MBufRawPacket** packetsArr = (MBufRawPacket**)packetStorage; - return packetsArr[index]->getMBuf(); +static rte_mbuf* getNextPacketFromMBufRawPacketArray(void* packetStorage, + int index) { + MBufRawPacket** packetsArr = (MBufRawPacket**)packetStorage; + return packetsArr[index]->getMBuf(); } -static rte_mbuf* getNextPacketFromMBufArray(void* packetStorage, int index) -{ - rte_mbuf** mbufArr = (rte_mbuf**)packetStorage; - return mbufArr[index]; +static rte_mbuf* getNextPacketFromMBufArray(void* packetStorage, int index) { + rte_mbuf** mbufArr = (rte_mbuf**)packetStorage; + return mbufArr[index]; } -static rte_mbuf* getNextPacketFromMBufRawPacketVec(void* packetStorage, int index) -{ - MBufRawPacketVector* packetVec = (MBufRawPacketVector*)packetStorage; - return packetVec->at(index)->getMBuf(); +static rte_mbuf* getNextPacketFromMBufRawPacketVec(void* packetStorage, + int index) { + MBufRawPacketVector* packetVec = (MBufRawPacketVector*)packetStorage; + return packetVec->at(index)->getMBuf(); } -static rte_mbuf* getNextPacketFromMBufRawPacket(void* packetStorage, int index) -{ - MBufRawPacket* mbufRawPacket = (MBufRawPacket*)packetStorage; - return mbufRawPacket->getMBuf(); +static rte_mbuf* getNextPacketFromMBufRawPacket(void* packetStorage, + int index) { + MBufRawPacket* mbufRawPacket = (MBufRawPacket*)packetStorage; + return mbufRawPacket->getMBuf(); } -uint16_t DpdkDevice::sendPacketsInner(uint16_t txQueueId, void* packetStorage, PacketIterator iter, int arrLength, bool useTxBuffer) -{ - if (unlikely(!m_DeviceOpened)) - { - PCPP_LOG_ERROR("Device '" << m_DeviceName << "' not opened!"); - return 0; - } - - if (unlikely(txQueueId >= m_NumOfTxQueuesOpened)) - { - PCPP_LOG_ERROR("TX queue " << txQueueId << " isn't opened in device"); - return 0; - } - - rte_mbuf* mBufArr[MAX_BURST_SIZE]; - - int packetIndex = 0; - int mBufArrIndex = 0; - uint16_t packetsSent = 0; - int lastSleep = 0; - - #define PACKET_TRANSMISSION_THRESHOLD 0.8 - int packetTxThreshold = m_Config.transmitDescriptorsNumber*PACKET_TRANSMISSION_THRESHOLD; - - while (packetIndex < arrLength) - { - rte_mbuf* mBuf = iter(packetStorage, packetIndex); - - if (useTxBuffer) - { - packetsSent += rte_eth_tx_buffer(m_Id, txQueueId, m_TxBuffers[txQueueId], mBuf); - } - else - { - mBufArr[mBufArrIndex++] = mBuf; - - if (unlikely(mBufArrIndex == MAX_BURST_SIZE)) - { - packetsSent += rte_eth_tx_burst(m_Id, txQueueId, mBufArr, MAX_BURST_SIZE); - mBufArrIndex = 0; - - if (unlikely((packetsSent - lastSleep) >= packetTxThreshold)) - { - PCPP_LOG_DEBUG("Since NIC couldn't send all packet in this iteration, waiting for 0.2 second for H/W descriptors to get free"); - usleep(200000); - lastSleep = packetsSent; - } - } - } - - packetIndex++; - } - - if (useTxBuffer) - { - packetsSent += flushTxBuffer(true, txQueueId); - } - else if (mBufArrIndex > 0) - { - packetsSent += rte_eth_tx_burst(m_Id, txQueueId, mBufArr, mBufArrIndex); - } - - return packetsSent; +uint16_t DpdkDevice::sendPacketsInner(uint16_t txQueueId, void* packetStorage, + PacketIterator iter, int arrLength, + bool useTxBuffer) { + if (unlikely(!m_DeviceOpened)) { + PCPP_LOG_ERROR("Device '" << m_DeviceName << "' not opened!"); + return 0; + } + + if (unlikely(txQueueId >= m_NumOfTxQueuesOpened)) { + PCPP_LOG_ERROR("TX queue " << txQueueId << " isn't opened in device"); + return 0; + } + + rte_mbuf* mBufArr[MAX_BURST_SIZE]; + + int packetIndex = 0; + int mBufArrIndex = 0; + uint16_t packetsSent = 0; + int lastSleep = 0; + +#define PACKET_TRANSMISSION_THRESHOLD 0.8 + int packetTxThreshold = + m_Config.transmitDescriptorsNumber * PACKET_TRANSMISSION_THRESHOLD; + + while (packetIndex < arrLength) { + rte_mbuf* mBuf = iter(packetStorage, packetIndex); + + if (useTxBuffer) { + packetsSent += + rte_eth_tx_buffer(m_Id, txQueueId, m_TxBuffers[txQueueId], mBuf); + } else { + mBufArr[mBufArrIndex++] = mBuf; + + if (unlikely(mBufArrIndex == MAX_BURST_SIZE)) { + packetsSent += + rte_eth_tx_burst(m_Id, txQueueId, mBufArr, MAX_BURST_SIZE); + mBufArrIndex = 0; + + if (unlikely((packetsSent - lastSleep) >= packetTxThreshold)) { + PCPP_LOG_DEBUG( + "Since NIC couldn't send all packet in this iteration, waiting " + "for 0.2 second for H/W descriptors to get free"); + usleep(200000); + lastSleep = packetsSent; + } + } + } + + packetIndex++; + } + + if (useTxBuffer) { + packetsSent += flushTxBuffer(true, txQueueId); + } else if (mBufArrIndex > 0) { + packetsSent += rte_eth_tx_burst(m_Id, txQueueId, mBufArr, mBufArrIndex); + } + + return packetsSent; } +uint16_t DpdkDevice::sendPackets(MBufRawPacket** rawPacketsArr, + uint16_t arrLength, uint16_t txQueueId, + bool useTxBuffer) { + uint16_t packetsSent = sendPacketsInner(txQueueId, (void*)rawPacketsArr, + getNextPacketFromMBufRawPacketArray, + arrLength, useTxBuffer); -uint16_t DpdkDevice::sendPackets(MBufRawPacket** rawPacketsArr, uint16_t arrLength, uint16_t txQueueId, bool useTxBuffer) -{ - uint16_t packetsSent = sendPacketsInner(txQueueId, (void*)rawPacketsArr, getNextPacketFromMBufRawPacketArray, arrLength, useTxBuffer); + bool needToFreeMbuf = false; + int applyForMBufs = arrLength; - bool needToFreeMbuf = false; - int applyForMBufs = arrLength; + if (unlikely(!useTxBuffer && (packetsSent != arrLength))) { + applyForMBufs = packetsSent; + } - if (unlikely(!useTxBuffer && (packetsSent != arrLength))) - { - applyForMBufs = packetsSent; - } + for (int index = 0; index < applyForMBufs; index++) + rawPacketsArr[index]->setFreeMbuf(needToFreeMbuf); - for (int index = 0; index < applyForMBufs; index++) - rawPacketsArr[index]->setFreeMbuf(needToFreeMbuf); + for (int index = applyForMBufs; index < arrLength; index++) + rawPacketsArr[index]->setFreeMbuf(!needToFreeMbuf); - for (int index = applyForMBufs; index < arrLength; index++) - rawPacketsArr[index]->setFreeMbuf(!needToFreeMbuf); - - return packetsSent; + return packetsSent; } -uint16_t DpdkDevice::sendPackets(Packet** packetsArr, uint16_t arrLength, uint16_t txQueueId, bool useTxBuffer) -{ - rte_mbuf* mBufArr[arrLength]; - MBufRawPacketVector mBufVec; - MBufRawPacket* mBufRawPacketArr[arrLength]; - - for (size_t i = 0; i < arrLength; i++) - { - MBufRawPacket* rawPacket = NULL; - uint8_t rawPacketType = packetsArr[i]->getRawPacketReadOnly()->getObjectType(); - if (rawPacketType != MBUFRAWPACKET_OBJECT_TYPE) - { - rawPacket = new MBufRawPacket(); - if (unlikely(!rawPacket->initFromRawPacket(packetsArr[i]->getRawPacketReadOnly(), this))) - { - delete rawPacket; - return 0; - } - - mBufVec.pushBack(rawPacket); - } - else - { - rawPacket = (MBufRawPacket*)packetsArr[i]->getRawPacketReadOnly(); - } - - mBufArr[i] = rawPacket->getMBuf(); - mBufRawPacketArr[i] = rawPacket; - } - - uint16_t packetsSent = sendPacketsInner(txQueueId, (void*)mBufArr, getNextPacketFromMBufArray, arrLength, useTxBuffer); - - bool needToFreeMbuf = (!useTxBuffer && (packetsSent != arrLength)); - for (int index = 0; index < arrLength; index++) - mBufRawPacketArr[index]->setFreeMbuf(needToFreeMbuf); - - return packetsSent; +uint16_t DpdkDevice::sendPackets(Packet** packetsArr, uint16_t arrLength, + uint16_t txQueueId, bool useTxBuffer) { + rte_mbuf* mBufArr[arrLength]; + MBufRawPacketVector mBufVec; + MBufRawPacket* mBufRawPacketArr[arrLength]; + + for (size_t i = 0; i < arrLength; i++) { + MBufRawPacket* rawPacket = NULL; + uint8_t rawPacketType = + packetsArr[i]->getRawPacketReadOnly()->getObjectType(); + if (rawPacketType != MBUFRAWPACKET_OBJECT_TYPE) { + rawPacket = new MBufRawPacket(); + if (unlikely(!rawPacket->initFromRawPacket( + packetsArr[i]->getRawPacketReadOnly(), this))) { + delete rawPacket; + return 0; + } + + mBufVec.pushBack(rawPacket); + } else { + rawPacket = (MBufRawPacket*)packetsArr[i]->getRawPacketReadOnly(); + } + + mBufArr[i] = rawPacket->getMBuf(); + mBufRawPacketArr[i] = rawPacket; + } + + uint16_t packetsSent = + sendPacketsInner(txQueueId, (void*)mBufArr, getNextPacketFromMBufArray, + arrLength, useTxBuffer); + + bool needToFreeMbuf = (!useTxBuffer && (packetsSent != arrLength)); + for (int index = 0; index < arrLength; index++) + mBufRawPacketArr[index]->setFreeMbuf(needToFreeMbuf); + + return packetsSent; } -uint16_t DpdkDevice::sendPackets(RawPacketVector& rawPacketsVec, uint16_t txQueueId, bool useTxBuffer) -{ - size_t vecSize = rawPacketsVec.size(); - rte_mbuf* mBufArr[vecSize]; - MBufRawPacket* mBufRawPacketArr[vecSize]; - MBufRawPacketVector mBufVec; - int mBufIndex = 0; - - for (RawPacketVector::ConstVectorIterator iter = rawPacketsVec.begin(); iter != rawPacketsVec.end(); iter++) - { - MBufRawPacket* rawPacket = NULL; - uint8_t rawPacketType = (*iter)->getObjectType(); - if (rawPacketType != MBUFRAWPACKET_OBJECT_TYPE) - { - rawPacket = new MBufRawPacket(); - if (unlikely(!rawPacket->initFromRawPacket(*iter, this))) - { - delete rawPacket; - return 0; - } - - mBufVec.pushBack(rawPacket); - } - else - { - rawPacket = (MBufRawPacket*)(*iter); - } - - mBufRawPacketArr[mBufIndex] = rawPacket; - mBufArr[mBufIndex++] = rawPacket->getMBuf(); - } - - uint16_t packetsSent = sendPacketsInner(txQueueId, (void*)mBufArr, getNextPacketFromMBufArray, vecSize, useTxBuffer); - - bool needToFreeMbuf = (!useTxBuffer && (packetsSent != vecSize)); - for (size_t index = 0; index < rawPacketsVec.size(); index++) - mBufRawPacketArr[index]->setFreeMbuf(needToFreeMbuf); - - return packetsSent; +uint16_t DpdkDevice::sendPackets(RawPacketVector& rawPacketsVec, + uint16_t txQueueId, bool useTxBuffer) { + size_t vecSize = rawPacketsVec.size(); + rte_mbuf* mBufArr[vecSize]; + MBufRawPacket* mBufRawPacketArr[vecSize]; + MBufRawPacketVector mBufVec; + int mBufIndex = 0; + + for (RawPacketVector::ConstVectorIterator iter = rawPacketsVec.begin(); + iter != rawPacketsVec.end(); iter++) { + MBufRawPacket* rawPacket = NULL; + uint8_t rawPacketType = (*iter)->getObjectType(); + if (rawPacketType != MBUFRAWPACKET_OBJECT_TYPE) { + rawPacket = new MBufRawPacket(); + if (unlikely(!rawPacket->initFromRawPacket(*iter, this))) { + delete rawPacket; + return 0; + } + + mBufVec.pushBack(rawPacket); + } else { + rawPacket = (MBufRawPacket*)(*iter); + } + + mBufRawPacketArr[mBufIndex] = rawPacket; + mBufArr[mBufIndex++] = rawPacket->getMBuf(); + } + + uint16_t packetsSent = + sendPacketsInner(txQueueId, (void*)mBufArr, getNextPacketFromMBufArray, + vecSize, useTxBuffer); + + bool needToFreeMbuf = (!useTxBuffer && (packetsSent != vecSize)); + for (size_t index = 0; index < rawPacketsVec.size(); index++) + mBufRawPacketArr[index]->setFreeMbuf(needToFreeMbuf); + + return packetsSent; } -uint16_t DpdkDevice::sendPackets(MBufRawPacketVector& rawPacketsVec, uint16_t txQueueId, bool useTxBuffer) -{ - size_t vecSize = rawPacketsVec.size(); - uint16_t packetsSent = sendPacketsInner(txQueueId, (void*)(&rawPacketsVec), getNextPacketFromMBufRawPacketVec, vecSize, useTxBuffer); +uint16_t DpdkDevice::sendPackets(MBufRawPacketVector& rawPacketsVec, + uint16_t txQueueId, bool useTxBuffer) { + size_t vecSize = rawPacketsVec.size(); + uint16_t packetsSent = + sendPacketsInner(txQueueId, (void*)(&rawPacketsVec), + getNextPacketFromMBufRawPacketVec, vecSize, useTxBuffer); - bool needToFreeMbuf = (!useTxBuffer && (packetsSent != vecSize)); + bool needToFreeMbuf = (!useTxBuffer && (packetsSent != vecSize)); - for (size_t index = 0; index < vecSize; index++) - rawPacketsVec.at(index)->setFreeMbuf(needToFreeMbuf); + for (size_t index = 0; index < vecSize; index++) + rawPacketsVec.at(index)->setFreeMbuf(needToFreeMbuf); - return packetsSent; + return packetsSent; } -bool DpdkDevice::sendPacket(RawPacket& rawPacket, uint16_t txQueueId, bool useTxBuffer) -{ - uint8_t rawPacketType = rawPacket.getObjectType(); - if (rawPacketType == MBUFRAWPACKET_OBJECT_TYPE) - { - bool packetSent = (sendPacketsInner(txQueueId, (MBufRawPacket*)&rawPacket, getNextPacketFromMBufRawPacket, 1, useTxBuffer) == 1); - bool needToFreeMbuf = (!useTxBuffer && !packetSent); - ((MBufRawPacket*)&rawPacket)->setFreeMbuf(needToFreeMbuf); - return packetSent; - } - - MBufRawPacket mbufRawPacket; - if (unlikely(!mbufRawPacket.initFromRawPacket(&rawPacket, this))) - return false; - - bool packetSent = (sendPacketsInner(txQueueId, &mbufRawPacket, getNextPacketFromMBufRawPacket, 1, useTxBuffer) == 1); - bool needToFreeMbuf = (!useTxBuffer && !packetSent); - mbufRawPacket.setFreeMbuf(needToFreeMbuf); - - return packetSent; +bool DpdkDevice::sendPacket(RawPacket& rawPacket, uint16_t txQueueId, + bool useTxBuffer) { + uint8_t rawPacketType = rawPacket.getObjectType(); + if (rawPacketType == MBUFRAWPACKET_OBJECT_TYPE) { + bool packetSent = + (sendPacketsInner(txQueueId, (MBufRawPacket*)&rawPacket, + getNextPacketFromMBufRawPacket, 1, useTxBuffer) == 1); + bool needToFreeMbuf = (!useTxBuffer && !packetSent); + ((MBufRawPacket*)&rawPacket)->setFreeMbuf(needToFreeMbuf); + return packetSent; + } + + MBufRawPacket mbufRawPacket; + if (unlikely(!mbufRawPacket.initFromRawPacket(&rawPacket, this))) + return false; + + bool packetSent = + (sendPacketsInner(txQueueId, &mbufRawPacket, + getNextPacketFromMBufRawPacket, 1, useTxBuffer) == 1); + bool needToFreeMbuf = (!useTxBuffer && !packetSent); + mbufRawPacket.setFreeMbuf(needToFreeMbuf); + + return packetSent; } -bool DpdkDevice::sendPacket(MBufRawPacket& rawPacket, uint16_t txQueueId, bool useTxBuffer) -{ - bool packetSent = (sendPacketsInner(txQueueId, &rawPacket, getNextPacketFromMBufRawPacket, 1, useTxBuffer) == 1); - bool needToFreeMbuf = (!useTxBuffer && !packetSent); - rawPacket.setFreeMbuf(needToFreeMbuf); - return packetSent; +bool DpdkDevice::sendPacket(MBufRawPacket& rawPacket, uint16_t txQueueId, + bool useTxBuffer) { + bool packetSent = + (sendPacketsInner(txQueueId, &rawPacket, getNextPacketFromMBufRawPacket, + 1, useTxBuffer) == 1); + bool needToFreeMbuf = (!useTxBuffer && !packetSent); + rawPacket.setFreeMbuf(needToFreeMbuf); + return packetSent; } -bool DpdkDevice::sendPacket(Packet& packet, uint16_t txQueueId, bool useTxBuffer) -{ - return sendPacket(*(packet.getRawPacket()), txQueueId); +bool DpdkDevice::sendPacket(Packet& packet, uint16_t txQueueId, + bool useTxBuffer) { + return sendPacket(*(packet.getRawPacket()), txQueueId); } -int DpdkDevice::getAmountOfFreeMbufs() const -{ - return (int)rte_mempool_avail_count(m_MBufMempool); +int DpdkDevice::getAmountOfFreeMbufs() const { + return (int)rte_mempool_avail_count(m_MBufMempool); } -int DpdkDevice::getAmountOfMbufsInUse() const -{ - return (int)rte_mempool_in_use_count(m_MBufMempool); +int DpdkDevice::getAmountOfMbufsInUse() const { + return (int)rte_mempool_in_use_count(m_MBufMempool); } -uint64_t DpdkDevice::convertRssHfToDpdkRssHf(uint64_t rssHF) const -{ - if (rssHF == (uint64_t)-1) - { - rte_eth_dev_info devInfo; - rte_eth_dev_info_get(m_Id, &devInfo); - return devInfo.flow_type_rss_offloads; - } +uint64_t DpdkDevice::convertRssHfToDpdkRssHf(uint64_t rssHF) const { + if (rssHF == (uint64_t)-1) { + rte_eth_dev_info devInfo; + rte_eth_dev_info_get(m_Id, &devInfo); + return devInfo.flow_type_rss_offloads; + } - uint64_t dpdkRssHF = 0; + uint64_t dpdkRssHF = 0; - if ((rssHF & RSS_IPV4) != 0) - dpdkRssHF |= DPDK_CONFIG_ETH_RSS_IPV4; + if ((rssHF & RSS_IPV4) != 0) + dpdkRssHF |= DPDK_CONFIG_ETH_RSS_IPV4; - if ((rssHF & RSS_FRAG_IPV4) != 0) - dpdkRssHF |= DPDK_CONFIG_ETH_RSS_FRAG_IPV4; + if ((rssHF & RSS_FRAG_IPV4) != 0) + dpdkRssHF |= DPDK_CONFIG_ETH_RSS_FRAG_IPV4; - if ((rssHF & RSS_NONFRAG_IPV4_TCP) != 0) - dpdkRssHF |= DPDK_CONFIG_ETH_RSS_NONFRAG_IPV4_TCP; + if ((rssHF & RSS_NONFRAG_IPV4_TCP) != 0) + dpdkRssHF |= DPDK_CONFIG_ETH_RSS_NONFRAG_IPV4_TCP; - if ((rssHF & RSS_NONFRAG_IPV4_UDP) != 0) - dpdkRssHF |= DPDK_CONFIG_ETH_RSS_NONFRAG_IPV4_UDP; + if ((rssHF & RSS_NONFRAG_IPV4_UDP) != 0) + dpdkRssHF |= DPDK_CONFIG_ETH_RSS_NONFRAG_IPV4_UDP; - if ((rssHF & RSS_NONFRAG_IPV4_SCTP) != 0) - dpdkRssHF |= DPDK_CONFIG_ETH_RSS_NONFRAG_IPV4_SCTP; + if ((rssHF & RSS_NONFRAG_IPV4_SCTP) != 0) + dpdkRssHF |= DPDK_CONFIG_ETH_RSS_NONFRAG_IPV4_SCTP; - if ((rssHF & RSS_NONFRAG_IPV4_OTHER) != 0) - dpdkRssHF |= DPDK_CONFIG_ETH_RSS_NONFRAG_IPV4_OTHER; + if ((rssHF & RSS_NONFRAG_IPV4_OTHER) != 0) + dpdkRssHF |= DPDK_CONFIG_ETH_RSS_NONFRAG_IPV4_OTHER; - if ((rssHF & RSS_IPV6) != 0) - dpdkRssHF |= DPDK_CONFIG_ETH_RSS_IPV6; + if ((rssHF & RSS_IPV6) != 0) + dpdkRssHF |= DPDK_CONFIG_ETH_RSS_IPV6; - if ((rssHF & RSS_FRAG_IPV6) != 0) - dpdkRssHF |= DPDK_CONFIG_ETH_RSS_FRAG_IPV6; + if ((rssHF & RSS_FRAG_IPV6) != 0) + dpdkRssHF |= DPDK_CONFIG_ETH_RSS_FRAG_IPV6; - if ((rssHF & RSS_NONFRAG_IPV6_TCP) != 0) - dpdkRssHF |= DPDK_CONFIG_ETH_RSS_NONFRAG_IPV6_TCP; + if ((rssHF & RSS_NONFRAG_IPV6_TCP) != 0) + dpdkRssHF |= DPDK_CONFIG_ETH_RSS_NONFRAG_IPV6_TCP; - if ((rssHF & RSS_NONFRAG_IPV6_UDP) != 0) - dpdkRssHF |= DPDK_CONFIG_ETH_RSS_NONFRAG_IPV6_UDP; + if ((rssHF & RSS_NONFRAG_IPV6_UDP) != 0) + dpdkRssHF |= DPDK_CONFIG_ETH_RSS_NONFRAG_IPV6_UDP; - if ((rssHF & RSS_NONFRAG_IPV6_SCTP) != 0) - dpdkRssHF |= DPDK_CONFIG_ETH_RSS_NONFRAG_IPV6_SCTP; + if ((rssHF & RSS_NONFRAG_IPV6_SCTP) != 0) + dpdkRssHF |= DPDK_CONFIG_ETH_RSS_NONFRAG_IPV6_SCTP; - if ((rssHF & RSS_NONFRAG_IPV6_OTHER) != 0) - dpdkRssHF |= DPDK_CONFIG_ETH_RSS_NONFRAG_IPV6_OTHER; + if ((rssHF & RSS_NONFRAG_IPV6_OTHER) != 0) + dpdkRssHF |= DPDK_CONFIG_ETH_RSS_NONFRAG_IPV6_OTHER; - if ((rssHF & RSS_L2_PAYLOAD) != 0) - dpdkRssHF |= DPDK_CONFIG_ETH_RSS_L2_PAYLOAD; + if ((rssHF & RSS_L2_PAYLOAD) != 0) + dpdkRssHF |= DPDK_CONFIG_ETH_RSS_L2_PAYLOAD; - if ((rssHF & RSS_IPV6_EX) != 0) - dpdkRssHF |= DPDK_CONFIG_ETH_RSS_IPV6_EX; + if ((rssHF & RSS_IPV6_EX) != 0) + dpdkRssHF |= DPDK_CONFIG_ETH_RSS_IPV6_EX; - if ((rssHF & RSS_IPV6_TCP_EX) != 0) - dpdkRssHF |= DPDK_CONFIG_ETH_RSS_IPV6_TCP_EX; + if ((rssHF & RSS_IPV6_TCP_EX) != 0) + dpdkRssHF |= DPDK_CONFIG_ETH_RSS_IPV6_TCP_EX; - if ((rssHF & RSS_IPV6_UDP_EX) != 0) - dpdkRssHF |= DPDK_CONFIG_ETH_RSS_IPV6_UDP_EX; + if ((rssHF & RSS_IPV6_UDP_EX) != 0) + dpdkRssHF |= DPDK_CONFIG_ETH_RSS_IPV6_UDP_EX; - if ((rssHF & RSS_PORT) != 0) - dpdkRssHF |= DPDK_CONFIG_ETH_RSS_PORT; + if ((rssHF & RSS_PORT) != 0) + dpdkRssHF |= DPDK_CONFIG_ETH_RSS_PORT; - if ((rssHF & RSS_VXLAN) != 0) - dpdkRssHF |= DPDK_CONFIG_ETH_RSS_VXLAN; + if ((rssHF & RSS_VXLAN) != 0) + dpdkRssHF |= DPDK_CONFIG_ETH_RSS_VXLAN; - if ((rssHF & RSS_GENEVE) != 0) - dpdkRssHF |= DPDK_CONFIG_ETH_RSS_GENEVE; + if ((rssHF & RSS_GENEVE) != 0) + dpdkRssHF |= DPDK_CONFIG_ETH_RSS_GENEVE; - if ((rssHF & RSS_NVGRE) != 0) - dpdkRssHF |= DPDK_CONFIG_ETH_RSS_NVGRE; + if ((rssHF & RSS_NVGRE) != 0) + dpdkRssHF |= DPDK_CONFIG_ETH_RSS_NVGRE; - return dpdkRssHF; + return dpdkRssHF; } -uint64_t DpdkDevice::convertDpdkRssHfToRssHf(uint64_t dpdkRssHF) const -{ - uint64_t rssHF = 0; +uint64_t DpdkDevice::convertDpdkRssHfToRssHf(uint64_t dpdkRssHF) const { + uint64_t rssHF = 0; - if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_IPV4) != 0) - rssHF |= RSS_IPV4; + if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_IPV4) != 0) + rssHF |= RSS_IPV4; - if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_FRAG_IPV4) != 0) - rssHF |= RSS_FRAG_IPV4; + if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_FRAG_IPV4) != 0) + rssHF |= RSS_FRAG_IPV4; - if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_NONFRAG_IPV4_TCP) != 0) - rssHF |= RSS_NONFRAG_IPV4_TCP; + if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_NONFRAG_IPV4_TCP) != 0) + rssHF |= RSS_NONFRAG_IPV4_TCP; - if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_NONFRAG_IPV4_UDP) != 0) - rssHF |= RSS_NONFRAG_IPV4_UDP; + if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_NONFRAG_IPV4_UDP) != 0) + rssHF |= RSS_NONFRAG_IPV4_UDP; - if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_NONFRAG_IPV4_SCTP) != 0) - rssHF |= RSS_NONFRAG_IPV4_SCTP; + if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_NONFRAG_IPV4_SCTP) != 0) + rssHF |= RSS_NONFRAG_IPV4_SCTP; - if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_NONFRAG_IPV4_OTHER) != 0) - rssHF |= RSS_NONFRAG_IPV4_OTHER; + if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_NONFRAG_IPV4_OTHER) != 0) + rssHF |= RSS_NONFRAG_IPV4_OTHER; - if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_IPV6) != 0) - rssHF |= RSS_IPV6; + if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_IPV6) != 0) + rssHF |= RSS_IPV6; - if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_FRAG_IPV6) != 0) - rssHF |= RSS_FRAG_IPV6; + if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_FRAG_IPV6) != 0) + rssHF |= RSS_FRAG_IPV6; - if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_NONFRAG_IPV6_TCP) != 0) - rssHF |= RSS_NONFRAG_IPV6_TCP; + if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_NONFRAG_IPV6_TCP) != 0) + rssHF |= RSS_NONFRAG_IPV6_TCP; - if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_NONFRAG_IPV6_UDP) != 0) - rssHF |= RSS_NONFRAG_IPV6_UDP; + if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_NONFRAG_IPV6_UDP) != 0) + rssHF |= RSS_NONFRAG_IPV6_UDP; - if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_NONFRAG_IPV6_SCTP) != 0) - rssHF |= RSS_NONFRAG_IPV6_SCTP; + if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_NONFRAG_IPV6_SCTP) != 0) + rssHF |= RSS_NONFRAG_IPV6_SCTP; - if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_NONFRAG_IPV6_OTHER) != 0) - rssHF |= RSS_NONFRAG_IPV6_OTHER; + if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_NONFRAG_IPV6_OTHER) != 0) + rssHF |= RSS_NONFRAG_IPV6_OTHER; - if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_L2_PAYLOAD) != 0) - rssHF |= RSS_L2_PAYLOAD; + if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_L2_PAYLOAD) != 0) + rssHF |= RSS_L2_PAYLOAD; - if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_IPV6_EX) != 0) - rssHF |= RSS_IPV6_EX; + if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_IPV6_EX) != 0) + rssHF |= RSS_IPV6_EX; - if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_IPV6_TCP_EX) != 0) - rssHF |= RSS_IPV6_TCP_EX; + if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_IPV6_TCP_EX) != 0) + rssHF |= RSS_IPV6_TCP_EX; - if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_IPV6_UDP_EX) != 0) - rssHF |= RSS_IPV6_UDP_EX; + if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_IPV6_UDP_EX) != 0) + rssHF |= RSS_IPV6_UDP_EX; - if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_PORT) != 0) - rssHF |= RSS_PORT; + if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_PORT) != 0) + rssHF |= RSS_PORT; - if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_VXLAN) != 0) - rssHF |= RSS_VXLAN; + if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_VXLAN) != 0) + rssHF |= RSS_VXLAN; - if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_GENEVE) != 0) - rssHF |= RSS_GENEVE; + if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_GENEVE) != 0) + rssHF |= RSS_GENEVE; - if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_NVGRE) != 0) - rssHF |= RSS_NVGRE; + if ((dpdkRssHF & DPDK_CONFIG_ETH_RSS_NVGRE) != 0) + rssHF |= RSS_NVGRE; - return rssHF; + return rssHF; } -bool DpdkDevice::isDeviceSupportRssHashFunction(DpdkRssHashFunction rssHF) const -{ - return isDeviceSupportRssHashFunction((uint64_t)rssHF); +bool DpdkDevice::isDeviceSupportRssHashFunction( + DpdkRssHashFunction rssHF) const { + return isDeviceSupportRssHashFunction((uint64_t)rssHF); } -bool DpdkDevice::isDeviceSupportRssHashFunction(uint64_t rssHFMask) const -{ - uint64_t dpdkRssHF = convertRssHfToDpdkRssHf(rssHFMask); +bool DpdkDevice::isDeviceSupportRssHashFunction(uint64_t rssHFMask) const { + uint64_t dpdkRssHF = convertRssHfToDpdkRssHf(rssHFMask); - rte_eth_dev_info devInfo; - rte_eth_dev_info_get(m_Id, &devInfo); + rte_eth_dev_info devInfo; + rte_eth_dev_info_get(m_Id, &devInfo); - return ((devInfo.flow_type_rss_offloads & dpdkRssHF) == dpdkRssHF); + return ((devInfo.flow_type_rss_offloads & dpdkRssHF) == dpdkRssHF); } -uint64_t DpdkDevice::getSupportedRssHashFunctions() const -{ - rte_eth_dev_info devInfo; - rte_eth_dev_info_get(m_Id, &devInfo); +uint64_t DpdkDevice::getSupportedRssHashFunctions() const { + rte_eth_dev_info devInfo; + rte_eth_dev_info_get(m_Id, &devInfo); - return convertDpdkRssHfToRssHf(devInfo.flow_type_rss_offloads); + return convertDpdkRssHfToRssHf(devInfo.flow_type_rss_offloads); } -uint64_t DpdkDevice::getConfiguredRssHashFunction() const -{ - if (m_Config.rssHashFunction == static_cast(RSS_DEFAULT)) - { - if (m_PMDType == PMD_I40E || m_PMDType == PMD_I40EVF) - { - return RSS_NONFRAG_IPV4_TCP | RSS_NONFRAG_IPV4_UDP | RSS_NONFRAG_IPV4_OTHER | RSS_FRAG_IPV4 | RSS_NONFRAG_IPV6_TCP | RSS_NONFRAG_IPV6_UDP | RSS_NONFRAG_IPV6_OTHER | RSS_FRAG_IPV6; - } - else - { - return RSS_IPV4 | RSS_IPV6; - } - - } - - if (m_Config.rssHashFunction == static_cast(RSS_ALL_SUPPORTED)) - { - return getSupportedRssHashFunctions(); - } - - return m_Config.rssHashFunction; +uint64_t DpdkDevice::getConfiguredRssHashFunction() const { + if (m_Config.rssHashFunction == static_cast(RSS_DEFAULT)) { + if (m_PMDType == PMD_I40E || m_PMDType == PMD_I40EVF) { + return RSS_NONFRAG_IPV4_TCP | RSS_NONFRAG_IPV4_UDP | + RSS_NONFRAG_IPV4_OTHER | RSS_FRAG_IPV4 | RSS_NONFRAG_IPV6_TCP | + RSS_NONFRAG_IPV6_UDP | RSS_NONFRAG_IPV6_OTHER | RSS_FRAG_IPV6; + } else { + return RSS_IPV4 | RSS_IPV6; + } + } + + if (m_Config.rssHashFunction == static_cast(RSS_ALL_SUPPORTED)) { + return getSupportedRssHashFunctions(); + } + + return m_Config.rssHashFunction; } -std::vector DpdkDevice::rssHashFunctionMaskToString(uint64_t rssHFMask) const -{ - std::vector result = std::vector(); +std::vector +DpdkDevice::rssHashFunctionMaskToString(uint64_t rssHFMask) const { + std::vector result = std::vector(); - if (rssHFMask == RSS_NONE) - { - result.push_back("RSS_NONE"); - return result; - } + if (rssHFMask == RSS_NONE) { + result.push_back("RSS_NONE"); + return result; + } - if ((rssHFMask & RSS_IPV4) != 0) - result.push_back("RSS_IPV4"); + if ((rssHFMask & RSS_IPV4) != 0) + result.push_back("RSS_IPV4"); - if ((rssHFMask & RSS_FRAG_IPV4) != 0) - result.push_back("RSS_FRAG_IPV4"); + if ((rssHFMask & RSS_FRAG_IPV4) != 0) + result.push_back("RSS_FRAG_IPV4"); - if ((rssHFMask & RSS_NONFRAG_IPV4_TCP) != 0) - result.push_back("RSS_NONFRAG_IPV4_TCP"); + if ((rssHFMask & RSS_NONFRAG_IPV4_TCP) != 0) + result.push_back("RSS_NONFRAG_IPV4_TCP"); - if ((rssHFMask & RSS_NONFRAG_IPV4_UDP) != 0) - result.push_back("RSS_NONFRAG_IPV4_UDP"); + if ((rssHFMask & RSS_NONFRAG_IPV4_UDP) != 0) + result.push_back("RSS_NONFRAG_IPV4_UDP"); - if ((rssHFMask & RSS_NONFRAG_IPV4_SCTP) != 0) - result.push_back("RSS_NONFRAG_IPV4_SCTP"); + if ((rssHFMask & RSS_NONFRAG_IPV4_SCTP) != 0) + result.push_back("RSS_NONFRAG_IPV4_SCTP"); - if ((rssHFMask & RSS_NONFRAG_IPV4_OTHER) != 0) - result.push_back("RSS_NONFRAG_IPV4_OTHER"); + if ((rssHFMask & RSS_NONFRAG_IPV4_OTHER) != 0) + result.push_back("RSS_NONFRAG_IPV4_OTHER"); - if ((rssHFMask & RSS_IPV6) != 0) - result.push_back("RSS_IPV6"); + if ((rssHFMask & RSS_IPV6) != 0) + result.push_back("RSS_IPV6"); - if ((rssHFMask & RSS_FRAG_IPV6) != 0) - result.push_back("RSS_FRAG_IPV6"); + if ((rssHFMask & RSS_FRAG_IPV6) != 0) + result.push_back("RSS_FRAG_IPV6"); - if ((rssHFMask & RSS_NONFRAG_IPV6_TCP) != 0) - result.push_back("RSS_NONFRAG_IPV6_TCP"); + if ((rssHFMask & RSS_NONFRAG_IPV6_TCP) != 0) + result.push_back("RSS_NONFRAG_IPV6_TCP"); - if ((rssHFMask & RSS_NONFRAG_IPV6_UDP) != 0) - result.push_back("RSS_NONFRAG_IPV6_UDP"); + if ((rssHFMask & RSS_NONFRAG_IPV6_UDP) != 0) + result.push_back("RSS_NONFRAG_IPV6_UDP"); - if ((rssHFMask & RSS_NONFRAG_IPV6_SCTP) != 0) - result.push_back("RSS_NONFRAG_IPV6_SCTP"); + if ((rssHFMask & RSS_NONFRAG_IPV6_SCTP) != 0) + result.push_back("RSS_NONFRAG_IPV6_SCTP"); - if ((rssHFMask & RSS_NONFRAG_IPV6_OTHER) != 0) - result.push_back("RSS_NONFRAG_IPV6_OTHER"); + if ((rssHFMask & RSS_NONFRAG_IPV6_OTHER) != 0) + result.push_back("RSS_NONFRAG_IPV6_OTHER"); - if ((rssHFMask & RSS_L2_PAYLOAD) != 0) - result.push_back("RSS_L2_PAYLOAD"); + if ((rssHFMask & RSS_L2_PAYLOAD) != 0) + result.push_back("RSS_L2_PAYLOAD"); - if ((rssHFMask & RSS_IPV6_EX) != 0) - result.push_back("RSS_IPV6_EX"); + if ((rssHFMask & RSS_IPV6_EX) != 0) + result.push_back("RSS_IPV6_EX"); - if ((rssHFMask & RSS_IPV6_TCP_EX) != 0) - result.push_back("RSS_IPV6_TCP_EX"); + if ((rssHFMask & RSS_IPV6_TCP_EX) != 0) + result.push_back("RSS_IPV6_TCP_EX"); - if ((rssHFMask & RSS_IPV6_UDP_EX) != 0) - result.push_back("RSS_IPV6_UDP_EX"); + if ((rssHFMask & RSS_IPV6_UDP_EX) != 0) + result.push_back("RSS_IPV6_UDP_EX"); - if ((rssHFMask & RSS_PORT) != 0) - result.push_back("RSS_PORT"); + if ((rssHFMask & RSS_PORT) != 0) + result.push_back("RSS_PORT"); - if ((rssHFMask & RSS_VXLAN) != 0) - result.push_back("RSS_VXLAN"); + if ((rssHFMask & RSS_VXLAN) != 0) + result.push_back("RSS_VXLAN"); - if ((rssHFMask & RSS_GENEVE) != 0) - result.push_back("RSS_GENEVE"); + if ((rssHFMask & RSS_GENEVE) != 0) + result.push_back("RSS_GENEVE"); - if ((rssHFMask & RSS_NVGRE) != 0) - result.push_back("RSS_NVGRE"); + if ((rssHFMask & RSS_NVGRE) != 0) + result.push_back("RSS_NVGRE"); - return result; + return result; } - } // namespace pcpp // GCOVR_EXCL_STOP diff --git a/Pcap++/src/DpdkDeviceList.cpp b/Pcap++/src/DpdkDeviceList.cpp index 063ae58aa6..5dab8eb905 100644 --- a/Pcap++/src/DpdkDeviceList.cpp +++ b/Pcap++/src/DpdkDeviceList.cpp @@ -10,36 +10,35 @@ #include "DpdkDeviceList.h" #include "Logger.h" -#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include -#include +#include #include +#include +#include #include -#include +#include #include -#include -#include -#include #include -#include -#include -#include -#include -#include #include -#include -#include -#include #include -#include -#include #include -#include +#include #include +#include #include -#include #include #if (RTE_VER_YEAR < 21) || (RTE_VER_YEAR == 21 && RTE_VER_MONTH < 11) @@ -50,375 +49,351 @@ #define MASTER_LCORE "--main-lcore" #endif -namespace pcpp -{ +namespace pcpp { bool DpdkDeviceList::m_IsDpdkInitialized = false; CoreMask DpdkDeviceList::m_CoreMask = 0; uint32_t DpdkDeviceList::m_MBufPoolSizePerDevice = 0; -DpdkDeviceList::DpdkDeviceList() -{ - m_IsInitialized = false; -} +DpdkDeviceList::DpdkDeviceList() { m_IsInitialized = false; } -DpdkDeviceList::~DpdkDeviceList() -{ - for (std::vector::iterator iter = m_DpdkDeviceList.begin(); iter != m_DpdkDeviceList.end(); iter++) - { - delete (*iter); - } +DpdkDeviceList::~DpdkDeviceList() { + for (std::vector::iterator iter = m_DpdkDeviceList.begin(); + iter != m_DpdkDeviceList.end(); iter++) { + delete (*iter); + } - m_DpdkDeviceList.clear(); + m_DpdkDeviceList.clear(); } -bool DpdkDeviceList::initDpdk(CoreMask coreMask, uint32_t mBufPoolSizePerDevice, uint8_t masterCore, uint32_t initDpdkArgc, char **initDpdkArgv, const std::string& appName) -{ - char **initDpdkArgvBuffer; - - if (m_IsDpdkInitialized) - { - if (coreMask == m_CoreMask) - return true; - else - { - PCPP_LOG_ERROR("Trying to re-initialize DPDK with a different core mask"); - return false; - } - } - - if (!verifyHugePagesAndDpdkDriver()) - { - return false; - } - - // verify mBufPoolSizePerDevice is power of 2 minus 1 - bool isPoolSizePowerOfTwoMinusOne = !(mBufPoolSizePerDevice == 0) && !((mBufPoolSizePerDevice+1) & (mBufPoolSizePerDevice)); - if (!isPoolSizePowerOfTwoMinusOne) - { - PCPP_LOG_ERROR("mBuf pool size must be a power of two minus one: n = (2^q - 1). It's currently: " << mBufPoolSizePerDevice); - return false; - } - - - std::stringstream dpdkParamsStream; - dpdkParamsStream << appName << " "; - dpdkParamsStream << "-n "; - dpdkParamsStream << "2 "; - dpdkParamsStream << "-c "; - dpdkParamsStream << "0x" << std::hex << std::setw(2) << std::setfill('0') << coreMask << " "; - dpdkParamsStream << MASTER_LCORE << " "; - dpdkParamsStream << (int)masterCore << " "; - - uint32_t i = 0; - while (i < initDpdkArgc && initDpdkArgv[i] != NULL) - { - dpdkParamsStream << initDpdkArgv[i] << " "; - i++; - } - - // Should be equal to the number of static params - initDpdkArgc += 7; - std::string dpdkParamsArray[initDpdkArgc]; - initDpdkArgvBuffer = new char*[initDpdkArgc]; - i = 0; - while (dpdkParamsStream.good() && i < initDpdkArgc) - { - dpdkParamsStream >> dpdkParamsArray[i]; - initDpdkArgvBuffer[i] = new char[dpdkParamsArray[i].length() + 1]; - strcpy(initDpdkArgvBuffer[i], dpdkParamsArray[i].c_str()); - i++; - } - - char* lastParam = initDpdkArgvBuffer[i-1]; - - for (i = 0; i < initDpdkArgc; i++) - { - PCPP_LOG_DEBUG("DPDK initialization params: " << initDpdkArgvBuffer[i]); - } - - optind = 1; - // init the EAL - int ret = rte_eal_init(initDpdkArgc, (char**)initDpdkArgvBuffer); - if (ret < 0) - { - PCPP_LOG_ERROR("failed to init the DPDK EAL"); - return false; - } - - for (i = 0; i < initDpdkArgc-1; i++) - { - delete [] initDpdkArgvBuffer[i]; - } - delete [] lastParam; - - delete [] initDpdkArgvBuffer; - - m_CoreMask = coreMask; - m_IsDpdkInitialized = true; - - m_MBufPoolSizePerDevice = mBufPoolSizePerDevice; - DpdkDeviceList::getInstance().setDpdkLogLevel(Logger::Info); - return DpdkDeviceList::getInstance().initDpdkDevices(m_MBufPoolSizePerDevice); +bool DpdkDeviceList::initDpdk(CoreMask coreMask, uint32_t mBufPoolSizePerDevice, + uint8_t masterCore, uint32_t initDpdkArgc, + char** initDpdkArgv, const std::string& appName) { + char** initDpdkArgvBuffer; + + if (m_IsDpdkInitialized) { + if (coreMask == m_CoreMask) + return true; + else { + PCPP_LOG_ERROR("Trying to re-initialize DPDK with a different core mask"); + return false; + } + } + + if (!verifyHugePagesAndDpdkDriver()) { + return false; + } + + // verify mBufPoolSizePerDevice is power of 2 minus 1 + bool isPoolSizePowerOfTwoMinusOne = + !(mBufPoolSizePerDevice == 0) && + !((mBufPoolSizePerDevice + 1) & (mBufPoolSizePerDevice)); + if (!isPoolSizePowerOfTwoMinusOne) { + PCPP_LOG_ERROR("mBuf pool size must be a power of two minus one: n = (2^q " + "- 1). It's currently: " + << mBufPoolSizePerDevice); + return false; + } + + std::stringstream dpdkParamsStream; + dpdkParamsStream << appName << " "; + dpdkParamsStream << "-n "; + dpdkParamsStream << "2 "; + dpdkParamsStream << "-c "; + dpdkParamsStream << "0x" << std::hex << std::setw(2) << std::setfill('0') + << coreMask << " "; + dpdkParamsStream << MASTER_LCORE << " "; + dpdkParamsStream << (int)masterCore << " "; + + uint32_t i = 0; + while (i < initDpdkArgc && initDpdkArgv[i] != NULL) { + dpdkParamsStream << initDpdkArgv[i] << " "; + i++; + } + + // Should be equal to the number of static params + initDpdkArgc += 7; + std::string dpdkParamsArray[initDpdkArgc]; + initDpdkArgvBuffer = new char*[initDpdkArgc]; + i = 0; + while (dpdkParamsStream.good() && i < initDpdkArgc) { + dpdkParamsStream >> dpdkParamsArray[i]; + initDpdkArgvBuffer[i] = new char[dpdkParamsArray[i].length() + 1]; + strcpy(initDpdkArgvBuffer[i], dpdkParamsArray[i].c_str()); + i++; + } + + char* lastParam = initDpdkArgvBuffer[i - 1]; + + for (i = 0; i < initDpdkArgc; i++) { + PCPP_LOG_DEBUG("DPDK initialization params: " << initDpdkArgvBuffer[i]); + } + + optind = 1; + // init the EAL + int ret = rte_eal_init(initDpdkArgc, (char**)initDpdkArgvBuffer); + if (ret < 0) { + PCPP_LOG_ERROR("failed to init the DPDK EAL"); + return false; + } + + for (i = 0; i < initDpdkArgc - 1; i++) { + delete[] initDpdkArgvBuffer[i]; + } + delete[] lastParam; + + delete[] initDpdkArgvBuffer; + + m_CoreMask = coreMask; + m_IsDpdkInitialized = true; + + m_MBufPoolSizePerDevice = mBufPoolSizePerDevice; + DpdkDeviceList::getInstance().setDpdkLogLevel(Logger::Info); + return DpdkDeviceList::getInstance().initDpdkDevices(m_MBufPoolSizePerDevice); } -bool DpdkDeviceList::initDpdkDevices(uint32_t mBufPoolSizePerDevice) -{ - if (!m_IsDpdkInitialized) - { - PCPP_LOG_ERROR("DPDK is not initialized!! Please call DpdkDeviceList::initDpdk(coreMask, mBufPoolSizePerDevice) before start using DPDK devices"); - return false; - } +bool DpdkDeviceList::initDpdkDevices(uint32_t mBufPoolSizePerDevice) { + if (!m_IsDpdkInitialized) { + PCPP_LOG_ERROR("DPDK is not initialized!! Please call " + "DpdkDeviceList::initDpdk(coreMask, mBufPoolSizePerDevice) " + "before start using DPDK devices"); + return false; + } - if (m_IsInitialized) - return true; + if (m_IsInitialized) + return true; #if (RTE_VER_YEAR < 18) || (RTE_VER_YEAR == 18 && RTE_VER_MONTH < 5) - int numOfPorts = (int)rte_eth_dev_count(); + int numOfPorts = (int)rte_eth_dev_count(); #else - int numOfPorts = (int)rte_eth_dev_count_avail(); + int numOfPorts = (int)rte_eth_dev_count_avail(); #endif - if (numOfPorts <= 0) - { - PCPP_LOG_ERROR("Zero DPDK ports are initialized. Something went wrong while initializing DPDK"); - return false; - } - - PCPP_LOG_DEBUG("Found " << numOfPorts << " DPDK ports. Constructing DpdkDevice for each one"); - - // Initialize a DpdkDevice per port - for (int i = 0; i < numOfPorts; i++) - { - DpdkDevice* newDevice = new DpdkDevice(i, mBufPoolSizePerDevice); - PCPP_LOG_DEBUG("DpdkDevice #" << i << ": Name='" << newDevice->getDeviceName() << "', PCI-slot='" << newDevice->getPciAddress() << "', PMD='" << newDevice->getPMDName() << "', MAC Addr='" << newDevice->getMacAddress() << "'"); - m_DpdkDeviceList.push_back(newDevice); - } - - m_IsInitialized = true; - return true; + if (numOfPorts <= 0) { + PCPP_LOG_ERROR("Zero DPDK ports are initialized. Something went wrong " + "while initializing DPDK"); + return false; + } + + PCPP_LOG_DEBUG( + "Found " << numOfPorts + << " DPDK ports. Constructing DpdkDevice for each one"); + + // Initialize a DpdkDevice per port + for (int i = 0; i < numOfPorts; i++) { + DpdkDevice* newDevice = new DpdkDevice(i, mBufPoolSizePerDevice); + PCPP_LOG_DEBUG("DpdkDevice #" + << i << ": Name='" << newDevice->getDeviceName() + << "', PCI-slot='" << newDevice->getPciAddress() + << "', PMD='" << newDevice->getPMDName() << "', MAC Addr='" + << newDevice->getMacAddress() << "'"); + m_DpdkDeviceList.push_back(newDevice); + } + + m_IsInitialized = true; + return true; } -DpdkDevice* DpdkDeviceList::getDeviceByPort(int portId) const -{ - if (!isInitialized()) - { - PCPP_LOG_ERROR("DpdkDeviceList not initialized"); - return NULL; - } +DpdkDevice* DpdkDeviceList::getDeviceByPort(int portId) const { + if (!isInitialized()) { + PCPP_LOG_ERROR("DpdkDeviceList not initialized"); + return NULL; + } - if ((uint32_t)portId >= m_DpdkDeviceList.size()) - { - return NULL; - } + if ((uint32_t)portId >= m_DpdkDeviceList.size()) { + return NULL; + } - return m_DpdkDeviceList.at(portId); + return m_DpdkDeviceList.at(portId); } -DpdkDevice* DpdkDeviceList::getDeviceByPciAddress(const std::string& pciAddr) const -{ - if (!isInitialized()) - { - PCPP_LOG_ERROR("DpdkDeviceList not initialized"); - return NULL; - } - - for (std::vector::const_iterator iter = m_DpdkDeviceList.begin(); iter != m_DpdkDeviceList.end(); iter++) - { - if ((*iter)->getPciAddress() == pciAddr) - return (*iter); - } - - return NULL; +DpdkDevice* +DpdkDeviceList::getDeviceByPciAddress(const std::string& pciAddr) const { + if (!isInitialized()) { + PCPP_LOG_ERROR("DpdkDeviceList not initialized"); + return NULL; + } + + for (std::vector::const_iterator iter = + m_DpdkDeviceList.begin(); + iter != m_DpdkDeviceList.end(); iter++) { + if ((*iter)->getPciAddress() == pciAddr) + return (*iter); + } + + return NULL; } -bool DpdkDeviceList::verifyHugePagesAndDpdkDriver() -{ - std::string execResult = executeShellCommand("cat /proc/meminfo | grep -s HugePages_Total | awk '{print $2}'"); - // trim '\n' at the end - execResult.erase(std::remove(execResult.begin(), execResult.end(), '\n'), execResult.end()); - - // convert the result to long - char* endPtr; - long totalHugePages = strtol(execResult.c_str(), &endPtr, 10); - - PCPP_LOG_DEBUG("Total number of huge-pages is " << totalHugePages); - - if (totalHugePages <= 0) - { - PCPP_LOG_ERROR("Huge pages aren't set, DPDK cannot be initialized. Please run /setup_dpdk.sh"); - return false; - } - - execResult = executeShellCommand("lsmod | grep -s igb_uio"); - if (execResult == "") - { - execResult = executeShellCommand("modinfo -d uio_pci_generic"); - if (execResult.find("ERROR") != std::string::npos) - { - execResult = executeShellCommand("modinfo -d vfio-pci"); - if (execResult.find("ERROR") != std::string::npos) - { - PCPP_LOG_ERROR("None of igb_uio, uio_pci_generic, vfio-pci kernel modules are loaded so DPDK cannot be initialized. Please run /setup_dpdk.sh"); - return false; - } - else - { - PCPP_LOG_DEBUG("vfio-pci module is loaded"); - } - } - else - { - PCPP_LOG_DEBUG("uio_pci_generic module is loaded"); - } - } - else - PCPP_LOG_DEBUG("igb_uio driver is loaded"); - - return true; +bool DpdkDeviceList::verifyHugePagesAndDpdkDriver() { + std::string execResult = executeShellCommand( + "cat /proc/meminfo | grep -s HugePages_Total | awk '{print $2}'"); + // trim '\n' at the end + execResult.erase(std::remove(execResult.begin(), execResult.end(), '\n'), + execResult.end()); + + // convert the result to long + char* endPtr; + long totalHugePages = strtol(execResult.c_str(), &endPtr, 10); + + PCPP_LOG_DEBUG("Total number of huge-pages is " << totalHugePages); + + if (totalHugePages <= 0) { + PCPP_LOG_ERROR("Huge pages aren't set, DPDK cannot be initialized. Please " + "run /setup_dpdk.sh"); + return false; + } + + execResult = executeShellCommand("lsmod | grep -s igb_uio"); + if (execResult == "") { + execResult = executeShellCommand("modinfo -d uio_pci_generic"); + if (execResult.find("ERROR") != std::string::npos) { + execResult = executeShellCommand("modinfo -d vfio-pci"); + if (execResult.find("ERROR") != std::string::npos) { + PCPP_LOG_ERROR("None of igb_uio, uio_pci_generic, vfio-pci kernel " + "modules are loaded so DPDK cannot be initialized. " + "Please run /setup_dpdk.sh"); + return false; + } else { + PCPP_LOG_DEBUG("vfio-pci module is loaded"); + } + } else { + PCPP_LOG_DEBUG("uio_pci_generic module is loaded"); + } + } else + PCPP_LOG_DEBUG("igb_uio driver is loaded"); + + return true; } -SystemCore DpdkDeviceList::getDpdkMasterCore() const -{ - return SystemCores::IdToSystemCore[GET_MASTER_CORE()]; +SystemCore DpdkDeviceList::getDpdkMasterCore() const { + return SystemCores::IdToSystemCore[GET_MASTER_CORE()]; } -void DpdkDeviceList::setDpdkLogLevel(Logger::LogLevel logLevel) -{ +void DpdkDeviceList::setDpdkLogLevel(Logger::LogLevel logLevel) { #if (RTE_VER_YEAR > 17) || (RTE_VER_YEAR == 17 && RTE_VER_MONTH >= 11) - if (logLevel == Logger::Info) - rte_log_set_global_level(RTE_LOG_NOTICE); - else // logLevel == Logger::Debug - rte_log_set_global_level(RTE_LOG_DEBUG); + if (logLevel == Logger::Info) + rte_log_set_global_level(RTE_LOG_NOTICE); + else // logLevel == Logger::Debug + rte_log_set_global_level(RTE_LOG_DEBUG); #else - if (logLevel == Logger::Info) - rte_set_log_level(RTE_LOG_NOTICE); - else // logLevel == Logger::Debug - rte_set_log_level(RTE_LOG_DEBUG); + if (logLevel == Logger::Info) + rte_set_log_level(RTE_LOG_NOTICE); + else // logLevel == Logger::Debug + rte_set_log_level(RTE_LOG_DEBUG); #endif } -Logger::LogLevel DpdkDeviceList::getDpdkLogLevel() const -{ +Logger::LogLevel DpdkDeviceList::getDpdkLogLevel() const { #if (RTE_VER_YEAR > 17) || (RTE_VER_YEAR == 17 && RTE_VER_MONTH >= 11) - if (rte_log_get_global_level() <= RTE_LOG_NOTICE) + if (rte_log_get_global_level() <= RTE_LOG_NOTICE) #else - if (rte_get_log_level() <= RTE_LOG_NOTICE) + if (rte_get_log_level() <= RTE_LOG_NOTICE) #endif - return Logger::Info; - else - return Logger::Debug; + return Logger::Info; + else + return Logger::Debug; } -bool DpdkDeviceList::writeDpdkLogToFile(FILE* logFile) -{ - return (rte_openlog_stream(logFile) == 0); +bool DpdkDeviceList::writeDpdkLogToFile(FILE* logFile) { + return (rte_openlog_stream(logFile) == 0); } -int DpdkDeviceList::dpdkWorkerThreadStart(void *ptr) -{ - DpdkWorkerThread* workerThread = (DpdkWorkerThread*)ptr; - workerThread->run(rte_lcore_id()); - return 0; +int DpdkDeviceList::dpdkWorkerThreadStart(void* ptr) { + DpdkWorkerThread* workerThread = (DpdkWorkerThread*)ptr; + workerThread->run(rte_lcore_id()); + return 0; } -bool DpdkDeviceList::startDpdkWorkerThreads(CoreMask coreMask, std::vector& workerThreadsVec) -{ - if (!isInitialized()) - { - PCPP_LOG_ERROR("DpdkDeviceList not initialized"); - return false; - } - - CoreMask tempCoreMask = coreMask; - size_t numOfCoresInMask = 0; - int coreNum = 0; - while (tempCoreMask > 0) - { - if (tempCoreMask & 1) - { - if (!rte_lcore_is_enabled(coreNum)) - { - PCPP_LOG_ERROR("Trying to use core #" << coreNum << " which isn't initialized by DPDK"); - return false; - } - - numOfCoresInMask++; - } - tempCoreMask = tempCoreMask >> 1; - coreNum++; - } - - if (numOfCoresInMask == 0) - { - PCPP_LOG_ERROR("Number of cores in mask is 0"); - return false; - } - - if (numOfCoresInMask != workerThreadsVec.size()) - { - PCPP_LOG_ERROR("Number of cores in core mask different from workerThreadsVec size"); - return false; - } - - if (coreMask & getDpdkMasterCore().Mask) - { - PCPP_LOG_ERROR("Cannot run worker thread on DPDK master core"); - return false; - } - - m_WorkerThreads.clear(); - uint32_t index = 0; - std::vector::iterator iter = workerThreadsVec.begin(); - while (iter != workerThreadsVec.end()) - { - SystemCore core = SystemCores::IdToSystemCore[index]; - if (!(coreMask & core.Mask)) - { - index++; - continue; - } - - int err = rte_eal_remote_launch(dpdkWorkerThreadStart, *iter, core.Id); - if (err != 0) - { - for (std::vector::iterator iter2 = workerThreadsVec.begin(); iter2 != iter; iter2++) - { - (*iter)->stop(); - rte_eal_wait_lcore((*iter)->getCoreId()); - PCPP_LOG_DEBUG("Thread on core [" << (*iter)->getCoreId() << "] stopped"); - } - PCPP_LOG_ERROR("Cannot create worker thread #" << core.Id << ". Error was: [" << strerror(err) << "]"); - return false; - } - m_WorkerThreads.push_back(*iter); - - index++; - iter++; - } - - return true; +bool DpdkDeviceList::startDpdkWorkerThreads( + CoreMask coreMask, std::vector& workerThreadsVec) { + if (!isInitialized()) { + PCPP_LOG_ERROR("DpdkDeviceList not initialized"); + return false; + } + + CoreMask tempCoreMask = coreMask; + size_t numOfCoresInMask = 0; + int coreNum = 0; + while (tempCoreMask > 0) { + if (tempCoreMask & 1) { + if (!rte_lcore_is_enabled(coreNum)) { + PCPP_LOG_ERROR("Trying to use core #" + << coreNum << " which isn't initialized by DPDK"); + return false; + } + + numOfCoresInMask++; + } + tempCoreMask = tempCoreMask >> 1; + coreNum++; + } + + if (numOfCoresInMask == 0) { + PCPP_LOG_ERROR("Number of cores in mask is 0"); + return false; + } + + if (numOfCoresInMask != workerThreadsVec.size()) { + PCPP_LOG_ERROR( + "Number of cores in core mask different from workerThreadsVec size"); + return false; + } + + if (coreMask & getDpdkMasterCore().Mask) { + PCPP_LOG_ERROR("Cannot run worker thread on DPDK master core"); + return false; + } + + m_WorkerThreads.clear(); + uint32_t index = 0; + std::vector::iterator iter = workerThreadsVec.begin(); + while (iter != workerThreadsVec.end()) { + SystemCore core = SystemCores::IdToSystemCore[index]; + if (!(coreMask & core.Mask)) { + index++; + continue; + } + + int err = rte_eal_remote_launch(dpdkWorkerThreadStart, *iter, core.Id); + if (err != 0) { + for (std::vector::iterator iter2 = + workerThreadsVec.begin(); + iter2 != iter; iter2++) { + (*iter)->stop(); + rte_eal_wait_lcore((*iter)->getCoreId()); + PCPP_LOG_DEBUG("Thread on core [" << (*iter)->getCoreId() + << "] stopped"); + } + PCPP_LOG_ERROR("Cannot create worker thread #" + << core.Id << ". Error was: [" << strerror(err) << "]"); + return false; + } + m_WorkerThreads.push_back(*iter); + + index++; + iter++; + } + + return true; } -void DpdkDeviceList::stopDpdkWorkerThreads() -{ - if (m_WorkerThreads.empty()) - { - PCPP_LOG_ERROR("No worker threads were set"); - return; - } - - for (std::vector::iterator iter = m_WorkerThreads.begin(); iter != m_WorkerThreads.end(); iter++) - { - (*iter)->stop(); - rte_eal_wait_lcore((*iter)->getCoreId()); - PCPP_LOG_DEBUG("Thread on core [" << (*iter)->getCoreId() << "] stopped"); - } - - m_WorkerThreads.clear(); - std::vector(m_WorkerThreads).swap(m_WorkerThreads); - - PCPP_LOG_DEBUG("All worker threads stopped"); +void DpdkDeviceList::stopDpdkWorkerThreads() { + if (m_WorkerThreads.empty()) { + PCPP_LOG_ERROR("No worker threads were set"); + return; + } + + for (std::vector::iterator iter = m_WorkerThreads.begin(); + iter != m_WorkerThreads.end(); iter++) { + (*iter)->stop(); + rte_eal_wait_lcore((*iter)->getCoreId()); + PCPP_LOG_DEBUG("Thread on core [" << (*iter)->getCoreId() << "] stopped"); + } + + m_WorkerThreads.clear(); + std::vector(m_WorkerThreads).swap(m_WorkerThreads); + + PCPP_LOG_DEBUG("All worker threads stopped"); } } // namespace pcpp diff --git a/Pcap++/src/KniDevice.cpp b/Pcap++/src/KniDevice.cpp index 5f29f6b052..e5cf1991d6 100644 --- a/Pcap++/src/KniDevice.cpp +++ b/Pcap++/src/KniDevice.cpp @@ -8,40 +8,39 @@ #include "Logger.h" #include "SystemUtils.h" -#include -#include -#include -#include #include #include +#include +#include +#include +#include -#include -#include -#include -#include +#include #include +#include +#include #include -#include +#include +#include +#include #include #include #include -#include #ifndef KNI_MEMPOOL_NAME_PREFIX -# define KNI_MEMPOOL_NAME_PREFIX "kniMempool" +#define KNI_MEMPOOL_NAME_PREFIX "kniMempool" #endif #ifndef MEMPOOL_CACHE_SIZE -# define MEMPOOL_CACHE_SIZE 256 +#define MEMPOOL_CACHE_SIZE 256 #endif #ifndef MAX_BURST_SIZE -# define MAX_BURST_SIZE 64 +#define MAX_BURST_SIZE 64 #endif #define CPP_VLA(TYPE, SIZE) (TYPE*)__builtin_alloca(sizeof(TYPE) * SIZE) -namespace pcpp -{ +namespace pcpp { /** * ========================== @@ -49,50 +48,39 @@ namespace pcpp * ========================== */ -struct KniDevice::KniThread -{ - enum KniThreadCleanupState - { - JOINABLE, - DETACHED, - INVALID - }; - typedef void(*threadMain)(void*, std::atomic& ); - KniThread(KniThreadCleanupState s, threadMain tm, void* data); - ~KniThread(); - - void cancel(); - - std::thread m_Descriptor; - KniThreadCleanupState m_CleanupState; - std::atomic m_StopThread; -}; +struct KniDevice::KniThread { + enum KniThreadCleanupState { JOINABLE, + DETACHED, + INVALID }; + typedef void (*threadMain)(void*, std::atomic&); + KniThread(KniThreadCleanupState s, threadMain tm, void* data); + ~KniThread(); -KniDevice::KniThread::KniThread(KniThreadCleanupState s, threadMain tm, void* data) : - m_CleanupState(s) -{ - m_StopThread = false; - m_Descriptor = std::thread(tm, data, std::ref(m_StopThread)); + void cancel(); - if (m_CleanupState == DETACHED) - { - m_Descriptor.detach(); - } -} + std::thread m_Descriptor; + KniThreadCleanupState m_CleanupState; + std::atomic m_StopThread; +}; -KniDevice::KniThread::~KniThread() -{ - if (m_CleanupState == JOINABLE) - { - m_Descriptor.join(); - } +KniDevice::KniThread::KniThread(KniThreadCleanupState s, threadMain tm, + void* data) + : m_CleanupState(s) { + m_StopThread = false; + m_Descriptor = std::thread(tm, data, std::ref(m_StopThread)); + + if (m_CleanupState == DETACHED) { + m_Descriptor.detach(); + } } -void KniDevice::KniThread::cancel() -{ - m_StopThread = true; +KniDevice::KniThread::~KniThread() { + if (m_CleanupState == JOINABLE) { + m_Descriptor.join(); + } } +void KniDevice::KniThread::cancel() { m_StopThread = true; } /** * =============== @@ -100,911 +88,824 @@ void KniDevice::KniThread::cancel() * =============== */ -namespace -{ - -inline bool destroyKniDevice(struct rte_kni* kni, const char* deviceName) -{ - if (rte_kni_release(kni) < 0) - { - PCPP_LOG_ERROR("Failed to destroy DPDK KNI device " << deviceName); - return true; - } - return false; -} - -inline KniDevice::KniLinkState setKniDeviceLinkState( - struct rte_kni* kni, - const char* deviceName, - KniDevice::KniLinkState state = KniDevice::LINK_UP -) -{ - KniDevice::KniLinkState oldState = KniDevice::LINK_NOT_SUPPORTED; - if (kni == NULL || !(state == KniDevice::LINK_UP || state == KniDevice::LINK_DOWN)) - { - return oldState = KniDevice::LINK_ERROR; - } - #if RTE_VERSION >= RTE_VERSION_NUM(18, 11, 0, 0) - oldState = (KniDevice::KniLinkState)rte_kni_update_link(kni, state); - if (oldState == KniDevice::LINK_ERROR) - { //? NOTE(echo-Mike): Not LOG_ERROR because will generate a lot of junk messages on some DPDK versions - PCPP_LOG_DEBUG("DPDK KNI Failed to update links state for device '" << deviceName << "'"); - } - #else - // To avoid compiler warnings - (void) kni; - (void) deviceName; - #endif - return oldState; -} - -inline struct rte_mempool* createMempool(size_t mempoolSize, int unique, const char* deviceName) -{ - struct rte_mempool* result = NULL; - char mempoolName[64]; - snprintf(mempoolName, sizeof(mempoolName), - KNI_MEMPOOL_NAME_PREFIX "%d", - unique - ); - result = rte_pktmbuf_pool_create( - mempoolName, - mempoolSize, - MEMPOOL_CACHE_SIZE, - 0, - RTE_MBUF_DEFAULT_BUF_SIZE, - rte_socket_id() - ); - if (result == NULL) - { - PCPP_LOG_ERROR("Failed to create packets memory pool for KNI device '" << deviceName << "', pool name: " << mempoolName); - } - else - { - PCPP_LOG_DEBUG("Successfully initialized packets pool of size [" << mempoolSize << "] for KNI device [" << deviceName << "]"); - } - return result; +namespace { + +inline bool destroyKniDevice(struct rte_kni* kni, const char* deviceName) { + if (rte_kni_release(kni) < 0) { + PCPP_LOG_ERROR("Failed to destroy DPDK KNI device " << deviceName); + return true; + } + return false; +} + +inline KniDevice::KniLinkState +setKniDeviceLinkState(struct rte_kni* kni, const char* deviceName, + KniDevice::KniLinkState state = KniDevice::LINK_UP) { + KniDevice::KniLinkState oldState = KniDevice::LINK_NOT_SUPPORTED; + if (kni == NULL || + !(state == KniDevice::LINK_UP || state == KniDevice::LINK_DOWN)) { + return oldState = KniDevice::LINK_ERROR; + } +#if RTE_VERSION >= RTE_VERSION_NUM(18, 11, 0, 0) + oldState = (KniDevice::KniLinkState)rte_kni_update_link(kni, state); + if (oldState == KniDevice::LINK_ERROR) { //? NOTE(echo-Mike): Not LOG_ERROR + //because will generate a lot of junk + //messages on some DPDK versions + PCPP_LOG_DEBUG("DPDK KNI Failed to update links state for device '" + << deviceName << "'"); + } +#else + // To avoid compiler warnings + (void)kni; + (void)deviceName; +#endif + return oldState; +} + +inline struct rte_mempool* createMempool(size_t mempoolSize, int unique, + const char* deviceName) { + struct rte_mempool* result = NULL; + char mempoolName[64]; + snprintf(mempoolName, sizeof(mempoolName), KNI_MEMPOOL_NAME_PREFIX "%d", + unique); + result = + rte_pktmbuf_pool_create(mempoolName, mempoolSize, MEMPOOL_CACHE_SIZE, 0, + RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id()); + if (result == NULL) { + PCPP_LOG_ERROR("Failed to create packets memory pool for KNI device '" + << deviceName << "', pool name: " << mempoolName); + } else { + PCPP_LOG_DEBUG("Successfully initialized packets pool of size [" + << mempoolSize << "] for KNI device [" << deviceName << "]"); + } + return result; } } // namespace -KniDevice::KniDevice(const KniDeviceConfiguration& conf, size_t mempoolSize, int unique) : - m_Device(NULL), m_MBufMempool(NULL) -{ - struct rte_kni_ops kniOps; - struct rte_kni_conf kniConf; - if (!m_DeviceInfo.init(conf)) - return; - m_Requests.thread = NULL; - std::memset(&m_Capturing, 0, sizeof(m_Capturing)); - std::memset(&m_Requests, 0, sizeof(m_Requests)); - - if ((m_MBufMempool = createMempool(mempoolSize, unique, conf.name.c_str())) == NULL) - return; - - std::memset(&kniOps, 0, sizeof(kniOps)); - std::memset(&kniConf, 0, sizeof(kniConf)); - snprintf(kniConf.name, RTE_KNI_NAMESIZE, "%s", conf.name.c_str()); - kniConf.core_id = conf.kthreadCoreId; - kniConf.mbuf_size = MBufRawPacket::MBUF_DATA_SIZE; - kniConf.force_bind = conf.bindKthread ? 1 : 0; - #if RTE_VERSION >= RTE_VERSION_NUM(18, 2, 0, 0) - if (conf.mac != MacAddress::Zero) - conf.mac.copyTo((uint8_t*)kniConf.mac_addr); - kniConf.mtu = conf.mtu; - #endif - - kniOps.port_id = conf.portId; - #if RTE_VERSION >= RTE_VERSION_NUM(17, 11, 0, 0) - if (conf.callbacks != NULL) - { - kniOps.change_mtu = conf.callbacks->change_mtu; - kniOps.config_network_if = conf.callbacks->config_network_if; - #if RTE_VERSION >= RTE_VERSION_NUM(18, 2, 0, 0) - kniOps.config_mac_address = conf.callbacks->config_mac_address; - kniOps.config_promiscusity = conf.callbacks->config_promiscusity; - #endif - } - #else - if (conf.oldCallbacks != NULL) - { - kniOps.change_mtu = conf.oldCallbacks->change_mtu; - kniOps.config_network_if = conf.oldCallbacks->config_network_if; - } - #endif - - m_Device = rte_kni_alloc(m_MBufMempool, &kniConf, &kniOps); - if (m_Device == NULL) - { - PCPP_LOG_ERROR("DPDK have failed to create KNI device " << conf.name); - } -} - -KniDevice::~KniDevice() -{ - m_Requests.cleanup(); - m_Capturing.cleanup(); - if (m_Device != NULL) - { - setKniDeviceLinkState(m_Device, m_DeviceInfo.name.c_str(), KniDevice::LINK_DOWN); - destroyKniDevice(m_Device, m_DeviceInfo.name.c_str()); - } - if (m_MBufMempool != NULL) - rte_mempool_free(m_MBufMempool); -} - -bool KniDevice::KniDeviceInfo::init(const KniDeviceConfiguration& conf) -{ - link = KniDevice::LINK_NOT_SUPPORTED; - promisc = KniDevice::PROMISC_DISABLE; - portId = conf.portId; - mtu = conf.mtu; - if (conf.name.empty()) - { - PCPP_LOG_ERROR( - "Failed to create KNI device. " - "Empty name provided" - ); - return false; - } - if (conf.name.size() >= IFNAMSIZ) - { - PCPP_LOG_ERROR( - "Failed to create KNI device. " - "Provided name has length more than possible to handle <" << IFNAMSIZ-1 << ">" - ); - return false; - } - name = conf.name; - mac = conf.mac; - return true; -} - -KniDevice::KniLinkState KniDevice::getLinkState(KniInfoState state) -{ - if (state == KniDevice::INFO_CACHED) - return m_DeviceInfo.link; - struct ifreq req; - std::memset(&req, 0, sizeof(req)); - if (!m_DeviceInfo.soc.makeRequest(m_DeviceInfo.name.c_str(), SIOCGIFFLAGS, &req)) - { - PCPP_LOG_ERROR("DPDK KNI failed to obtain interface link state from Linux"); - PCPP_LOG_DEBUG("Last known link state for device '" << m_DeviceInfo.name << "' is returned"); - return m_DeviceInfo.link; - } - return m_DeviceInfo.link = KniLinkState(req.ifr_flags & IFF_UP); -} - -MacAddress KniDevice::getMacAddress(KniInfoState state) -{ - if (state == KniDevice::INFO_CACHED) - return m_DeviceInfo.mac; - struct ifreq req; - std::memset(&req, 0, sizeof(req)); - req.ifr_hwaddr.sa_family = ARPHRD_ETHER; - if (!m_DeviceInfo.soc.makeRequest(m_DeviceInfo.name.c_str(), SIOCGIFHWADDR, &req)) - { - PCPP_LOG_ERROR("DPDK KNI failed to obtain MAC address from Linux"); - PCPP_LOG_DEBUG("Last known MAC address for device '" << m_DeviceInfo.name << "' is returned"); - return m_DeviceInfo.mac; - } - return m_DeviceInfo.mac = MacAddress((uint8_t*)req.ifr_hwaddr.sa_data); -} - -uint16_t KniDevice::getMtu(KniInfoState state) -{ - if (state == KniDevice::INFO_CACHED) - return m_DeviceInfo.mtu; - struct ifreq req; - std::memset(&req, 0, sizeof(req)); - if (!m_DeviceInfo.soc.makeRequest(m_DeviceInfo.name.c_str(), SIOCGIFMTU, &req)) - { - PCPP_LOG_ERROR("DPDK KNI failed to obtain interface MTU from Linux"); - PCPP_LOG_DEBUG("Last known MTU for device '" << m_DeviceInfo.name << "' is returned"); - return m_DeviceInfo.mtu; - } - return m_DeviceInfo.mtu = req.ifr_mtu; -} - -KniDevice::KniPromiscuousMode KniDevice::getPromiscuous(KniInfoState state) -{ - if (state == KniDevice::INFO_CACHED) - return m_DeviceInfo.promisc; - struct ifreq req; - std::memset(&req, 0, sizeof(req)); - if (!m_DeviceInfo.soc.makeRequest(m_DeviceInfo.name.c_str(), SIOCGIFFLAGS, &req)) - { - PCPP_LOG_ERROR("DPDK KNI failed to obtain interface Promiscuous mode from Linux"); - PCPP_LOG_DEBUG("Last known Promiscuous mode for device '" << m_DeviceInfo.name << "' is returned"); - return m_DeviceInfo.promisc; - } - return m_DeviceInfo.promisc = (req.ifr_flags & IFF_PROMISC) ? KniDevice::PROMISC_ENABLE : KniDevice::PROMISC_DISABLE; -} - -bool KniDevice::setLinkState(KniLinkState state) -{ - if (!(state == KniDevice::LINK_DOWN || state == KniDevice::LINK_UP)) - return false; - struct ifreq req; - std::memset(&req, 0, sizeof(req)); - if (!m_DeviceInfo.soc.makeRequest(m_DeviceInfo.name.c_str(), SIOCGIFFLAGS, &req)) - { - PCPP_LOG_ERROR("DPDK KNI failed to obtain interface flags from Linux"); - return false; - } - if ((state == KniDevice::LINK_DOWN && req.ifr_flags & IFF_UP) || - (state == KniDevice::LINK_UP && !(req.ifr_flags & IFF_UP))) - { - req.ifr_flags ^= IFF_UP; - if (!m_DeviceInfo.soc.makeRequest(m_DeviceInfo.name.c_str(), SIOCSIFFLAGS, &req)) - { - PCPP_LOG_ERROR("DPDK KNI failed to set '" << m_DeviceInfo.name << "' link mode"); - return false; - } - } - m_DeviceInfo.link = state; - return true; -} - -bool KniDevice::setMacAddress(MacAddress mac) -{ - if (!mac.isValid()) - return false; - struct ifreq req; - std::memset(&req, 0, sizeof(req)); - req.ifr_hwaddr.sa_family = ARPHRD_ETHER; - mac.copyTo((uint8_t*)req.ifr_hwaddr.sa_data); - if (!m_DeviceInfo.soc.makeRequest(m_DeviceInfo.name.c_str(), SIOCSIFHWADDR, &req)) - { - PCPP_LOG_ERROR("DPDK KNI failed to set MAC address"); - return false; - } - m_DeviceInfo.mac = mac; - return true; -} - -bool KniDevice::setMtu(uint16_t mtu) -{ - struct ifreq req; - std::memset(&req, 0, sizeof(req)); - req.ifr_mtu = mtu; - if (!m_DeviceInfo.soc.makeRequest(m_DeviceInfo.name.c_str(), SIOCSIFMTU, &req)) - { - PCPP_LOG_ERROR("DPDK KNI failed to set interface MTU"); - return false; - } - m_DeviceInfo.mtu = mtu; - return true; -} - -bool KniDevice::setPromiscuous(KniPromiscuousMode mode) -{ - if (!(mode == KniDevice::PROMISC_DISABLE || mode == KniDevice::PROMISC_ENABLE)) - return false; - struct ifreq req; - std::memset(&req, 0, sizeof(req)); - if (!m_DeviceInfo.soc.makeRequest(m_DeviceInfo.name.c_str(), SIOCGIFFLAGS, &req)) - { - PCPP_LOG_ERROR("DPDK KNI failed to obtain interface flags from Linux"); - return false; - } - if ((mode == KniDevice::PROMISC_DISABLE && req.ifr_flags & IFF_PROMISC) || - (mode == KniDevice::PROMISC_ENABLE && !(req.ifr_flags & IFF_PROMISC))) - { - req.ifr_flags ^= IFF_PROMISC; - if (!m_DeviceInfo.soc.makeRequest(m_DeviceInfo.name.c_str(), SIOCSIFFLAGS, &req)) - { - PCPP_LOG_ERROR("DPDK KNI failed to set '" << m_DeviceInfo.name << "' link mode"); - return false; - } - } - m_DeviceInfo.promisc = mode; - return true; -} - -KniDevice::KniLinkState KniDevice::updateLinkState(KniLinkState state) -{ - KniLinkState oldState = setKniDeviceLinkState(m_Device, m_DeviceInfo.name.c_str(), state); - if (oldState != KniDevice::LINK_NOT_SUPPORTED && oldState != KniDevice::LINK_ERROR) - m_DeviceInfo.link = state; - return oldState; -} - -bool KniDevice::handleRequests() -{ - return rte_kni_handle_request(m_Device) == 0; -} - -void KniDevice::KniRequests::cleanup() -{ - if (thread) - thread->cancel(); - delete thread; - thread = NULL; - sleepS = sleepNs = 0; -} - -void KniDevice::KniRequests::runRequests(void* devicePointer, std::atomic& stopThread) -{ - KniDevice* device = (KniDevice*)devicePointer; - struct timespec sleepTime; - sleepTime.tv_sec = device->m_Requests.sleepS; - sleepTime.tv_nsec = device->m_Requests.sleepNs; - struct rte_kni* kni_dev = device->m_Device; - for(;;) - { - nanosleep(&sleepTime, NULL); - rte_kni_handle_request(kni_dev); - if (stopThread) - { - return; - } - } -} - -bool KniDevice::startRequestHandlerThread(long sleepSeconds, long sleepNanoSeconds) -{ - if (m_Requests.thread != NULL) - { - PCPP_LOG_ERROR("KNI request thread is already started for device '" << m_DeviceInfo.name << "'"); - return false; - } - m_Requests.sleepS = sleepSeconds; - m_Requests.sleepNs = sleepNanoSeconds; - m_Requests.thread = new KniThread(KniThread::DETACHED, KniRequests::runRequests, (void*)this); - if (m_Requests.thread->m_CleanupState == KniThread::INVALID) - { - m_Requests.cleanup(); - return false; - } - return true; -} - -void KniDevice::stopRequestHandlerThread() -{ - if (m_Requests.thread == NULL) - { - PCPP_LOG_DEBUG("Attempt to stop not running KNI request thread for device '" << m_DeviceInfo.name << "'"); - return; - } - m_Requests.cleanup(); -} - -uint16_t KniDevice::receivePackets(MBufRawPacketVector& rawPacketsArr) -{ - if (unlikely(!m_DeviceOpened)) - { - PCPP_LOG_ERROR("KNI device '" << m_DeviceInfo.name << "' is not opened"); - return 0; - } - if (unlikely(m_Capturing.isRunning())) - { - PCPP_LOG_ERROR( - "KNI device '" << m_DeviceInfo.name << "' capture mode is currently running. " - "Cannot receive packets in parallel" - ); - return 0; - } - - struct rte_mbuf* mBufArray[MAX_BURST_SIZE]; - uint32_t numOfPktsReceived = rte_kni_rx_burst(m_Device, mBufArray, MAX_BURST_SIZE); - - //the following line trashes the log with many messages. Uncomment only if necessary - //PCPP_LOG_DEBUG("KNI Captured %d packets", numOfPktsReceived); - - if (unlikely(!numOfPktsReceived)) - { - return 0; - } - - timespec time; - clock_gettime(CLOCK_REALTIME, &time); - - for (uint32_t index = 0; index < numOfPktsReceived; ++index) - { - struct rte_mbuf* mBuf = mBufArray[index]; - MBufRawPacket* newRawPacket = new MBufRawPacket(); - newRawPacket->setMBuf(mBuf, time); - rawPacketsArr.pushBack(newRawPacket); - } - - return numOfPktsReceived; -} - -uint16_t KniDevice::receivePackets(MBufRawPacket** rawPacketsArr, uint16_t rawPacketArrLength) -{ - if (unlikely(!m_DeviceOpened)) - { - PCPP_LOG_ERROR("KNI device '" << m_DeviceInfo.name << "' is not opened"); - return 0; - } - if (unlikely(m_Capturing.isRunning())) - { - PCPP_LOG_ERROR( - "KNI device '" << m_DeviceInfo.name << "' capture mode is currently running. " - "Cannot receive packets in parallel" - ); - return 0; - } - if (unlikely(rawPacketsArr == NULL)) - { - PCPP_LOG_ERROR("KNI Provided address of array to store packets is NULL"); - return 0; - } - - struct rte_mbuf** mBufArray = CPP_VLA(struct rte_mbuf*, rawPacketArrLength); - uint16_t packetsReceived = rte_kni_rx_burst(m_Device, mBufArray, MAX_BURST_SIZE); - - if (unlikely(!packetsReceived)) - { - return 0; - } - - timespec time; - clock_gettime(CLOCK_REALTIME, &time); - - for (size_t index = 0; index < packetsReceived; ++index) - { - struct rte_mbuf* mBuf = mBufArray[index]; - if (rawPacketsArr[index] == NULL) - rawPacketsArr[index] = new MBufRawPacket(); - - rawPacketsArr[index]->setMBuf(mBuf, time); - } - - return packetsReceived; -} - -uint16_t KniDevice::receivePackets(Packet** packetsArr, uint16_t packetsArrLength) -{ - if (unlikely(!m_DeviceOpened)) - { - PCPP_LOG_ERROR("KNI device '" << m_DeviceInfo.name << "' is not opened"); - return 0; - } - if (unlikely(m_Capturing.isRunning())) - { - PCPP_LOG_ERROR( - "KNI device '" << m_DeviceInfo.name << "' capture mode is currently running. " - "Cannot receive packets in parallel" - ); - return 0; - } - - - struct rte_mbuf** mBufArray = CPP_VLA(struct rte_mbuf*, packetsArrLength); - uint16_t packetsReceived = rte_kni_rx_burst(m_Device, mBufArray, MAX_BURST_SIZE); - - if (unlikely(!packetsReceived)) - { - return 0; - } - - timespec time; - clock_gettime(CLOCK_REALTIME, &time); - - for (size_t index = 0; index < packetsReceived; ++index) - { - struct rte_mbuf* mBuf = mBufArray[index]; - MBufRawPacket* newRawPacket = new MBufRawPacket(); - newRawPacket->setMBuf(mBuf, time); - if (packetsArr[index] == NULL) - packetsArr[index] = new Packet(); - - packetsArr[index]->setRawPacket(newRawPacket, true); - } - - return packetsReceived; -} - -uint16_t KniDevice::sendPackets(MBufRawPacket** rawPacketsArr, uint16_t arrLength) -{ - if (unlikely(!m_DeviceOpened)) - { - PCPP_LOG_ERROR("KNI device '" << m_DeviceInfo.name << "' is not opened"); - return 0; - } - - struct rte_mbuf** mBufArray = CPP_VLA(struct rte_mbuf*, arrLength); - for (uint16_t i = 0; i < arrLength; ++i) - { - mBufArray[i] = rawPacketsArr[i]->getMBuf(); - } - - uint16_t packetsSent = rte_kni_tx_burst(m_Device, mBufArray, arrLength); - for (uint16_t i = 0; i < arrLength; ++i) - { - rawPacketsArr[i]->setFreeMbuf(i >= packetsSent); - } - - return packetsSent; -} - -uint16_t KniDevice::sendPackets(Packet** packetsArr, uint16_t arrLength) -{ - if (unlikely(!m_DeviceOpened)) - { - PCPP_LOG_ERROR("KNI device '" << m_DeviceInfo.name << "' is not opened"); - return 0; - } - - struct rte_mbuf** mBufArray = CPP_VLA(struct rte_mbuf*, arrLength); - MBufRawPacket** mBufRawPacketArr = CPP_VLA(MBufRawPacket*, arrLength); - MBufRawPacket** allocated = CPP_VLA(MBufRawPacket*, arrLength); - uint16_t allocated_count = 0, packetsSent = 0; - MBufRawPacket* rawPacket; - RawPacket* raw_pkt; - - for (uint16_t i = 0; i < arrLength; ++i) - { - raw_pkt = packetsArr[i]->getRawPacketReadOnly(); - uint8_t raw_type = raw_pkt->getObjectType(); - if (raw_type != MBUFRAWPACKET_OBJECT_TYPE) - { - MBufRawPacket* pkt = new MBufRawPacket(); - if (unlikely(!pkt->initFromRawPacket(raw_pkt, this))) - { - delete pkt; - goto error_out; - } - rawPacket = allocated[allocated_count++] = pkt; - } - else - { - rawPacket = (MBufRawPacket*)raw_pkt; - } - mBufRawPacketArr[i] = rawPacket; - mBufArray[i] = rawPacket->getMBuf(); - } - - packetsSent = rte_kni_tx_burst(m_Device, mBufArray, arrLength); - for (uint16_t i = 0; i < arrLength; ++i) - { - mBufRawPacketArr[i]->setFreeMbuf(i >= packetsSent); - } +KniDevice::KniDevice(const KniDeviceConfiguration& conf, size_t mempoolSize, + int unique) + : m_Device(NULL), m_MBufMempool(NULL) { + struct rte_kni_ops kniOps; + struct rte_kni_conf kniConf; + if (!m_DeviceInfo.init(conf)) + return; + m_Requests.thread = NULL; + std::memset(&m_Capturing, 0, sizeof(m_Capturing)); + std::memset(&m_Requests, 0, sizeof(m_Requests)); + + if ((m_MBufMempool = createMempool(mempoolSize, unique, conf.name.c_str())) == + NULL) + return; + + std::memset(&kniOps, 0, sizeof(kniOps)); + std::memset(&kniConf, 0, sizeof(kniConf)); + snprintf(kniConf.name, RTE_KNI_NAMESIZE, "%s", conf.name.c_str()); + kniConf.core_id = conf.kthreadCoreId; + kniConf.mbuf_size = MBufRawPacket::MBUF_DATA_SIZE; + kniConf.force_bind = conf.bindKthread ? 1 : 0; +#if RTE_VERSION >= RTE_VERSION_NUM(18, 2, 0, 0) + if (conf.mac != MacAddress::Zero) + conf.mac.copyTo((uint8_t*)kniConf.mac_addr); + kniConf.mtu = conf.mtu; +#endif + + kniOps.port_id = conf.portId; +#if RTE_VERSION >= RTE_VERSION_NUM(17, 11, 0, 0) + if (conf.callbacks != NULL) { + kniOps.change_mtu = conf.callbacks->change_mtu; + kniOps.config_network_if = conf.callbacks->config_network_if; +#if RTE_VERSION >= RTE_VERSION_NUM(18, 2, 0, 0) + kniOps.config_mac_address = conf.callbacks->config_mac_address; + kniOps.config_promiscusity = conf.callbacks->config_promiscusity; +#endif + } +#else + if (conf.oldCallbacks != NULL) { + kniOps.change_mtu = conf.oldCallbacks->change_mtu; + kniOps.config_network_if = conf.oldCallbacks->config_network_if; + } +#endif + + m_Device = rte_kni_alloc(m_MBufMempool, &kniConf, &kniOps); + if (m_Device == NULL) { + PCPP_LOG_ERROR("DPDK have failed to create KNI device " << conf.name); + } +} + +KniDevice::~KniDevice() { + m_Requests.cleanup(); + m_Capturing.cleanup(); + if (m_Device != NULL) { + setKniDeviceLinkState(m_Device, m_DeviceInfo.name.c_str(), + KniDevice::LINK_DOWN); + destroyKniDevice(m_Device, m_DeviceInfo.name.c_str()); + } + if (m_MBufMempool != NULL) + rte_mempool_free(m_MBufMempool); +} + +bool KniDevice::KniDeviceInfo::init(const KniDeviceConfiguration& conf) { + link = KniDevice::LINK_NOT_SUPPORTED; + promisc = KniDevice::PROMISC_DISABLE; + portId = conf.portId; + mtu = conf.mtu; + if (conf.name.empty()) { + PCPP_LOG_ERROR("Failed to create KNI device. " + "Empty name provided"); + return false; + } + if (conf.name.size() >= IFNAMSIZ) { + PCPP_LOG_ERROR("Failed to create KNI device. " + "Provided name has length more than possible to handle <" + << IFNAMSIZ - 1 << ">"); + return false; + } + name = conf.name; + mac = conf.mac; + return true; +} + +KniDevice::KniLinkState KniDevice::getLinkState(KniInfoState state) { + if (state == KniDevice::INFO_CACHED) + return m_DeviceInfo.link; + struct ifreq req; + std::memset(&req, 0, sizeof(req)); + if (!m_DeviceInfo.soc.makeRequest(m_DeviceInfo.name.c_str(), SIOCGIFFLAGS, + &req)) { + PCPP_LOG_ERROR("DPDK KNI failed to obtain interface link state from Linux"); + PCPP_LOG_DEBUG("Last known link state for device '" << m_DeviceInfo.name + << "' is returned"); + return m_DeviceInfo.link; + } + return m_DeviceInfo.link = KniLinkState(req.ifr_flags & IFF_UP); +} + +MacAddress KniDevice::getMacAddress(KniInfoState state) { + if (state == KniDevice::INFO_CACHED) + return m_DeviceInfo.mac; + struct ifreq req; + std::memset(&req, 0, sizeof(req)); + req.ifr_hwaddr.sa_family = ARPHRD_ETHER; + if (!m_DeviceInfo.soc.makeRequest(m_DeviceInfo.name.c_str(), SIOCGIFHWADDR, + &req)) { + PCPP_LOG_ERROR("DPDK KNI failed to obtain MAC address from Linux"); + PCPP_LOG_DEBUG("Last known MAC address for device '" << m_DeviceInfo.name + << "' is returned"); + return m_DeviceInfo.mac; + } + return m_DeviceInfo.mac = MacAddress((uint8_t*)req.ifr_hwaddr.sa_data); +} + +uint16_t KniDevice::getMtu(KniInfoState state) { + if (state == KniDevice::INFO_CACHED) + return m_DeviceInfo.mtu; + struct ifreq req; + std::memset(&req, 0, sizeof(req)); + if (!m_DeviceInfo.soc.makeRequest(m_DeviceInfo.name.c_str(), SIOCGIFMTU, + &req)) { + PCPP_LOG_ERROR("DPDK KNI failed to obtain interface MTU from Linux"); + PCPP_LOG_DEBUG("Last known MTU for device '" << m_DeviceInfo.name + << "' is returned"); + return m_DeviceInfo.mtu; + } + return m_DeviceInfo.mtu = req.ifr_mtu; +} + +KniDevice::KniPromiscuousMode KniDevice::getPromiscuous(KniInfoState state) { + if (state == KniDevice::INFO_CACHED) + return m_DeviceInfo.promisc; + struct ifreq req; + std::memset(&req, 0, sizeof(req)); + if (!m_DeviceInfo.soc.makeRequest(m_DeviceInfo.name.c_str(), SIOCGIFFLAGS, + &req)) { + PCPP_LOG_ERROR( + "DPDK KNI failed to obtain interface Promiscuous mode from Linux"); + PCPP_LOG_DEBUG("Last known Promiscuous mode for device '" + << m_DeviceInfo.name << "' is returned"); + return m_DeviceInfo.promisc; + } + return m_DeviceInfo.promisc = (req.ifr_flags & IFF_PROMISC) + ? KniDevice::PROMISC_ENABLE + : KniDevice::PROMISC_DISABLE; +} + +bool KniDevice::setLinkState(KniLinkState state) { + if (!(state == KniDevice::LINK_DOWN || state == KniDevice::LINK_UP)) + return false; + struct ifreq req; + std::memset(&req, 0, sizeof(req)); + if (!m_DeviceInfo.soc.makeRequest(m_DeviceInfo.name.c_str(), SIOCGIFFLAGS, + &req)) { + PCPP_LOG_ERROR("DPDK KNI failed to obtain interface flags from Linux"); + return false; + } + if ((state == KniDevice::LINK_DOWN && req.ifr_flags & IFF_UP) || + (state == KniDevice::LINK_UP && !(req.ifr_flags & IFF_UP))) { + req.ifr_flags ^= IFF_UP; + if (!m_DeviceInfo.soc.makeRequest(m_DeviceInfo.name.c_str(), SIOCSIFFLAGS, + &req)) { + PCPP_LOG_ERROR("DPDK KNI failed to set '" << m_DeviceInfo.name + << "' link mode"); + return false; + } + } + m_DeviceInfo.link = state; + return true; +} + +bool KniDevice::setMacAddress(MacAddress mac) { + if (!mac.isValid()) + return false; + struct ifreq req; + std::memset(&req, 0, sizeof(req)); + req.ifr_hwaddr.sa_family = ARPHRD_ETHER; + mac.copyTo((uint8_t*)req.ifr_hwaddr.sa_data); + if (!m_DeviceInfo.soc.makeRequest(m_DeviceInfo.name.c_str(), SIOCSIFHWADDR, + &req)) { + PCPP_LOG_ERROR("DPDK KNI failed to set MAC address"); + return false; + } + m_DeviceInfo.mac = mac; + return true; +} + +bool KniDevice::setMtu(uint16_t mtu) { + struct ifreq req; + std::memset(&req, 0, sizeof(req)); + req.ifr_mtu = mtu; + if (!m_DeviceInfo.soc.makeRequest(m_DeviceInfo.name.c_str(), SIOCSIFMTU, + &req)) { + PCPP_LOG_ERROR("DPDK KNI failed to set interface MTU"); + return false; + } + m_DeviceInfo.mtu = mtu; + return true; +} + +bool KniDevice::setPromiscuous(KniPromiscuousMode mode) { + if (!(mode == KniDevice::PROMISC_DISABLE || + mode == KniDevice::PROMISC_ENABLE)) + return false; + struct ifreq req; + std::memset(&req, 0, sizeof(req)); + if (!m_DeviceInfo.soc.makeRequest(m_DeviceInfo.name.c_str(), SIOCGIFFLAGS, + &req)) { + PCPP_LOG_ERROR("DPDK KNI failed to obtain interface flags from Linux"); + return false; + } + if ((mode == KniDevice::PROMISC_DISABLE && req.ifr_flags & IFF_PROMISC) || + (mode == KniDevice::PROMISC_ENABLE && !(req.ifr_flags & IFF_PROMISC))) { + req.ifr_flags ^= IFF_PROMISC; + if (!m_DeviceInfo.soc.makeRequest(m_DeviceInfo.name.c_str(), SIOCSIFFLAGS, + &req)) { + PCPP_LOG_ERROR("DPDK KNI failed to set '" << m_DeviceInfo.name + << "' link mode"); + return false; + } + } + m_DeviceInfo.promisc = mode; + return true; +} + +KniDevice::KniLinkState KniDevice::updateLinkState(KniLinkState state) { + KniLinkState oldState = + setKniDeviceLinkState(m_Device, m_DeviceInfo.name.c_str(), state); + if (oldState != KniDevice::LINK_NOT_SUPPORTED && + oldState != KniDevice::LINK_ERROR) + m_DeviceInfo.link = state; + return oldState; +} + +bool KniDevice::handleRequests() { + return rte_kni_handle_request(m_Device) == 0; +} + +void KniDevice::KniRequests::cleanup() { + if (thread) + thread->cancel(); + delete thread; + thread = NULL; + sleepS = sleepNs = 0; +} + +void KniDevice::KniRequests::runRequests(void* devicePointer, + std::atomic& stopThread) { + KniDevice* device = (KniDevice*)devicePointer; + struct timespec sleepTime; + sleepTime.tv_sec = device->m_Requests.sleepS; + sleepTime.tv_nsec = device->m_Requests.sleepNs; + struct rte_kni* kni_dev = device->m_Device; + for (;;) { + nanosleep(&sleepTime, NULL); + rte_kni_handle_request(kni_dev); + if (stopThread) { + return; + } + } +} + +bool KniDevice::startRequestHandlerThread(long sleepSeconds, + long sleepNanoSeconds) { + if (m_Requests.thread != NULL) { + PCPP_LOG_ERROR("KNI request thread is already started for device '" + << m_DeviceInfo.name << "'"); + return false; + } + m_Requests.sleepS = sleepSeconds; + m_Requests.sleepNs = sleepNanoSeconds; + m_Requests.thread = new KniThread(KniThread::DETACHED, + KniRequests::runRequests, (void*)this); + if (m_Requests.thread->m_CleanupState == KniThread::INVALID) { + m_Requests.cleanup(); + return false; + } + return true; +} + +void KniDevice::stopRequestHandlerThread() { + if (m_Requests.thread == NULL) { + PCPP_LOG_DEBUG("Attempt to stop not running KNI request thread for device '" + << m_DeviceInfo.name << "'"); + return; + } + m_Requests.cleanup(); +} + +uint16_t KniDevice::receivePackets(MBufRawPacketVector& rawPacketsArr) { + if (unlikely(!m_DeviceOpened)) { + PCPP_LOG_ERROR("KNI device '" << m_DeviceInfo.name << "' is not opened"); + return 0; + } + if (unlikely(m_Capturing.isRunning())) { + PCPP_LOG_ERROR("KNI device '" << m_DeviceInfo.name + << "' capture mode is currently running. " + "Cannot receive packets in parallel"); + return 0; + } + + struct rte_mbuf* mBufArray[MAX_BURST_SIZE]; + uint32_t numOfPktsReceived = + rte_kni_rx_burst(m_Device, mBufArray, MAX_BURST_SIZE); + + // the following line trashes the log with many messages. Uncomment only if + // necessary PCPP_LOG_DEBUG("KNI Captured %d packets", numOfPktsReceived); + + if (unlikely(!numOfPktsReceived)) { + return 0; + } + + timespec time; + clock_gettime(CLOCK_REALTIME, &time); + + for (uint32_t index = 0; index < numOfPktsReceived; ++index) { + struct rte_mbuf* mBuf = mBufArray[index]; + MBufRawPacket* newRawPacket = new MBufRawPacket(); + newRawPacket->setMBuf(mBuf, time); + rawPacketsArr.pushBack(newRawPacket); + } + + return numOfPktsReceived; +} + +uint16_t KniDevice::receivePackets(MBufRawPacket** rawPacketsArr, + uint16_t rawPacketArrLength) { + if (unlikely(!m_DeviceOpened)) { + PCPP_LOG_ERROR("KNI device '" << m_DeviceInfo.name << "' is not opened"); + return 0; + } + if (unlikely(m_Capturing.isRunning())) { + PCPP_LOG_ERROR("KNI device '" << m_DeviceInfo.name + << "' capture mode is currently running. " + "Cannot receive packets in parallel"); + return 0; + } + if (unlikely(rawPacketsArr == NULL)) { + PCPP_LOG_ERROR("KNI Provided address of array to store packets is NULL"); + return 0; + } + + struct rte_mbuf** mBufArray = CPP_VLA(struct rte_mbuf*, rawPacketArrLength); + uint16_t packetsReceived = + rte_kni_rx_burst(m_Device, mBufArray, MAX_BURST_SIZE); + + if (unlikely(!packetsReceived)) { + return 0; + } + + timespec time; + clock_gettime(CLOCK_REALTIME, &time); + + for (size_t index = 0; index < packetsReceived; ++index) { + struct rte_mbuf* mBuf = mBufArray[index]; + if (rawPacketsArr[index] == NULL) + rawPacketsArr[index] = new MBufRawPacket(); + + rawPacketsArr[index]->setMBuf(mBuf, time); + } + + return packetsReceived; +} + +uint16_t KniDevice::receivePackets(Packet** packetsArr, + uint16_t packetsArrLength) { + if (unlikely(!m_DeviceOpened)) { + PCPP_LOG_ERROR("KNI device '" << m_DeviceInfo.name << "' is not opened"); + return 0; + } + if (unlikely(m_Capturing.isRunning())) { + PCPP_LOG_ERROR("KNI device '" << m_DeviceInfo.name + << "' capture mode is currently running. " + "Cannot receive packets in parallel"); + return 0; + } + + struct rte_mbuf** mBufArray = CPP_VLA(struct rte_mbuf*, packetsArrLength); + uint16_t packetsReceived = + rte_kni_rx_burst(m_Device, mBufArray, MAX_BURST_SIZE); + + if (unlikely(!packetsReceived)) { + return 0; + } + + timespec time; + clock_gettime(CLOCK_REALTIME, &time); + + for (size_t index = 0; index < packetsReceived; ++index) { + struct rte_mbuf* mBuf = mBufArray[index]; + MBufRawPacket* newRawPacket = new MBufRawPacket(); + newRawPacket->setMBuf(mBuf, time); + if (packetsArr[index] == NULL) + packetsArr[index] = new Packet(); + + packetsArr[index]->setRawPacket(newRawPacket, true); + } + + return packetsReceived; +} + +uint16_t KniDevice::sendPackets(MBufRawPacket** rawPacketsArr, + uint16_t arrLength) { + if (unlikely(!m_DeviceOpened)) { + PCPP_LOG_ERROR("KNI device '" << m_DeviceInfo.name << "' is not opened"); + return 0; + } + + struct rte_mbuf** mBufArray = CPP_VLA(struct rte_mbuf*, arrLength); + for (uint16_t i = 0; i < arrLength; ++i) { + mBufArray[i] = rawPacketsArr[i]->getMBuf(); + } + + uint16_t packetsSent = rte_kni_tx_burst(m_Device, mBufArray, arrLength); + for (uint16_t i = 0; i < arrLength; ++i) { + rawPacketsArr[i]->setFreeMbuf(i >= packetsSent); + } + + return packetsSent; +} + +uint16_t KniDevice::sendPackets(Packet** packetsArr, uint16_t arrLength) { + if (unlikely(!m_DeviceOpened)) { + PCPP_LOG_ERROR("KNI device '" << m_DeviceInfo.name << "' is not opened"); + return 0; + } + + struct rte_mbuf** mBufArray = CPP_VLA(struct rte_mbuf*, arrLength); + MBufRawPacket** mBufRawPacketArr = CPP_VLA(MBufRawPacket*, arrLength); + MBufRawPacket** allocated = CPP_VLA(MBufRawPacket*, arrLength); + uint16_t allocated_count = 0, packetsSent = 0; + MBufRawPacket* rawPacket; + RawPacket* raw_pkt; + + for (uint16_t i = 0; i < arrLength; ++i) { + raw_pkt = packetsArr[i]->getRawPacketReadOnly(); + uint8_t raw_type = raw_pkt->getObjectType(); + if (raw_type != MBUFRAWPACKET_OBJECT_TYPE) { + MBufRawPacket* pkt = new MBufRawPacket(); + if (unlikely(!pkt->initFromRawPacket(raw_pkt, this))) { + delete pkt; + goto error_out; + } + rawPacket = allocated[allocated_count++] = pkt; + } else { + rawPacket = (MBufRawPacket*)raw_pkt; + } + mBufRawPacketArr[i] = rawPacket; + mBufArray[i] = rawPacket->getMBuf(); + } + + packetsSent = rte_kni_tx_burst(m_Device, mBufArray, arrLength); + for (uint16_t i = 0; i < arrLength; ++i) { + mBufRawPacketArr[i]->setFreeMbuf(i >= packetsSent); + } error_out: - for (uint16_t i = 0; i < allocated_count; ++i) - delete allocated[i]; - return packetsSent; -} - -uint16_t KniDevice::sendPackets(MBufRawPacketVector& rawPacketsVec) -{ - if (unlikely(!m_DeviceOpened)) - { - PCPP_LOG_ERROR("KNI device '" << m_DeviceInfo.name << "' is not opened"); - return 0; - } - - size_t arrLength = rawPacketsVec.size(); - struct rte_mbuf** mBufArray = CPP_VLA(struct rte_mbuf*, arrLength); - uint16_t pos = 0; - for (MBufRawPacketVector::VectorIterator iter = rawPacketsVec.begin(); iter != rawPacketsVec.end(); ++iter) - { - mBufArray[pos] = (*iter)->getMBuf(); - ++pos; - } - - uint16_t packetsSent = rte_kni_tx_burst(m_Device, mBufArray, arrLength); - pos = 0; - for (MBufRawPacketVector::VectorIterator iter = rawPacketsVec.begin(); iter != rawPacketsVec.end(); ++iter) - { - (*iter)->setFreeMbuf(pos >= packetsSent); - ++pos; - } - - return packetsSent; -} - -uint16_t KniDevice::sendPackets(RawPacketVector& rawPacketsVec) -{ - if (unlikely(!m_DeviceOpened)) - { - PCPP_LOG_ERROR("KNI device '" << m_DeviceInfo.name << "' is not opened"); - return 0; - } - - size_t arrLength = rawPacketsVec.size(); - struct rte_mbuf** mBufArray = CPP_VLA(struct rte_mbuf*, arrLength); - MBufRawPacket** mBufRawPacketArr = CPP_VLA(MBufRawPacket*, arrLength); - MBufRawPacket** allocated = CPP_VLA(MBufRawPacket*, arrLength); - uint16_t allocatedCount = 0, packetsSent = 0, pos = 0; - MBufRawPacket* rawPacket; - - for (RawPacketVector::VectorIterator iter = rawPacketsVec.begin(); iter != rawPacketsVec.end(); ++iter) - { - uint8_t raw_type = (*iter)->getObjectType(); - if (raw_type != MBUFRAWPACKET_OBJECT_TYPE) - { - MBufRawPacket* pkt = new MBufRawPacket(); - if (unlikely(!pkt->initFromRawPacket(*iter, this))) - { - delete pkt; - goto error_out; - } - rawPacket = allocated[allocatedCount++] = pkt; - } - else - { - rawPacket = (MBufRawPacket*)(*iter); - } - mBufRawPacketArr[pos] = rawPacket; - mBufArray[pos] = rawPacket->getMBuf(); - ++pos; - } - - packetsSent = rte_kni_tx_burst(m_Device, mBufArray, arrLength); - for (uint16_t i = 0; i < arrLength; ++i) - { - mBufRawPacketArr[i]->setFreeMbuf(i >= packetsSent); - } + for (uint16_t i = 0; i < allocated_count; ++i) + delete allocated[i]; + return packetsSent; +} + +uint16_t KniDevice::sendPackets(MBufRawPacketVector& rawPacketsVec) { + if (unlikely(!m_DeviceOpened)) { + PCPP_LOG_ERROR("KNI device '" << m_DeviceInfo.name << "' is not opened"); + return 0; + } + + size_t arrLength = rawPacketsVec.size(); + struct rte_mbuf** mBufArray = CPP_VLA(struct rte_mbuf*, arrLength); + uint16_t pos = 0; + for (MBufRawPacketVector::VectorIterator iter = rawPacketsVec.begin(); + iter != rawPacketsVec.end(); ++iter) { + mBufArray[pos] = (*iter)->getMBuf(); + ++pos; + } + + uint16_t packetsSent = rte_kni_tx_burst(m_Device, mBufArray, arrLength); + pos = 0; + for (MBufRawPacketVector::VectorIterator iter = rawPacketsVec.begin(); + iter != rawPacketsVec.end(); ++iter) { + (*iter)->setFreeMbuf(pos >= packetsSent); + ++pos; + } + + return packetsSent; +} + +uint16_t KniDevice::sendPackets(RawPacketVector& rawPacketsVec) { + if (unlikely(!m_DeviceOpened)) { + PCPP_LOG_ERROR("KNI device '" << m_DeviceInfo.name << "' is not opened"); + return 0; + } + + size_t arrLength = rawPacketsVec.size(); + struct rte_mbuf** mBufArray = CPP_VLA(struct rte_mbuf*, arrLength); + MBufRawPacket** mBufRawPacketArr = CPP_VLA(MBufRawPacket*, arrLength); + MBufRawPacket** allocated = CPP_VLA(MBufRawPacket*, arrLength); + uint16_t allocatedCount = 0, packetsSent = 0, pos = 0; + MBufRawPacket* rawPacket; + + for (RawPacketVector::VectorIterator iter = rawPacketsVec.begin(); + iter != rawPacketsVec.end(); ++iter) { + uint8_t raw_type = (*iter)->getObjectType(); + if (raw_type != MBUFRAWPACKET_OBJECT_TYPE) { + MBufRawPacket* pkt = new MBufRawPacket(); + if (unlikely(!pkt->initFromRawPacket(*iter, this))) { + delete pkt; + goto error_out; + } + rawPacket = allocated[allocatedCount++] = pkt; + } else { + rawPacket = (MBufRawPacket*)(*iter); + } + mBufRawPacketArr[pos] = rawPacket; + mBufArray[pos] = rawPacket->getMBuf(); + ++pos; + } + + packetsSent = rte_kni_tx_burst(m_Device, mBufArray, arrLength); + for (uint16_t i = 0; i < arrLength; ++i) { + mBufRawPacketArr[i]->setFreeMbuf(i >= packetsSent); + } error_out: - for (uint16_t i = 0; i < allocatedCount; ++i) - delete allocated[i]; - return packetsSent; -} - -bool KniDevice::sendPacket(RawPacket& rawPacket) -{ - if (unlikely(!m_DeviceOpened)) - { - PCPP_LOG_ERROR("KNI device '" << m_DeviceInfo.name << "' is not opened"); - return 0; - } - - struct rte_mbuf* mbuf; - MBufRawPacket* mbufRawPacket = NULL; - bool sent = false; - bool wasAllocated = false; - - if (rawPacket.getObjectType() != MBUFRAWPACKET_OBJECT_TYPE) - { - mbufRawPacket = new MBufRawPacket(); - if (unlikely(!mbufRawPacket->initFromRawPacket(&rawPacket, this))) - { - delete mbufRawPacket; - return sent; - } - mbuf = mbufRawPacket->getMBuf(); - wasAllocated = true; - } - else - { - mbufRawPacket = (MBufRawPacket*)(&rawPacket); - mbuf = mbufRawPacket->getMBuf(); - } - - sent = rte_kni_tx_burst(m_Device, &mbuf, 1); - mbufRawPacket->setFreeMbuf(!sent); - if (wasAllocated) - delete mbufRawPacket; - - return sent; -} - -bool KniDevice::sendPacket(MBufRawPacket& rawPacket) -{ - if (unlikely(!m_DeviceOpened)) - { - PCPP_LOG_ERROR("KNI device '" << m_DeviceInfo.name << "' is not opened"); - return 0; - } - - struct rte_mbuf* mbuf = rawPacket.getMBuf(); - bool sent = false; - - sent = rte_kni_tx_burst(m_Device, &mbuf, 1); - rawPacket.setFreeMbuf(!sent); - - return sent; -} - -bool KniDevice::sendPacket(Packet& packet) -{ - return sendPacket(*packet.getRawPacket()); -} - -void KniDevice::KniCapturing::runCapture(void* devicePointer, std::atomic& stopThread) -{ - KniDevice* device = (KniDevice*)devicePointer; - OnKniPacketArriveCallback callback = device->m_Capturing.callback; - void* userCookie = device->m_Capturing.userCookie; - struct rte_mbuf* mBufArray[MAX_BURST_SIZE]; - struct rte_kni* kni = device->m_Device; - - PCPP_LOG_DEBUG("Starting KNI capture thread for device '" << device->m_DeviceInfo.name << "'"); - - for(;;) - { - uint32_t numOfPktsReceived = rte_kni_rx_burst(kni, mBufArray, MAX_BURST_SIZE); - if (unlikely(numOfPktsReceived == 0)) - { - if (stopThread) - { - return; - } - continue; - } - - timespec time; - clock_gettime(CLOCK_REALTIME, &time); - - if (likely(callback != NULL)) - { - MBufRawPacket rawPackets[MAX_BURST_SIZE]; - for (uint32_t index = 0; index < numOfPktsReceived; ++index) - { - rawPackets[index].setMBuf(mBufArray[index], time); - } - - if (!callback(rawPackets, numOfPktsReceived, device, userCookie)) - break; - } - if (stopThread) - { - return; - } - } -} - -void KniDevice::KniCapturing::cleanup() -{ - if (thread) - thread->cancel(); - delete thread; - thread = NULL; - callback = NULL; - userCookie = NULL; -} - -bool KniDevice::startCapture( - OnKniPacketArriveCallback onPacketArrives, - void* onPacketArrivesUserCookie -) -{ - if (unlikely(!m_DeviceOpened)) - { - PCPP_LOG_ERROR("KNI device '" << m_DeviceInfo.name << "' is not opened. Can't start capture"); - return false; - } - if (unlikely(m_Capturing.thread != NULL)) - { - PCPP_LOG_ERROR("KNI device '" << m_DeviceInfo.name << "' is already capturing"); - return false; - } - - m_Capturing.callback = onPacketArrives; - m_Capturing.userCookie = onPacketArrivesUserCookie; - - m_Capturing.thread = new KniThread(KniThread::JOINABLE, KniCapturing::runCapture, (void*)this); - if (m_Capturing.thread->m_CleanupState == KniThread::INVALID) - { - PCPP_LOG_DEBUG("KNI failed to start capturing thread on device '" << m_DeviceInfo.name << "'"); - delete m_Capturing.thread; - return false; - } - - return true; -} - -void KniDevice::stopCapture() -{ - if (m_Capturing.thread == NULL) - { - PCPP_LOG_DEBUG("Attempt to stop not running KNI capturing thread for device '" << m_DeviceInfo.name << "'"); - return; - } - m_Capturing.cleanup(); + for (uint16_t i = 0; i < allocatedCount; ++i) + delete allocated[i]; + return packetsSent; +} + +bool KniDevice::sendPacket(RawPacket& rawPacket) { + if (unlikely(!m_DeviceOpened)) { + PCPP_LOG_ERROR("KNI device '" << m_DeviceInfo.name << "' is not opened"); + return 0; + } + + struct rte_mbuf* mbuf; + MBufRawPacket* mbufRawPacket = NULL; + bool sent = false; + bool wasAllocated = false; + + if (rawPacket.getObjectType() != MBUFRAWPACKET_OBJECT_TYPE) { + mbufRawPacket = new MBufRawPacket(); + if (unlikely(!mbufRawPacket->initFromRawPacket(&rawPacket, this))) { + delete mbufRawPacket; + return sent; + } + mbuf = mbufRawPacket->getMBuf(); + wasAllocated = true; + } else { + mbufRawPacket = (MBufRawPacket*)(&rawPacket); + mbuf = mbufRawPacket->getMBuf(); + } + + sent = rte_kni_tx_burst(m_Device, &mbuf, 1); + mbufRawPacket->setFreeMbuf(!sent); + if (wasAllocated) + delete mbufRawPacket; + + return sent; +} + +bool KniDevice::sendPacket(MBufRawPacket& rawPacket) { + if (unlikely(!m_DeviceOpened)) { + PCPP_LOG_ERROR("KNI device '" << m_DeviceInfo.name << "' is not opened"); + return 0; + } + + struct rte_mbuf* mbuf = rawPacket.getMBuf(); + bool sent = false; + + sent = rte_kni_tx_burst(m_Device, &mbuf, 1); + rawPacket.setFreeMbuf(!sent); + + return sent; +} + +bool KniDevice::sendPacket(Packet& packet) { + return sendPacket(*packet.getRawPacket()); +} + +void KniDevice::KniCapturing::runCapture(void* devicePointer, + std::atomic& stopThread) { + KniDevice* device = (KniDevice*)devicePointer; + OnKniPacketArriveCallback callback = device->m_Capturing.callback; + void* userCookie = device->m_Capturing.userCookie; + struct rte_mbuf* mBufArray[MAX_BURST_SIZE]; + struct rte_kni* kni = device->m_Device; + + PCPP_LOG_DEBUG("Starting KNI capture thread for device '" + << device->m_DeviceInfo.name << "'"); + + for (;;) { + uint32_t numOfPktsReceived = + rte_kni_rx_burst(kni, mBufArray, MAX_BURST_SIZE); + if (unlikely(numOfPktsReceived == 0)) { + if (stopThread) { + return; + } + continue; + } + + timespec time; + clock_gettime(CLOCK_REALTIME, &time); + + if (likely(callback != NULL)) { + MBufRawPacket rawPackets[MAX_BURST_SIZE]; + for (uint32_t index = 0; index < numOfPktsReceived; ++index) { + rawPackets[index].setMBuf(mBufArray[index], time); + } + + if (!callback(rawPackets, numOfPktsReceived, device, userCookie)) + break; + } + if (stopThread) { + return; + } + } +} + +void KniDevice::KniCapturing::cleanup() { + if (thread) + thread->cancel(); + delete thread; + thread = NULL; + callback = NULL; + userCookie = NULL; +} + +bool KniDevice::startCapture(OnKniPacketArriveCallback onPacketArrives, + void* onPacketArrivesUserCookie) { + if (unlikely(!m_DeviceOpened)) { + PCPP_LOG_ERROR("KNI device '" << m_DeviceInfo.name + << "' is not opened. Can't start capture"); + return false; + } + if (unlikely(m_Capturing.thread != NULL)) { + PCPP_LOG_ERROR("KNI device '" << m_DeviceInfo.name + << "' is already capturing"); + return false; + } + + m_Capturing.callback = onPacketArrives; + m_Capturing.userCookie = onPacketArrivesUserCookie; + + m_Capturing.thread = new KniThread(KniThread::JOINABLE, + KniCapturing::runCapture, (void*)this); + if (m_Capturing.thread->m_CleanupState == KniThread::INVALID) { + PCPP_LOG_DEBUG("KNI failed to start capturing thread on device '" + << m_DeviceInfo.name << "'"); + delete m_Capturing.thread; + return false; + } + + return true; +} + +void KniDevice::stopCapture() { + if (m_Capturing.thread == NULL) { + PCPP_LOG_DEBUG( + "Attempt to stop not running KNI capturing thread for device '" + << m_DeviceInfo.name << "'"); + return; + } + m_Capturing.cleanup(); } int KniDevice::startCaptureBlockingMode( - OnKniPacketArriveCallback onPacketArrives, - void* onPacketArrivesUserCookie, - int timeout -) -{ - if (unlikely(!m_DeviceOpened)) - { - PCPP_LOG_ERROR("KNI device '" << m_DeviceInfo.name << "' is not opened. Can't start capture"); - return 0; - } - if (unlikely(m_Capturing.thread != NULL)) - { - PCPP_LOG_ERROR("KNI device '" << m_DeviceInfo.name << "' is already capturing"); - return 0; - } - m_Capturing.callback = onPacketArrives; - m_Capturing.userCookie = onPacketArrivesUserCookie; - if (unlikely(m_Capturing.callback == NULL)) - { - PCPP_LOG_ERROR("Attempt to start KNI device '" << m_DeviceInfo.name << "' capturing in blocking mode without callback"); - return 0; - } - - struct rte_mbuf* mBufArray[MAX_BURST_SIZE]; - - if (timeout <= 0) - { - for(;;) - { - uint32_t numOfPktsReceived = rte_kni_rx_burst(m_Device, mBufArray, MAX_BURST_SIZE); - if (likely(numOfPktsReceived != 0)) - { - MBufRawPacket rawPackets[MAX_BURST_SIZE]; - timespec time; - clock_gettime(CLOCK_REALTIME, &time); - - for (uint32_t index = 0; index < numOfPktsReceived; ++index) - { - rawPackets[index].setMBuf(mBufArray[index], time); - } - - if (!m_Capturing.callback(rawPackets, numOfPktsReceived, this, m_Capturing.userCookie)) - return 1; - } - } - } - else - { - long startTimeSec = 0, startTimeNSec = 0; - long curTimeSec = 0, curTimeNSec = 0; - clockGetTime(startTimeSec, startTimeNSec); - - while(curTimeSec <= (startTimeSec + timeout)) - { - clockGetTime(curTimeSec, curTimeNSec); - uint32_t numOfPktsReceived = rte_kni_rx_burst(m_Device, mBufArray, MAX_BURST_SIZE); - if (likely(numOfPktsReceived != 0)) - { - MBufRawPacket rawPackets[MAX_BURST_SIZE]; - timespec time; - time.tv_sec = curTimeSec; - time.tv_nsec = curTimeNSec; - - for (uint32_t index = 0; index < numOfPktsReceived; ++index) - { - rawPackets[index].setMBuf(mBufArray[index], time); - } - - if (!m_Capturing.callback(rawPackets, numOfPktsReceived, this, m_Capturing.userCookie)) - return 1; - } - } - } - return -1; -} - -bool KniDevice::open() -{ - if (unlikely(m_DeviceOpened)) - { - PCPP_LOG_ERROR("KNI device '" << m_DeviceInfo.name << "' is already opened"); - return false; - } - (void) updateLinkState(LINK_UP); - switch (m_DeviceInfo.link) - { - case LINK_ERROR: - return m_DeviceOpened = false; - case LINK_NOT_SUPPORTED: - /* fall through */ - case LINK_DOWN: - /* fall through */ - case LINK_UP: - return m_DeviceOpened = true; - } - return false; -} - -void KniDevice::close() -{ - if (m_Capturing.thread != NULL) - { - m_Capturing.cleanup(); - } - updateLinkState(LINK_DOWN); - m_DeviceOpened = false; + OnKniPacketArriveCallback onPacketArrives, void* onPacketArrivesUserCookie, + int timeout) { + if (unlikely(!m_DeviceOpened)) { + PCPP_LOG_ERROR("KNI device '" << m_DeviceInfo.name + << "' is not opened. Can't start capture"); + return 0; + } + if (unlikely(m_Capturing.thread != NULL)) { + PCPP_LOG_ERROR("KNI device '" << m_DeviceInfo.name + << "' is already capturing"); + return 0; + } + m_Capturing.callback = onPacketArrives; + m_Capturing.userCookie = onPacketArrivesUserCookie; + if (unlikely(m_Capturing.callback == NULL)) { + PCPP_LOG_ERROR("Attempt to start KNI device '" + << m_DeviceInfo.name + << "' capturing in blocking mode without callback"); + return 0; + } + + struct rte_mbuf* mBufArray[MAX_BURST_SIZE]; + + if (timeout <= 0) { + for (;;) { + uint32_t numOfPktsReceived = + rte_kni_rx_burst(m_Device, mBufArray, MAX_BURST_SIZE); + if (likely(numOfPktsReceived != 0)) { + MBufRawPacket rawPackets[MAX_BURST_SIZE]; + timespec time; + clock_gettime(CLOCK_REALTIME, &time); + + for (uint32_t index = 0; index < numOfPktsReceived; ++index) { + rawPackets[index].setMBuf(mBufArray[index], time); + } + + if (!m_Capturing.callback(rawPackets, numOfPktsReceived, this, + m_Capturing.userCookie)) + return 1; + } + } + } else { + long startTimeSec = 0, startTimeNSec = 0; + long curTimeSec = 0, curTimeNSec = 0; + clockGetTime(startTimeSec, startTimeNSec); + + while (curTimeSec <= (startTimeSec + timeout)) { + clockGetTime(curTimeSec, curTimeNSec); + uint32_t numOfPktsReceived = + rte_kni_rx_burst(m_Device, mBufArray, MAX_BURST_SIZE); + if (likely(numOfPktsReceived != 0)) { + MBufRawPacket rawPackets[MAX_BURST_SIZE]; + timespec time; + time.tv_sec = curTimeSec; + time.tv_nsec = curTimeNSec; + + for (uint32_t index = 0; index < numOfPktsReceived; ++index) { + rawPackets[index].setMBuf(mBufArray[index], time); + } + + if (!m_Capturing.callback(rawPackets, numOfPktsReceived, this, + m_Capturing.userCookie)) + return 1; + } + } + } + return -1; +} + +bool KniDevice::open() { + if (unlikely(m_DeviceOpened)) { + PCPP_LOG_ERROR("KNI device '" << m_DeviceInfo.name + << "' is already opened"); + return false; + } + (void)updateLinkState(LINK_UP); + switch (m_DeviceInfo.link) { + case LINK_ERROR: + return m_DeviceOpened = false; + case LINK_NOT_SUPPORTED: + /* fall through */ + case LINK_DOWN: + /* fall through */ + case LINK_UP: + return m_DeviceOpened = true; + } + return false; +} + +void KniDevice::close() { + if (m_Capturing.thread != NULL) { + m_Capturing.cleanup(); + } + updateLinkState(LINK_DOWN); + m_DeviceOpened = false; } } // namespace pcpp diff --git a/Pcap++/src/KniDeviceList.cpp b/Pcap++/src/KniDeviceList.cpp index 546cb3a607..9a2ab62a77 100644 --- a/Pcap++/src/KniDeviceList.cpp +++ b/Pcap++/src/KniDeviceList.cpp @@ -4,24 +4,23 @@ #define LOG_MODULE PcapLogModuleKniDevice -#include #include +#include #include "KniDeviceList.h" #include "Logger.h" #include "SystemUtils.h" -#include #include +#include #ifndef MAX_KNI_DEVICES // This value have no meaning in current DPDK implementation (ver >= 18.11) // In older versions have literal meaning -# define MAX_KNI_DEVICES 4 +#define MAX_KNI_DEVICES 4 #endif -namespace pcpp -{ +namespace pcpp { /** * =================== @@ -29,157 +28,134 @@ namespace pcpp * =================== */ -static inline bool checkKniDriver() -{ - std::string execResult = executeShellCommand("lsmod | grep -s rte_kni"); - if (execResult == "") - { - PCPP_LOG_ERROR("rte_kni driver isn't loaded, DPDK KNI module cannot be initialized"); - return false; - } - PCPP_LOG_DEBUG("rte_kni driver is loaded"); - return true; +static inline bool checkKniDriver() { + std::string execResult = executeShellCommand("lsmod | grep -s rte_kni"); + if (execResult == "") { + PCPP_LOG_ERROR( + "rte_kni driver isn't loaded, DPDK KNI module cannot be initialized"); + return false; + } + PCPP_LOG_DEBUG("rte_kni driver is loaded"); + return true; } -KniDeviceList::KniDeviceList() : - m_Devices(), - m_Initialized(true), m_KniUniqueId(0) -{ - m_Devices.reserve(MAX_KNI_DEVICES); - if (!checkKniDriver()) - { - m_Initialized = false; - return; - } - if (!DpdkDeviceList::getInstance().isInitialized()) - { - m_Initialized = false; - return; - } - #if RTE_VERSION >= RTE_VERSION_NUM(18, 11, 0, 0) - if (rte_kni_init(MAX_KNI_DEVICES) < 0) - { - PCPP_LOG_ERROR("Failed to initialize DPDK KNI module"); - m_Initialized = false; - } - #else - rte_kni_init(MAX_KNI_DEVICES); - #endif +KniDeviceList::KniDeviceList() + : m_Devices(), m_Initialized(true), m_KniUniqueId(0) { + m_Devices.reserve(MAX_KNI_DEVICES); + if (!checkKniDriver()) { + m_Initialized = false; + return; + } + if (!DpdkDeviceList::getInstance().isInitialized()) { + m_Initialized = false; + return; + } +#if RTE_VERSION >= RTE_VERSION_NUM(18, 11, 0, 0) + if (rte_kni_init(MAX_KNI_DEVICES) < 0) { + PCPP_LOG_ERROR("Failed to initialize DPDK KNI module"); + m_Initialized = false; + } +#else + rte_kni_init(MAX_KNI_DEVICES); +#endif } -KniDeviceList::~KniDeviceList() -{ - for (size_t i = 0; i < m_Devices.size(); ++i) - delete m_Devices[i]; - rte_kni_close(); +KniDeviceList::~KniDeviceList() { + for (size_t i = 0; i < m_Devices.size(); ++i) + delete m_Devices[i]; + rte_kni_close(); } -KniDeviceList& KniDeviceList::getInstance() -{ - static KniDeviceList g_KniDeviceList; - return g_KniDeviceList; +KniDeviceList& KniDeviceList::getInstance() { + static KniDeviceList g_KniDeviceList; + return g_KniDeviceList; } -KniDevice* KniDeviceList::createDevice( - const KniDevice::KniDeviceConfiguration& config, - const size_t mempoolSize -) -{ - if (!isInitialized()) - return NULL; - KniDevice* kniDevice = getDeviceByName(std::string(config.name)); - if (kniDevice != NULL) - { - PCPP_LOG_ERROR("Attempt to create DPDK KNI device with same name: '" << config.name << "'"); - PCPP_LOG_DEBUG("Use KniDeviceList::getDeviceByName or KniDeviceList::getDeviceByPort."); - return NULL; - } - if (config.portId != UINT16_MAX) - { - kniDevice = getDeviceByPort(config.portId); - if (kniDevice != NULL) - { - PCPP_LOG_ERROR("Attempt to create DPDK KNI device with same port ID: " << config.portId); - PCPP_LOG_DEBUG("Use KniDeviceList::getDeviceByName or KniDeviceList::getDeviceByPort."); - return NULL; - } - } - kniDevice = new KniDevice(config, mempoolSize, m_KniUniqueId++); - m_Devices.push_back(kniDevice); - return kniDevice; +KniDevice* +KniDeviceList::createDevice(const KniDevice::KniDeviceConfiguration& config, + const size_t mempoolSize) { + if (!isInitialized()) + return NULL; + KniDevice* kniDevice = getDeviceByName(std::string(config.name)); + if (kniDevice != NULL) { + PCPP_LOG_ERROR("Attempt to create DPDK KNI device with same name: '" + << config.name << "'"); + PCPP_LOG_DEBUG("Use KniDeviceList::getDeviceByName or " + "KniDeviceList::getDeviceByPort."); + return NULL; + } + if (config.portId != UINT16_MAX) { + kniDevice = getDeviceByPort(config.portId); + if (kniDevice != NULL) { + PCPP_LOG_ERROR("Attempt to create DPDK KNI device with same port ID: " + << config.portId); + PCPP_LOG_DEBUG("Use KniDeviceList::getDeviceByName or " + "KniDeviceList::getDeviceByPort."); + return NULL; + } + } + kniDevice = new KniDevice(config, mempoolSize, m_KniUniqueId++); + m_Devices.push_back(kniDevice); + return kniDevice; } -void KniDeviceList::destroyDevice(KniDevice* kniDevice) -{ - m_Devices.erase( - std::remove( - m_Devices.begin(), - m_Devices.end(), - kniDevice - ), - m_Devices.end() - ); - delete kniDevice; +void KniDeviceList::destroyDevice(KniDevice* kniDevice) { + m_Devices.erase(std::remove(m_Devices.begin(), m_Devices.end(), kniDevice), + m_Devices.end()); + delete kniDevice; } -KniDevice* KniDeviceList::getDeviceByPort(const uint16_t portId) -{ - //? Linear search here is optimal for low count of devices. - //? We assume that no one will create large count of devices or will rapidly search them. - //? Same for function - KniDevice* kniDevice = NULL; - if (!isInitialized()) - return kniDevice; - for (size_t i = 0; i < m_Devices.size(); ++i) - { - kniDevice = m_Devices[i]; - if (kniDevice && kniDevice->m_DeviceInfo.portId == portId) - return kniDevice; - } - return kniDevice = NULL; +KniDevice* KniDeviceList::getDeviceByPort(const uint16_t portId) { + //? Linear search here is optimal for low count of devices. + //? We assume that no one will create large count of devices or will rapidly + //search them. ? Same for function + KniDevice* kniDevice = NULL; + if (!isInitialized()) + return kniDevice; + for (size_t i = 0; i < m_Devices.size(); ++i) { + kniDevice = m_Devices[i]; + if (kniDevice && kniDevice->m_DeviceInfo.portId == portId) + return kniDevice; + } + return kniDevice = NULL; } -KniDevice* KniDeviceList::getDeviceByName(const std::string& name) -{ - KniDevice* kniDevice = NULL; - if (!isInitialized()) - return kniDevice; - for (size_t i = 0; i < m_Devices.size(); ++i) - { - kniDevice = m_Devices[i]; - if (kniDevice && kniDevice->m_DeviceInfo.name == name) - return kniDevice; - } - return kniDevice = NULL; +KniDevice* KniDeviceList::getDeviceByName(const std::string& name) { + KniDevice* kniDevice = NULL; + if (!isInitialized()) + return kniDevice; + for (size_t i = 0; i < m_Devices.size(); ++i) { + kniDevice = m_Devices[i]; + if (kniDevice && kniDevice->m_DeviceInfo.name == name) + return kniDevice; + } + return kniDevice = NULL; } -KniDeviceList::KniCallbackVersion KniDeviceList::callbackVersion() -{ - #if RTE_VERSION >= RTE_VERSION_NUM(17, 11, 0, 0) - return KniDeviceList::CALLBACKS_NEW; - #else - return KniDeviceList::CALLBACKS_OLD; - #endif +KniDeviceList::KniCallbackVersion KniDeviceList::callbackVersion() { +#if RTE_VERSION >= RTE_VERSION_NUM(17, 11, 0, 0) + return KniDeviceList::CALLBACKS_NEW; +#else + return KniDeviceList::CALLBACKS_OLD; +#endif } -bool KniDeviceList::isCallbackSupported(const KniCallbackType cbType) -{ - switch (cbType) - { - case KniDeviceList::CALLBACK_MTU: - /* fall through */ - case KniDeviceList::CALLBACK_LINK: - return true; - case KniDeviceList::CALLBACK_MAC: - /* fall through */ - case KniDeviceList::CALLBACK_PROMISC: - #if RTE_VERSION >= RTE_VERSION_NUM(18, 2, 0, 0) - return true; - #else - return false; - #endif - } - return false; +bool KniDeviceList::isCallbackSupported(const KniCallbackType cbType) { + switch (cbType) { + case KniDeviceList::CALLBACK_MTU: + /* fall through */ + case KniDeviceList::CALLBACK_LINK: + return true; + case KniDeviceList::CALLBACK_MAC: + /* fall through */ + case KniDeviceList::CALLBACK_PROMISC: +#if RTE_VERSION >= RTE_VERSION_NUM(18, 2, 0, 0) + return true; +#else + return false; +#endif + } + return false; } } // namespace pcpp diff --git a/Pcap++/src/LinuxNicInformationSocket.cpp b/Pcap++/src/LinuxNicInformationSocket.cpp index e49387f8b6..b7920d9b20 100644 --- a/Pcap++/src/LinuxNicInformationSocket.cpp +++ b/Pcap++/src/LinuxNicInformationSocket.cpp @@ -2,14 +2,14 @@ #define LOG_MODULE UndefinedLogModule -#include "Logger.h" #include "LinuxNicInformationSocket.h" +#include "Logger.h" +#include +#include +#include #include #include -#include -#include -#include #include #include @@ -17,68 +17,51 @@ #define INVALID_SOCKET_VALUE (-1) -namespace pcpp -{ +namespace pcpp { static inline LinuxNicInformationSocket::LinuxSocket -openLinuxNicInformationSocket() -{ - LinuxNicInformationSocket::LinuxSocket soc = socket(AF_INET, SOCK_DGRAM, 0); - if (soc < 0) - { - const char* error = std::strerror(errno); - PCPP_LOG_DEBUG("Can't open Linux information socket. Errno string: "<< error); - return soc = INVALID_SOCKET_VALUE; - } - return soc; +openLinuxNicInformationSocket() { + LinuxNicInformationSocket::LinuxSocket soc = socket(AF_INET, SOCK_DGRAM, 0); + if (soc < 0) { + const char* error = std::strerror(errno); + PCPP_LOG_DEBUG( + "Can't open Linux information socket. Errno string: " << error); + return soc = INVALID_SOCKET_VALUE; + } + return soc; } -LinuxNicInformationSocket::LinuxNicInformationSocket() : - m_Socket(openLinuxNicInformationSocket()) -{} +LinuxNicInformationSocket::LinuxNicInformationSocket() + : m_Socket(openLinuxNicInformationSocket()) {} -LinuxNicInformationSocket::~LinuxNicInformationSocket() -{ - if (m_Socket == INVALID_SOCKET_VALUE) - { - PCPP_LOG_DEBUG("Closing not opened Linux NIC information socket"); - } - else - { - close(m_Socket); - } +LinuxNicInformationSocket::~LinuxNicInformationSocket() { + if (m_Socket == INVALID_SOCKET_VALUE) { + PCPP_LOG_DEBUG("Closing not opened Linux NIC information socket"); + } else { + close(m_Socket); + } } -bool LinuxNicInformationSocket::makeRequest( - const char* nicName, - const IoctlType ioctlType, - ifreq* request -) -{ - if (m_Socket == INVALID_SOCKET_VALUE) - { - m_Socket = openLinuxNicInformationSocket(); - if (m_Socket == INVALID_SOCKET_VALUE) - { - PCPP_LOG_ERROR( - "Request to Linux NIC incformation socket failed. " - "Can't open socket" - ); - return false; - } - } - snprintf(request->ifr_name, IFNAMSIZ, "%s", nicName); - if (ioctl(m_Socket, ioctlType, request)) - { - const char* error = std::strerror(errno); - PCPP_LOG_ERROR( - "Request to Linux NIC incformation socket failed. " - "ioctl(2) failed with error string: " - << error - ); - return false; - } - return true; +bool LinuxNicInformationSocket::makeRequest(const char* nicName, + const IoctlType ioctlType, + ifreq* request) { + if (m_Socket == INVALID_SOCKET_VALUE) { + m_Socket = openLinuxNicInformationSocket(); + if (m_Socket == INVALID_SOCKET_VALUE) { + PCPP_LOG_ERROR("Request to Linux NIC incformation socket failed. " + "Can't open socket"); + return false; + } + } + snprintf(request->ifr_name, IFNAMSIZ, "%s", nicName); + if (ioctl(m_Socket, ioctlType, request)) { + const char* error = std::strerror(errno); + PCPP_LOG_ERROR("Request to Linux NIC incformation socket failed. " + "ioctl(2) failed with error string: " + << error); + return false; + } + return true; } } // namespace pcpp #endif /* __linux__ */ diff --git a/Pcap++/src/MBufRawPacket.cpp b/Pcap++/src/MBufRawPacket.cpp index 9915ef5b47..9bf5a2b311 100644 --- a/Pcap++/src/MBufRawPacket.cpp +++ b/Pcap++/src/MBufRawPacket.cpp @@ -7,27 +7,26 @@ #define __STDC_LIMIT_MACROS #define __STDC_FORMAT_MACROS +#include "rte_errno.h" #include "rte_mbuf.h" #include "rte_mempool.h" -#include "rte_errno.h" -#include "MBufRawPacket.h" -#include "Logger.h" #include "DpdkDevice.h" +#include "Logger.h" +#include "MBufRawPacket.h" #ifdef USE_DPDK_KNI #include "KniDevice.h" #endif -#include #include +#include #include #ifndef MBUF_DATA_SIZE_DEFINE -# define MBUF_DATA_SIZE_DEFINE RTE_MBUF_DEFAULT_DATAROOM +#define MBUF_DATA_SIZE_DEFINE RTE_MBUF_DEFAULT_DATAROOM #endif -namespace pcpp -{ +namespace pcpp { /** * =================== @@ -37,307 +36,291 @@ namespace pcpp const int MBufRawPacket::MBUF_DATA_SIZE = MBUF_DATA_SIZE_DEFINE; -MBufRawPacket::~MBufRawPacket() -{ - if (m_MBuf != NULL && m_FreeMbuf) - { - rte_pktmbuf_free(m_MBuf); - } +MBufRawPacket::~MBufRawPacket() { + if (m_MBuf != NULL && m_FreeMbuf) { + rte_pktmbuf_free(m_MBuf); + } } -bool MBufRawPacket::init(struct rte_mempool* mempool) -{ - if (m_MBuf != NULL) - { - PCPP_LOG_ERROR("MBufRawPacket already initialized"); - return false; - } - - if (mempool == NULL) - { - PCPP_LOG_ERROR("Could not initialize MBufRawPacket no mempool provided"); - return false; - } - - m_MBuf = rte_pktmbuf_alloc(mempool); - if (m_MBuf == NULL) - { - PCPP_LOG_ERROR("Couldn't allocate mbuf"); - return false; - } - - m_Mempool = mempool; - return true; +bool MBufRawPacket::init(struct rte_mempool* mempool) { + if (m_MBuf != NULL) { + PCPP_LOG_ERROR("MBufRawPacket already initialized"); + return false; + } + + if (mempool == NULL) { + PCPP_LOG_ERROR("Could not initialize MBufRawPacket no mempool provided"); + return false; + } + + m_MBuf = rte_pktmbuf_alloc(mempool); + if (m_MBuf == NULL) { + PCPP_LOG_ERROR("Couldn't allocate mbuf"); + return false; + } + + m_Mempool = mempool; + return true; } -bool MBufRawPacket::init(DpdkDevice* device) -{ - return init(device->m_MBufMempool); +bool MBufRawPacket::init(DpdkDevice* device) { + return init(device->m_MBufMempool); } #ifdef USE_DPDK_KNI -bool MBufRawPacket::init(KniDevice* device) -{ - return init(device->m_MBufMempool); +bool MBufRawPacket::init(KniDevice* device) { + return init(device->m_MBufMempool); } #endif -bool MBufRawPacket::initFromRawPacket(const RawPacket* rawPacket, struct rte_mempool* mempool) -{ - if (!init(mempool)) - return false; +bool MBufRawPacket::initFromRawPacket(const RawPacket* rawPacket, + struct rte_mempool* mempool) { + if (!init(mempool)) + return false; - m_RawPacketSet = false; + m_RawPacketSet = false; - // mbuf is allocated with length of 0, need to adjust it to the size of other - if (rte_pktmbuf_append(m_MBuf, rawPacket->getRawDataLen()) == NULL) - { - PCPP_LOG_ERROR("Couldn't append " << rawPacket->getRawDataLen() << " bytes to mbuf"); - return false; - } + // mbuf is allocated with length of 0, need to adjust it to the size of other + if (rte_pktmbuf_append(m_MBuf, rawPacket->getRawDataLen()) == NULL) { + PCPP_LOG_ERROR("Couldn't append " << rawPacket->getRawDataLen() + << " bytes to mbuf"); + return false; + } - m_RawData = rte_pktmbuf_mtod(m_MBuf, uint8_t*); - m_RawDataLen = rte_pktmbuf_pkt_len(m_MBuf); + m_RawData = rte_pktmbuf_mtod(m_MBuf, uint8_t*); + m_RawDataLen = rte_pktmbuf_pkt_len(m_MBuf); - copyDataFrom(*rawPacket, false); + copyDataFrom(*rawPacket, false); - return true; + return true; } -bool MBufRawPacket::initFromRawPacket(const RawPacket* rawPacket, DpdkDevice* device) -{ - return initFromRawPacket(rawPacket, device->m_MBufMempool); +bool MBufRawPacket::initFromRawPacket(const RawPacket* rawPacket, + DpdkDevice* device) { + return initFromRawPacket(rawPacket, device->m_MBufMempool); } #ifdef USE_DPDK_KNI -bool MBufRawPacket::initFromRawPacket(const RawPacket* rawPacket, KniDevice* device) -{ - return initFromRawPacket(rawPacket, device->m_MBufMempool); +bool MBufRawPacket::initFromRawPacket(const RawPacket* rawPacket, + KniDevice* device) { + return initFromRawPacket(rawPacket, device->m_MBufMempool); } #endif -MBufRawPacket::MBufRawPacket(const MBufRawPacket& other) -{ - m_DeleteRawDataAtDestructor = false; - m_MBuf = NULL; - m_RawDataLen = 0; - m_RawPacketSet = false; - m_RawData = NULL; - m_Mempool = other.m_Mempool; - - rte_mbuf* newMbuf = rte_pktmbuf_alloc(m_Mempool); - if (newMbuf == NULL) - { - PCPP_LOG_ERROR("Couldn't allocate mbuf"); - return; - } - - // mbuf is allocated with length of 0, need to adjust it to the size of other - if (rte_pktmbuf_append(newMbuf, other.m_RawDataLen) == NULL) - { - PCPP_LOG_ERROR("Couldn't append " << other.m_RawDataLen << " bytes to mbuf"); - return; - } - - setMBuf(newMbuf, other.m_TimeStamp); - - m_RawPacketSet = false; - - copyDataFrom(other, false); -} - -MBufRawPacket& MBufRawPacket::operator=(const MBufRawPacket& other) -{ - if (m_MBuf == NULL) - { - PCPP_LOG_ERROR("MBufRawPacket isn't initialized"); - return *this; - } - - // adjust the size of the mbuf to the new data - if (m_RawDataLen < other.m_RawDataLen) - { - if (rte_pktmbuf_append(m_MBuf, other.m_RawDataLen - m_RawDataLen) == NULL) - { - PCPP_LOG_ERROR("Couldn't append " << (other.m_RawDataLen - m_RawDataLen) << " bytes to mbuf"); - return *this; - } - } - else if (m_RawDataLen > other.m_RawDataLen) - { - if (rte_pktmbuf_adj(m_MBuf, m_RawDataLen - other.m_RawDataLen) == NULL) - { - PCPP_LOG_ERROR("Couldn't remove " << m_RawDataLen - other.m_RawDataLen << " bytes to mbuf"); - return *this; - } - } - - m_RawPacketSet = false; - - copyDataFrom(other, false); - - return *this; -} +MBufRawPacket::MBufRawPacket(const MBufRawPacket& other) { + m_DeleteRawDataAtDestructor = false; + m_MBuf = NULL; + m_RawDataLen = 0; + m_RawPacketSet = false; + m_RawData = NULL; + m_Mempool = other.m_Mempool; -bool MBufRawPacket::setRawData(const uint8_t* pRawData, int rawDataLen, timespec timestamp, LinkLayerType layerType, int frameLength) -{ - if (rawDataLen > MBUF_DATA_SIZE) - { - PCPP_LOG_ERROR("Cannot set raw data which length is larger than mBuf max size. mBuf max length: " << MBUF_DATA_SIZE << "; requested length: " << rawDataLen); - return false; - } - - if (m_MBuf == NULL) - { - if (!(init(m_Mempool))) - { - PCPP_LOG_ERROR("Couldn't allocate new mBuf"); - return false; - } - } - - // adjust the size of the mbuf to the new data - if (m_RawDataLen < rawDataLen) - { - if (rte_pktmbuf_append(m_MBuf, rawDataLen - m_RawDataLen) == NULL) - { - PCPP_LOG_ERROR("Couldn't append " << (rawDataLen - m_RawDataLen) << " bytes to mbuf"); - return false; - } - } - else if (m_RawDataLen > rawDataLen) - { - if (rte_pktmbuf_adj(m_MBuf, m_RawDataLen - rawDataLen) == NULL) - { - PCPP_LOG_ERROR("Couldn't remove " << (m_RawDataLen - rawDataLen) << " bytes to mbuf"); - return false; - } - } - - m_RawData = rte_pktmbuf_mtod(m_MBuf, uint8_t*); - m_RawDataLen = rte_pktmbuf_pkt_len(m_MBuf); - memcpy(m_RawData, pRawData, m_RawDataLen); - delete [] pRawData; - m_TimeStamp = timestamp; - m_RawPacketSet = true; - m_FrameLength = frameLength; - m_LinkLayerType = layerType; - - return true; -} + rte_mbuf* newMbuf = rte_pktmbuf_alloc(m_Mempool); + if (newMbuf == NULL) { + PCPP_LOG_ERROR("Couldn't allocate mbuf"); + return; + } -void MBufRawPacket::clear() -{ - if (m_MBuf != NULL && m_FreeMbuf) - { - rte_pktmbuf_free(m_MBuf); - } + // mbuf is allocated with length of 0, need to adjust it to the size of other + if (rte_pktmbuf_append(newMbuf, other.m_RawDataLen) == NULL) { + PCPP_LOG_ERROR("Couldn't append " << other.m_RawDataLen + << " bytes to mbuf"); + return; + } - m_MBuf = NULL; + setMBuf(newMbuf, other.m_TimeStamp); - m_RawData = NULL; + m_RawPacketSet = false; - RawPacket::clear(); + copyDataFrom(other, false); } -void MBufRawPacket::appendData(const uint8_t* dataToAppend, size_t dataToAppendLen) -{ - if (m_MBuf == NULL) - { - PCPP_LOG_ERROR("MBufRawPacket not initialized. Please call the init() method"); - return; //TODO: need to return false here or something - } - - char* startOfNewlyAppendedData = rte_pktmbuf_append(m_MBuf, dataToAppendLen); - if (startOfNewlyAppendedData == NULL) - { - PCPP_LOG_ERROR("Couldn't append " << dataToAppendLen << " bytes to RawPacket - not enough room in mBuf"); - return; //TODO: need to return false here or something - } - - RawPacket::appendData(dataToAppend, dataToAppendLen); +MBufRawPacket& MBufRawPacket::operator=(const MBufRawPacket& other) { + if (m_MBuf == NULL) { + PCPP_LOG_ERROR("MBufRawPacket isn't initialized"); + return *this; + } + + // adjust the size of the mbuf to the new data + if (m_RawDataLen < other.m_RawDataLen) { + if (rte_pktmbuf_append(m_MBuf, other.m_RawDataLen - m_RawDataLen) == NULL) { + PCPP_LOG_ERROR("Couldn't append " << (other.m_RawDataLen - m_RawDataLen) + << " bytes to mbuf"); + return *this; + } + } else if (m_RawDataLen > other.m_RawDataLen) { + if (rte_pktmbuf_adj(m_MBuf, m_RawDataLen - other.m_RawDataLen) == NULL) { + PCPP_LOG_ERROR("Couldn't remove " << m_RawDataLen - other.m_RawDataLen + << " bytes to mbuf"); + return *this; + } + } + + m_RawPacketSet = false; + + copyDataFrom(other, false); + + return *this; +} - PCPP_LOG_DEBUG("Appended " << dataToAppendLen << " bytes to MBufRawPacket"); +bool MBufRawPacket::setRawData(const uint8_t* pRawData, int rawDataLen, + timespec timestamp, LinkLayerType layerType, + int frameLength) { + if (rawDataLen > MBUF_DATA_SIZE) { + PCPP_LOG_ERROR("Cannot set raw data which length is larger than mBuf max " + "size. mBuf max length: " + << MBUF_DATA_SIZE << "; requested length: " << rawDataLen); + return false; + } + + if (m_MBuf == NULL) { + if (!(init(m_Mempool))) { + PCPP_LOG_ERROR("Couldn't allocate new mBuf"); + return false; + } + } + + // adjust the size of the mbuf to the new data + if (m_RawDataLen < rawDataLen) { + if (rte_pktmbuf_append(m_MBuf, rawDataLen - m_RawDataLen) == NULL) { + PCPP_LOG_ERROR("Couldn't append " << (rawDataLen - m_RawDataLen) + << " bytes to mbuf"); + return false; + } + } else if (m_RawDataLen > rawDataLen) { + if (rte_pktmbuf_adj(m_MBuf, m_RawDataLen - rawDataLen) == NULL) { + PCPP_LOG_ERROR("Couldn't remove " << (m_RawDataLen - rawDataLen) + << " bytes to mbuf"); + return false; + } + } + + m_RawData = rte_pktmbuf_mtod(m_MBuf, uint8_t*); + m_RawDataLen = rte_pktmbuf_pkt_len(m_MBuf); + memcpy(m_RawData, pRawData, m_RawDataLen); + delete[] pRawData; + m_TimeStamp = timestamp; + m_RawPacketSet = true; + m_FrameLength = frameLength; + m_LinkLayerType = layerType; + + return true; } -void MBufRawPacket::insertData(int atIndex, const uint8_t* dataToInsert, size_t dataToInsertLen) -{ - if (m_MBuf == NULL) - { - PCPP_LOG_ERROR("MBufRawPacket not initialized. Please call the init() method"); - return; //TODO: need to return false here or something - } +void MBufRawPacket::clear() { + if (m_MBuf != NULL && m_FreeMbuf) { + rte_pktmbuf_free(m_MBuf); + } - char* startOfNewlyAppendedData = rte_pktmbuf_append(m_MBuf, dataToInsertLen); - if (startOfNewlyAppendedData == NULL) - { - PCPP_LOG_ERROR("Couldn't append " << dataToInsertLen << " bytes to RawPacket - not enough room in mBuf"); - return; //TODO: need to return false here or something - } + m_MBuf = NULL; - RawPacket::insertData(atIndex, dataToInsert, dataToInsertLen); + m_RawData = NULL; - PCPP_LOG_DEBUG("Inserted " << dataToInsertLen << " bytes to MBufRawPacket"); + RawPacket::clear(); } -bool MBufRawPacket::removeData(int atIndex, size_t numOfBytesToRemove) -{ - if (m_MBuf == NULL) - { - PCPP_LOG_ERROR("MBufRawPacket not initialized. Please call the init() method"); - return false; - } +void MBufRawPacket::appendData(const uint8_t* dataToAppend, + size_t dataToAppendLen) { + if (m_MBuf == NULL) { + PCPP_LOG_ERROR( + "MBufRawPacket not initialized. Please call the init() method"); + return; // TODO: need to return false here or something + } + + char* startOfNewlyAppendedData = rte_pktmbuf_append(m_MBuf, dataToAppendLen); + if (startOfNewlyAppendedData == NULL) { + PCPP_LOG_ERROR("Couldn't append " + << dataToAppendLen + << " bytes to RawPacket - not enough room in mBuf"); + return; // TODO: need to return false here or something + } + + RawPacket::appendData(dataToAppend, dataToAppendLen); + + PCPP_LOG_DEBUG("Appended " << dataToAppendLen << " bytes to MBufRawPacket"); +} - if (!RawPacket::removeData(atIndex, numOfBytesToRemove)) - return false; +void MBufRawPacket::insertData(int atIndex, const uint8_t* dataToInsert, + size_t dataToInsertLen) { + if (m_MBuf == NULL) { + PCPP_LOG_ERROR( + "MBufRawPacket not initialized. Please call the init() method"); + return; // TODO: need to return false here or something + } + + char* startOfNewlyAppendedData = rte_pktmbuf_append(m_MBuf, dataToInsertLen); + if (startOfNewlyAppendedData == NULL) { + PCPP_LOG_ERROR("Couldn't append " + << dataToInsertLen + << " bytes to RawPacket - not enough room in mBuf"); + return; // TODO: need to return false here or something + } + + RawPacket::insertData(atIndex, dataToInsert, dataToInsertLen); + + PCPP_LOG_DEBUG("Inserted " << dataToInsertLen << " bytes to MBufRawPacket"); +} - if (rte_pktmbuf_trim(m_MBuf, numOfBytesToRemove) != 0) - { - PCPP_LOG_ERROR("Couldn't trim the mBuf"); - return false; - } +bool MBufRawPacket::removeData(int atIndex, size_t numOfBytesToRemove) { + if (m_MBuf == NULL) { + PCPP_LOG_ERROR( + "MBufRawPacket not initialized. Please call the init() method"); + return false; + } - PCPP_LOG_DEBUG("Trimmed " << numOfBytesToRemove << " bytes from MBufRawPacket"); + if (!RawPacket::removeData(atIndex, numOfBytesToRemove)) + return false; - return true; -} + if (rte_pktmbuf_trim(m_MBuf, numOfBytesToRemove) != 0) { + PCPP_LOG_ERROR("Couldn't trim the mBuf"); + return false; + } -bool MBufRawPacket::reallocateData(size_t newBufferLength) -{ - if ((int)newBufferLength < m_RawDataLen) - { - PCPP_LOG_ERROR("Cannot reallocate mBuf raw packet to a smaller size. Current data length: " << m_RawDataLen << "; requested length: " << newBufferLength); - return false; - } + PCPP_LOG_DEBUG("Trimmed " << numOfBytesToRemove + << " bytes from MBufRawPacket"); - if (newBufferLength > MBUF_DATA_SIZE) - { - PCPP_LOG_ERROR("Cannot reallocate mBuf raw packet to a size larger than mBuf data. mBuf max length: " << MBUF_DATA_SIZE << "; requested length: " << newBufferLength); - return false; - } - - // no need to do any memory allocation because mbuf is already allocated + return true; +} - return true; +bool MBufRawPacket::reallocateData(size_t newBufferLength) { + if ((int)newBufferLength < m_RawDataLen) { + PCPP_LOG_ERROR("Cannot reallocate mBuf raw packet to a smaller size. " + "Current data length: " + << m_RawDataLen + << "; requested length: " << newBufferLength); + return false; + } + + if (newBufferLength > MBUF_DATA_SIZE) { + PCPP_LOG_ERROR("Cannot reallocate mBuf raw packet to a size larger than " + "mBuf data. mBuf max length: " + << MBUF_DATA_SIZE + << "; requested length: " << newBufferLength); + return false; + } + + // no need to do any memory allocation because mbuf is already allocated + + return true; } -void MBufRawPacket::setMBuf(struct rte_mbuf* mBuf, timespec timestamp) -{ - if (m_MBuf != NULL && m_FreeMbuf) - rte_pktmbuf_free(m_MBuf); +void MBufRawPacket::setMBuf(struct rte_mbuf* mBuf, timespec timestamp) { + if (m_MBuf != NULL && m_FreeMbuf) + rte_pktmbuf_free(m_MBuf); - if (mBuf == NULL) - { - PCPP_LOG_ERROR("mbuf to set is NULL"); - return; - } + if (mBuf == NULL) { + PCPP_LOG_ERROR("mbuf to set is NULL"); + return; + } - m_MBuf = mBuf; - RawPacket::setRawData(rte_pktmbuf_mtod(mBuf, const uint8_t*), rte_pktmbuf_pkt_len(mBuf), timestamp, LINKTYPE_ETHERNET); + m_MBuf = mBuf; + RawPacket::setRawData(rte_pktmbuf_mtod(mBuf, const uint8_t*), + rte_pktmbuf_pkt_len(mBuf), timestamp, + LINKTYPE_ETHERNET); } } // namespace pcpp // GCOVR_EXCL_STOP -#endif /* USE_DPDK */ +#endif /* USE_DPDK */ diff --git a/Pcap++/src/NetworkUtils.cpp b/Pcap++/src/NetworkUtils.cpp index affc10b68f..e10e0e63be 100644 --- a/Pcap++/src/NetworkUtils.cpp +++ b/Pcap++/src/NetworkUtils.cpp @@ -1,447 +1,432 @@ #define LOG_MODULE NetworkUtils +#include "NetworkUtils.h" +#include "ArpLayer.h" +#include "DnsLayer.h" +#include "EndianPortable.h" +#include "EthLayer.h" +#include "IPv4Layer.h" +#include "Logger.h" +#include "Packet.h" +#include "PcapFilter.h" +#include "UdpLayer.h" #include #include #include #include -#include "Logger.h" -#include "Packet.h" -#include "EthLayer.h" -#include "ArpLayer.h" -#include "IPv4Layer.h" -#include "UdpLayer.h" -#include "DnsLayer.h" -#include "PcapFilter.h" -#include "NetworkUtils.h" -#include "EndianPortable.h" #ifdef _MSC_VER #include "SystemUtils.h" #endif #ifndef ETIMEDOUT -#define ETIMEDOUT 10060 +#define ETIMEDOUT 10060 #endif -#define DNS_PORT 53 - +#define DNS_PORT 53 -namespace pcpp -{ +namespace pcpp { const int NetworkUtils::DefaultTimeout = 5; - -struct ArpingReceivedData -{ - std::mutex &mutex; - std::condition_variable &cond; - IPv4Address ipAddr; - clock_t start; - MacAddress result; - double arpResponseTime; +struct ArpingReceivedData { + std::mutex& mutex; + std::condition_variable& cond; + IPv4Address ipAddr; + clock_t start; + MacAddress result; + double arpResponseTime; }; +static void arpPacketReceived(RawPacket* rawPacket, PcapLiveDevice*, + void* userCookie) { + // extract timestamp of packet + clock_t receiveTime = clock(); -static void arpPacketReceived(RawPacket* rawPacket, PcapLiveDevice*, void* userCookie) -{ - // extract timestamp of packet - clock_t receiveTime = clock(); - - // get the data from the main thread - ArpingReceivedData* data = (ArpingReceivedData*)userCookie; + // get the data from the main thread + ArpingReceivedData* data = (ArpingReceivedData*)userCookie; - // parse the response packet - Packet packet(rawPacket); + // parse the response packet + Packet packet(rawPacket); - // verify that it's an ARP packet (although it must be because I set an ARP reply filter on the interface) - if (!packet.isPacketOfType(ARP)) - return; + // verify that it's an ARP packet (although it must be because I set an ARP + // reply filter on the interface) + if (!packet.isPacketOfType(ARP)) + return; - // extract the ARP layer from the packet - ArpLayer* arpReplyLayer = packet.getLayerOfType(true); // lookup in reverse order - if (arpReplyLayer == nullptr) - return; + // extract the ARP layer from the packet + ArpLayer* arpReplyLayer = + packet.getLayerOfType(true); // lookup in reverse order + if (arpReplyLayer == nullptr) + return; - // verify it's the right ARP response - if (arpReplyLayer->getArpHeader()->hardwareType != htobe16(1) /* Ethernet */ - || arpReplyLayer->getArpHeader()->protocolType != htobe16(PCPP_ETHERTYPE_IP)) - return; + // verify it's the right ARP response + if (arpReplyLayer->getArpHeader()->hardwareType != htobe16(1) /* Ethernet */ + || + arpReplyLayer->getArpHeader()->protocolType != htobe16(PCPP_ETHERTYPE_IP)) + return; - // verify the ARP response is the response for out request (and not some arbitrary ARP response) - if (arpReplyLayer->getSenderIpAddr() != data->ipAddr) - return; + // verify the ARP response is the response for out request (and not some + // arbitrary ARP response) + if (arpReplyLayer->getSenderIpAddr() != data->ipAddr) + return; - // measure response time - double diffticks = receiveTime-data->start; - double diffms = (diffticks*1000)/CLOCKS_PER_SEC; + // measure response time + double diffticks = receiveTime - data->start; + double diffms = (diffticks * 1000) / CLOCKS_PER_SEC; - data->arpResponseTime = diffms; - data->result = arpReplyLayer->getSenderMacAddress(); + data->arpResponseTime = diffms; + data->result = arpReplyLayer->getSenderMacAddress(); - // signal the main thread the ARP reply was received - data->cond.notify_one(); + // signal the main thread the ARP reply was received + data->cond.notify_one(); } +MacAddress +NetworkUtils::getMacAddress(IPv4Address ipAddr, PcapLiveDevice* device, + double& arpResponseTimeMS, MacAddress sourceMac, + IPv4Address sourceIP, int arpTimeout) const { + MacAddress result = MacAddress::Zero; -MacAddress NetworkUtils::getMacAddress(IPv4Address ipAddr, PcapLiveDevice* device, double& arpResponseTimeMS, - MacAddress sourceMac, IPv4Address sourceIP, int arpTimeout) const -{ - MacAddress result = MacAddress::Zero; - - // open the device if not already opened - bool closeDeviceAtTheEnd = false; - if (!device->isOpened()) - { - closeDeviceAtTheEnd = true; - if (!device->open()) - { - PCPP_LOG_ERROR("Cannot open device"); - return result; - } - } - - if (sourceMac == MacAddress::Zero) - sourceMac = device->getMacAddress(); - - if (sourceIP == IPv4Address::Zero) - sourceIP = device->getIPv4Address(); - - if (arpTimeout <= 0) - arpTimeout = NetworkUtils::DefaultTimeout; - - // create an ARP request from sourceMac and sourceIP and ask for target IP - - Packet arpRequest(100); - - MacAddress destMac(0xff, 0xff, 0xff, 0xff, 0xff, 0xff); - EthLayer ethLayer(sourceMac, destMac); - - ArpLayer arpLayer(ARP_REQUEST, sourceMac, destMac, sourceIP, ipAddr); - - if (!arpRequest.addLayer(ðLayer)) - { - PCPP_LOG_ERROR("Couldn't build Eth layer for ARP request"); - return result; - } - - if (!arpRequest.addLayer(&arpLayer)) - { - PCPP_LOG_ERROR("Couldn't build ARP layer for ARP request"); - return result; - } - - arpRequest.computeCalculateFields(); - - // set a filter for the interface to intercept only ARP response packets - ArpFilter arpFilter(ARP_REPLY); - if (!device->setFilter(arpFilter)) - { - PCPP_LOG_ERROR("Couldn't set ARP filter for device"); - return result; - } - - // since packet capture is done on another thread, I use a conditional mutex with timeout to synchronize between the capture - // thread and the main thread. When the capture thread starts running the main thread is blocking on the conditional mutex. - // When the ARP response is captured the capture thread signals the main thread and the main thread stops capturing and continues - // to the next iteration. If a timeout passes and no ARP response is captured, the main thread stops capturing - - std::mutex mutex; - std::condition_variable cond; - - // this is the token that passes between the 2 threads. It contains pointers to the conditional mutex, the target IP for identifying - // the ARP response, the iteration index and a timestamp to calculate the response time - ArpingReceivedData data = { - mutex, - cond, - ipAddr, - clock(), - MacAddress::Zero, - 0 - }; - - struct timeval now; - gettimeofday(&now,nullptr); - - // start capturing. The capture is done on another thread, hence "arpPacketReceived" is running on that thread - device->startCapture(arpPacketReceived, &data); - - // send the ARP request - device->sendPacket(&arpRequest); - - // block on the conditional mutex until capture thread signals or until timeout expires - // cppcheck-suppress localMutex - std::unique_lock lock(mutex); - std::cv_status res = cond.wait_for(lock, std::chrono::seconds(arpTimeout)); - - // stop the capturing thread - device->stopCapture(); - - // check if timeout expired - if (res == std::cv_status::timeout) - { - PCPP_LOG_ERROR("ARP request time out"); - return result; - } - - if (closeDeviceAtTheEnd) - device->close(); - else - device->clearFilter(); - - result = data.result; - arpResponseTimeMS = data.arpResponseTime; - - return result; -} + // open the device if not already opened + bool closeDeviceAtTheEnd = false; + if (!device->isOpened()) { + closeDeviceAtTheEnd = true; + if (!device->open()) { + PCPP_LOG_ERROR("Cannot open device"); + return result; + } + } + if (sourceMac == MacAddress::Zero) + sourceMac = device->getMacAddress(); + if (sourceIP == IPv4Address::Zero) + sourceIP = device->getIPv4Address(); -struct DNSReceivedData -{ - std::mutex &mutex; - std::condition_variable &cond; - std::string hostname; - uint16_t transactionID; - clock_t start; - IPv4Address result; - uint32_t ttl; - double dnsResponseTime; -}; + if (arpTimeout <= 0) + arpTimeout = NetworkUtils::DefaultTimeout; + + // create an ARP request from sourceMac and sourceIP and ask for target IP + + Packet arpRequest(100); + + MacAddress destMac(0xff, 0xff, 0xff, 0xff, 0xff, 0xff); + EthLayer ethLayer(sourceMac, destMac); + + ArpLayer arpLayer(ARP_REQUEST, sourceMac, destMac, sourceIP, ipAddr); + + if (!arpRequest.addLayer(ðLayer)) { + PCPP_LOG_ERROR("Couldn't build Eth layer for ARP request"); + return result; + } + + if (!arpRequest.addLayer(&arpLayer)) { + PCPP_LOG_ERROR("Couldn't build ARP layer for ARP request"); + return result; + } + + arpRequest.computeCalculateFields(); + + // set a filter for the interface to intercept only ARP response packets + ArpFilter arpFilter(ARP_REPLY); + if (!device->setFilter(arpFilter)) { + PCPP_LOG_ERROR("Couldn't set ARP filter for device"); + return result; + } + + // since packet capture is done on another thread, I use a conditional mutex + // with timeout to synchronize between the capture thread and the main thread. + // When the capture thread starts running the main thread is blocking on the + // conditional mutex. When the ARP response is captured the capture thread + // signals the main thread and the main thread stops capturing and continues + // to the next iteration. If a timeout passes and no ARP response is captured, + // the main thread stops capturing + + std::mutex mutex; + std::condition_variable cond; -static void dnsResponseReceived(RawPacket* rawPacket, PcapLiveDevice*, void* userCookie) -{ - // extract timestamp of packet - clock_t receiveTime = clock(); - - // get data from the main thread - DNSReceivedData* data = (DNSReceivedData*)userCookie; - - // parse the response packet - Packet packet(rawPacket); - - // verify that it's an DNS packet (although it must be because DNS port filter was set on the interface) - if (!packet.isPacketOfType(DNS)) - return; - - // extract the DNS layer from the packet - DnsLayer* dnsResponseLayer = packet.getLayerOfType(true); // lookup in reverse order - if (dnsResponseLayer == nullptr) - return; - - // verify it's the right DNS response - if (dnsResponseLayer->getDnsHeader()->queryOrResponse != 1 /* DNS response */ - || dnsResponseLayer->getDnsHeader()->numberOfAnswers < htobe16(1) - || dnsResponseLayer->getDnsHeader()->transactionID != htobe16(data->transactionID)) - { - return; - } - - // DNS resolving can be recursive as many DNS responses contain multiple answers with recursive canonical names (CNAME) for - // the hostname. For example: a DNS response for www.a.com can have multiple answers: - //- First with CNAME: www.a.com -> www.b.com - //- Second with CNAME: www.b.com -> www.c.com - //- Third with resolving: www.c.com -> 1.1.1.1 - // So the search must be recursive until an IPv4 resolving is found or until no hostname or canonical name are found (and then return) - - std::string hostToFind = data->hostname; - - DnsResource* dnsAnswer = nullptr; - - while (true) - { - dnsAnswer = dnsResponseLayer->getAnswer(hostToFind, true); - - // if response doesn't contain hostname or cname - return - if (dnsAnswer == nullptr) - { - PCPP_LOG_DEBUG("DNS answer doesn't contain hostname '" << hostToFind << "'"); - return; - } - - DnsType dnsType = dnsAnswer->getDnsType(); - // if answer contains IPv4 resolving - break the loop and return the IP address - if (dnsType == DNS_TYPE_A) - { - PCPP_LOG_DEBUG("Found IPv4 resolving for hostname '" << hostToFind << "'"); - break; - } - // if answer contains a cname - continue to search this cname in the packet - hopefully find the IP resolving - else if (dnsType == DNS_TYPE_CNAME) - { - PCPP_LOG_DEBUG("Got a DNS response for hostname '" << hostToFind << "' with CNAME '" << dnsAnswer->getData()->toString() << "'"); - hostToFind = dnsAnswer->getData()->toString(); - } - // if answer is of type other than A or CNAME (for example AAAA - IPv6) - type is not supported - return - else - { - PCPP_LOG_DEBUG("Got a DNS response with type which is not A or CNAME"); - return; - } - } - // if we got here it means an IPv4 resolving was found - - // measure response time - clock_t diffticks = receiveTime-data->start; - double diffms = (diffticks*1000.0)/CLOCKS_PER_SEC; - - data->dnsResponseTime = diffms; - data->result = dnsAnswer->getData()->castAs()->getIpAddress(); - data->ttl = dnsAnswer->getTTL(); - - // signal the main thread the ARP reply was received - data->cond.notify_one(); + // this is the token that passes between the 2 threads. It contains pointers + // to the conditional mutex, the target IP for identifying the ARP response, + // the iteration index and a timestamp to calculate the response time + ArpingReceivedData data = {mutex, cond, ipAddr, clock(), MacAddress::Zero, 0}; + + struct timeval now; + gettimeofday(&now, nullptr); + + // start capturing. The capture is done on another thread, hence + // "arpPacketReceived" is running on that thread + device->startCapture(arpPacketReceived, &data); + + // send the ARP request + device->sendPacket(&arpRequest); + + // block on the conditional mutex until capture thread signals or until + // timeout expires cppcheck-suppress localMutex + std::unique_lock lock(mutex); + std::cv_status res = cond.wait_for(lock, std::chrono::seconds(arpTimeout)); + + // stop the capturing thread + device->stopCapture(); + + // check if timeout expired + if (res == std::cv_status::timeout) { + PCPP_LOG_ERROR("ARP request time out"); + return result; + } + + if (closeDeviceAtTheEnd) + device->close(); + else + device->clearFilter(); + + result = data.result; + arpResponseTimeMS = data.arpResponseTime; + + return result; } +struct DNSReceivedData { + std::mutex& mutex; + std::condition_variable& cond; + std::string hostname; + uint16_t transactionID; + clock_t start; + IPv4Address result; + uint32_t ttl; + double dnsResponseTime; +}; + +static void dnsResponseReceived(RawPacket* rawPacket, PcapLiveDevice*, + void* userCookie) { + // extract timestamp of packet + clock_t receiveTime = clock(); + + // get data from the main thread + DNSReceivedData* data = (DNSReceivedData*)userCookie; + + // parse the response packet + Packet packet(rawPacket); + + // verify that it's an DNS packet (although it must be because DNS port filter + // was set on the interface) + if (!packet.isPacketOfType(DNS)) + return; + + // extract the DNS layer from the packet + DnsLayer* dnsResponseLayer = + packet.getLayerOfType(true); // lookup in reverse order + if (dnsResponseLayer == nullptr) + return; + + // verify it's the right DNS response + if (dnsResponseLayer->getDnsHeader()->queryOrResponse != 1 /* DNS response */ + || dnsResponseLayer->getDnsHeader()->numberOfAnswers < htobe16(1) || + dnsResponseLayer->getDnsHeader()->transactionID != + htobe16(data->transactionID)) { + return; + } + + // DNS resolving can be recursive as many DNS responses contain multiple + // answers with recursive canonical names (CNAME) for the hostname. For + // example: a DNS response for www.a.com can have multiple answers: + //- First with CNAME: www.a.com -> www.b.com + //- Second with CNAME: www.b.com -> www.c.com + //- Third with resolving: www.c.com -> 1.1.1.1 + // So the search must be recursive until an IPv4 resolving is found or until + // no hostname or canonical name are found (and then return) + + std::string hostToFind = data->hostname; + + DnsResource* dnsAnswer = nullptr; + + while (true) { + dnsAnswer = dnsResponseLayer->getAnswer(hostToFind, true); + + // if response doesn't contain hostname or cname - return + if (dnsAnswer == nullptr) { + PCPP_LOG_DEBUG("DNS answer doesn't contain hostname '" << hostToFind + << "'"); + return; + } + + DnsType dnsType = dnsAnswer->getDnsType(); + // if answer contains IPv4 resolving - break the loop and return the IP + // address + if (dnsType == DNS_TYPE_A) { + PCPP_LOG_DEBUG("Found IPv4 resolving for hostname '" << hostToFind + << "'"); + break; + } + // if answer contains a cname - continue to search this cname in the packet + // - hopefully find the IP resolving + else if (dnsType == DNS_TYPE_CNAME) { + PCPP_LOG_DEBUG("Got a DNS response for hostname '" + << hostToFind << "' with CNAME '" + << dnsAnswer->getData()->toString() << "'"); + hostToFind = dnsAnswer->getData()->toString(); + } + // if answer is of type other than A or CNAME (for example AAAA - IPv6) - + // type is not supported - return + else { + PCPP_LOG_DEBUG("Got a DNS response with type which is not A or CNAME"); + return; + } + } + // if we got here it means an IPv4 resolving was found + + // measure response time + clock_t diffticks = receiveTime - data->start; + double diffms = (diffticks * 1000.0) / CLOCKS_PER_SEC; + + data->dnsResponseTime = diffms; + data->result = + dnsAnswer->getData()->castAs()->getIpAddress(); + data->ttl = dnsAnswer->getTTL(); + + // signal the main thread the ARP reply was received + data->cond.notify_one(); +} -IPv4Address NetworkUtils::getIPv4Address(const std::string& hostname, PcapLiveDevice* device, double& dnsResponseTimeMS, uint32_t& dnsTTL, - int dnsTimeout, IPv4Address dnsServerIP, IPv4Address gatewayIP) const -{ - IPv4Address result = IPv4Address::Zero; - - // open the device if not already opened - bool closeDeviceAtTheEnd = false; - if (!device->isOpened()) - { - closeDeviceAtTheEnd = true; - if (!device->open()) - { - PCPP_LOG_ERROR("Cannot open device"); - return result; - } - } - - // first - resolve gateway MAC address - - // if gateway IP wasn't provided - try to find the default gateway - if (gatewayIP == IPv4Address::Zero) - { - gatewayIP = device->getDefaultGateway(); - } - - if (!gatewayIP.isValid() || gatewayIP == IPv4Address::Zero) - { - PCPP_LOG_ERROR("Gateway address isn't valid or couldn't find default gateway"); - return result; - } - - // send the ARP request to find gateway MAC address - double arpResTime; - MacAddress gatewayMacAddress = getMacAddress(gatewayIP, device, arpResTime); - - if (gatewayMacAddress == MacAddress::Zero) - { - PCPP_LOG_ERROR("Couldn't resolve gateway MAC address"); - return result; - } - - if (dnsTimeout <= 0) - dnsTimeout = NetworkUtils::DefaultTimeout; - - // validate DNS server IP. If it wasn't provided - set the system-configured DNS server - if (dnsServerIP == IPv4Address::Zero && device->getDnsServers().size() > 0) - { - dnsServerIP = device->getDnsServers().at(0); - } - - if (!dnsServerIP.isValid()) - { - PCPP_LOG_ERROR("DNS server IP isn't valid"); - return result; - } - - // create DNS request - - Packet dnsRequest(100); - MacAddress sourceMac = device->getMacAddress(); - EthLayer ethLayer(sourceMac, gatewayMacAddress, PCPP_ETHERTYPE_IP); - IPv4Layer ipLayer(device->getIPv4Address(), dnsServerIP); - ipLayer.getIPv4Header()->timeToLive = 128; - - // randomize source port to a number >= 10000 - int srcPortLowest = 10000; - int srcPortRange = 65535 - srcPortLowest; - uint16_t srcPort = (rand() % srcPortRange) + srcPortLowest; - UdpLayer udpLayer(srcPort, DNS_PORT); - - // create the DNS request for the hostname - DnsLayer dnsLayer; - - // randomize transaction ID - uint16_t transactionID = rand() % 65535; - dnsLayer.getDnsHeader()->transactionID = htobe16(transactionID); - dnsLayer.addQuery(hostname, DNS_TYPE_A, DNS_CLASS_IN); - - // add all layers to packet - if (!dnsRequest.addLayer(ðLayer) || !dnsRequest.addLayer(&ipLayer) || !dnsRequest.addLayer(&udpLayer) || !dnsRequest.addLayer(&dnsLayer)) - { - PCPP_LOG_ERROR("Couldn't construct DNS query"); - return result; - } - - dnsRequest.computeCalculateFields(); - - // set a DNS response filter on the device - PortFilter dnsResponseFilter(53, SRC); - if (!device->setFilter(dnsResponseFilter)) - { - PCPP_LOG_ERROR("Couldn't set DNS response filter"); - return result; - } - - // since packet capture is done on another thread, I use a conditional mutex with timeout to synchronize between the capture - // thread and the main thread. When the capture thread starts running the main thread is blocking on the conditional mutex. - // When the DNS response are captured the capture thread signals the main thread and the main thread stops capturing and continues - // to the next iteration. if a timeout passes and no DNS response is captured, the main thread stops capturing - - std::mutex mutex; - std::condition_variable cond; - - // this is the token that passes between the 2 threads - DNSReceivedData data = { - mutex, - cond, - hostname, - transactionID, - clock(), - IPv4Address::Zero, - 0, - 0 - }; - - - struct timeval now; - gettimeofday(&now,nullptr); - - // start capturing. The capture is done on another thread, hence "dnsResponseReceived" is running on that thread - device->startCapture(dnsResponseReceived, &data); - - // send the DNS request - device->sendPacket(&dnsRequest); - - // block on the conditional mutex until capture thread signals or until timeout expires - // cppcheck-suppress localMutex - std::unique_lock lock(mutex); - std::cv_status res = cond.wait_for(lock, std::chrono::seconds(dnsTimeout)); - - // stop the capturing thread - device->stopCapture(); - - // check if timeout expired - if (res == std::cv_status::timeout) - { - PCPP_LOG_ERROR("DNS request time out"); - return result; - } - - if (closeDeviceAtTheEnd) - device->close(); - else - device->clearFilter(); - - result = data.result; - dnsResponseTimeMS = data.dnsResponseTime; - dnsTTL = data.ttl; - - return result; +IPv4Address NetworkUtils::getIPv4Address(const std::string& hostname, + PcapLiveDevice* device, + double& dnsResponseTimeMS, + uint32_t& dnsTTL, int dnsTimeout, + IPv4Address dnsServerIP, + IPv4Address gatewayIP) const { + IPv4Address result = IPv4Address::Zero; + + // open the device if not already opened + bool closeDeviceAtTheEnd = false; + if (!device->isOpened()) { + closeDeviceAtTheEnd = true; + if (!device->open()) { + PCPP_LOG_ERROR("Cannot open device"); + return result; + } + } + + // first - resolve gateway MAC address + + // if gateway IP wasn't provided - try to find the default gateway + if (gatewayIP == IPv4Address::Zero) { + gatewayIP = device->getDefaultGateway(); + } + + if (!gatewayIP.isValid() || gatewayIP == IPv4Address::Zero) { + PCPP_LOG_ERROR( + "Gateway address isn't valid or couldn't find default gateway"); + return result; + } + + // send the ARP request to find gateway MAC address + double arpResTime; + MacAddress gatewayMacAddress = getMacAddress(gatewayIP, device, arpResTime); + + if (gatewayMacAddress == MacAddress::Zero) { + PCPP_LOG_ERROR("Couldn't resolve gateway MAC address"); + return result; + } + + if (dnsTimeout <= 0) + dnsTimeout = NetworkUtils::DefaultTimeout; + + // validate DNS server IP. If it wasn't provided - set the system-configured + // DNS server + if (dnsServerIP == IPv4Address::Zero && device->getDnsServers().size() > 0) { + dnsServerIP = device->getDnsServers().at(0); + } + + if (!dnsServerIP.isValid()) { + PCPP_LOG_ERROR("DNS server IP isn't valid"); + return result; + } + + // create DNS request + + Packet dnsRequest(100); + MacAddress sourceMac = device->getMacAddress(); + EthLayer ethLayer(sourceMac, gatewayMacAddress, PCPP_ETHERTYPE_IP); + IPv4Layer ipLayer(device->getIPv4Address(), dnsServerIP); + ipLayer.getIPv4Header()->timeToLive = 128; + + // randomize source port to a number >= 10000 + int srcPortLowest = 10000; + int srcPortRange = 65535 - srcPortLowest; + uint16_t srcPort = (rand() % srcPortRange) + srcPortLowest; + UdpLayer udpLayer(srcPort, DNS_PORT); + + // create the DNS request for the hostname + DnsLayer dnsLayer; + + // randomize transaction ID + uint16_t transactionID = rand() % 65535; + dnsLayer.getDnsHeader()->transactionID = htobe16(transactionID); + dnsLayer.addQuery(hostname, DNS_TYPE_A, DNS_CLASS_IN); + + // add all layers to packet + if (!dnsRequest.addLayer(ðLayer) || !dnsRequest.addLayer(&ipLayer) || + !dnsRequest.addLayer(&udpLayer) || !dnsRequest.addLayer(&dnsLayer)) { + PCPP_LOG_ERROR("Couldn't construct DNS query"); + return result; + } + + dnsRequest.computeCalculateFields(); + + // set a DNS response filter on the device + PortFilter dnsResponseFilter(53, SRC); + if (!device->setFilter(dnsResponseFilter)) { + PCPP_LOG_ERROR("Couldn't set DNS response filter"); + return result; + } + + // since packet capture is done on another thread, I use a conditional mutex + // with timeout to synchronize between the capture thread and the main thread. + // When the capture thread starts running the main thread is blocking on the + // conditional mutex. When the DNS response are captured the capture thread + // signals the main thread and the main thread stops capturing and continues + // to the next iteration. if a timeout passes and no DNS response is captured, + // the main thread stops capturing + + std::mutex mutex; + std::condition_variable cond; + + // this is the token that passes between the 2 threads + DNSReceivedData data = { + mutex, cond, hostname, transactionID, clock(), IPv4Address::Zero, 0, 0}; + + struct timeval now; + gettimeofday(&now, nullptr); + + // start capturing. The capture is done on another thread, hence + // "dnsResponseReceived" is running on that thread + device->startCapture(dnsResponseReceived, &data); + + // send the DNS request + device->sendPacket(&dnsRequest); + + // block on the conditional mutex until capture thread signals or until + // timeout expires cppcheck-suppress localMutex + std::unique_lock lock(mutex); + std::cv_status res = cond.wait_for(lock, std::chrono::seconds(dnsTimeout)); + + // stop the capturing thread + device->stopCapture(); + + // check if timeout expired + if (res == std::cv_status::timeout) { + PCPP_LOG_ERROR("DNS request time out"); + return result; + } + + if (closeDeviceAtTheEnd) + device->close(); + else + device->clearFilter(); + + result = data.result; + dnsResponseTimeMS = data.dnsResponseTime; + dnsTTL = data.ttl; + + return result; } } // namespace pcpp diff --git a/Pcap++/src/PcapDevice.cpp b/Pcap++/src/PcapDevice.cpp index a8bc77267e..229e9709c0 100644 --- a/Pcap++/src/PcapDevice.cpp +++ b/Pcap++/src/PcapDevice.cpp @@ -1,70 +1,61 @@ #include "PcapDevice.h" -#include "PcapFilter.h" #include "Logger.h" +#include "PcapFilter.h" #include "TimespecTimeval.h" #include "pcap.h" -namespace pcpp -{ - -IPcapDevice::~IPcapDevice() -{ +namespace pcpp { + +IPcapDevice::~IPcapDevice() {} + +bool IPcapDevice::setFilter(std::string filterAsString) { + PCPP_LOG_DEBUG("Filter to be set: '" << filterAsString << "'"); + if (!m_DeviceOpened) { + PCPP_LOG_ERROR("Device not Opened!! cannot set filter"); + return false; + } + + struct bpf_program prog; + PCPP_LOG_DEBUG("Compiling the filter '" << filterAsString << "'"); + if (pcap_compile(m_PcapDescriptor, &prog, filterAsString.c_str(), 1, 0) < 0) { + /* + * Print out appropriate text, followed by the error message + * generated by the packet capture library. + */ + PCPP_LOG_ERROR("Error compiling filter. Error message is: " + << pcap_geterr(m_PcapDescriptor)); + return false; + } + + PCPP_LOG_DEBUG("Setting the compiled filter"); + if (pcap_setfilter(m_PcapDescriptor, &prog) < 0) { + /* + * Print out error. The format will be the prefix string, + * created above, followed by the error message that the packet + * capture library generates. + */ + PCPP_LOG_ERROR("Error setting a compiled filter. Error message is: " + << pcap_geterr(m_PcapDescriptor)); + pcap_freecode(&prog); + return false; + } + + PCPP_LOG_DEBUG("Filter set successfully"); + + pcap_freecode(&prog); + + return true; } -bool IPcapDevice::setFilter(std::string filterAsString) -{ - PCPP_LOG_DEBUG("Filter to be set: '" << filterAsString << "'"); - if (!m_DeviceOpened) - { - PCPP_LOG_ERROR("Device not Opened!! cannot set filter"); - return false; - } - - struct bpf_program prog; - PCPP_LOG_DEBUG("Compiling the filter '" << filterAsString << "'"); - if (pcap_compile(m_PcapDescriptor, &prog, filterAsString.c_str(), 1, 0) < 0) - { - /* - * Print out appropriate text, followed by the error message - * generated by the packet capture library. - */ - PCPP_LOG_ERROR("Error compiling filter. Error message is: " << pcap_geterr(m_PcapDescriptor)); - return false; - } - - PCPP_LOG_DEBUG("Setting the compiled filter"); - if (pcap_setfilter(m_PcapDescriptor, &prog) < 0) - { - /* - * Print out error. The format will be the prefix string, - * created above, followed by the error message that the packet - * capture library generates. - */ - PCPP_LOG_ERROR("Error setting a compiled filter. Error message is: " << pcap_geterr(m_PcapDescriptor)); - pcap_freecode(&prog); - return false; - } - - PCPP_LOG_DEBUG("Filter set successfully"); - - pcap_freecode(&prog); - - return true; -} - -bool IPcapDevice::clearFilter() -{ - return setFilter(""); -} +bool IPcapDevice::clearFilter() { return setFilter(""); } -bool IPcapDevice::matchPacketWithFilter(GeneralFilter& filter, RawPacket* rawPacket) -{ - return filter.matchPacketWithFilter(rawPacket); +bool IPcapDevice::matchPacketWithFilter(GeneralFilter& filter, + RawPacket* rawPacket) { + return filter.matchPacketWithFilter(rawPacket); } -std::string IPcapDevice::getPcapLibVersionInfo() -{ - return std::string(pcap_lib_version()); +std::string IPcapDevice::getPcapLibVersionInfo() { + return std::string(pcap_lib_version()); } } // namespace pcpp diff --git a/Pcap++/src/PcapFileDevice.cpp b/Pcap++/src/PcapFileDevice.cpp index 2e361353cd..03e6e913a7 100644 --- a/Pcap++/src/PcapFileDevice.cpp +++ b/Pcap++/src/PcapFileDevice.cpp @@ -1,927 +1,896 @@ #define LOG_MODULE PcapLogModuleFileDevice -#include -#include #include "PcapFileDevice.h" -#include "light_pcapng_ext.h" +#include "EndianPortable.h" #include "Logger.h" #include "TimespecTimeval.h" +#include "light_pcapng_ext.h" #include "pcap.h" -#include +#include #include -#include "EndianPortable.h" +#include +#include -namespace pcpp -{ +namespace pcpp { template -constexpr size_t ARRAY_SIZE(T (&)[N]) { return N; } - -struct pcap_file_header -{ - uint32_t magic; - uint16_t version_major; - uint16_t version_minor; - int32_t thiszone; - uint32_t sigfigs; - uint32_t snaplen; - uint32_t linktype; +constexpr size_t ARRAY_SIZE(T (&)[N]) { + return N; +} + +struct pcap_file_header { + uint32_t magic; + uint16_t version_major; + uint16_t version_minor; + int32_t thiszone; + uint32_t sigfigs; + uint32_t snaplen; + uint32_t linktype; }; -struct packet_header -{ - uint32_t tv_sec; - uint32_t tv_usec; - uint32_t caplen; - uint32_t len; +struct packet_header { + uint32_t tv_sec; + uint32_t tv_usec; + uint32_t caplen; + uint32_t len; }; // ~~~~~~~~~~~~~~~~~~~ // IFileDevice members // ~~~~~~~~~~~~~~~~~~~ -IFileDevice::IFileDevice(const std::string& fileName) : IPcapDevice() -{ - m_FileName = fileName; +IFileDevice::IFileDevice(const std::string& fileName) : IPcapDevice() { + m_FileName = fileName; } -IFileDevice::~IFileDevice() -{ - IFileDevice::close(); -} +IFileDevice::~IFileDevice() { IFileDevice::close(); } -std::string IFileDevice::getFileName() const -{ - return m_FileName; -} +std::string IFileDevice::getFileName() const { return m_FileName; } -void IFileDevice::close() -{ - if (m_PcapDescriptor != nullptr) - { - pcap_close(m_PcapDescriptor); - PCPP_LOG_DEBUG("Successfully closed file reader device for filename '" << m_FileName << "'"); - m_PcapDescriptor = nullptr; - } +void IFileDevice::close() { + if (m_PcapDescriptor != nullptr) { + pcap_close(m_PcapDescriptor); + PCPP_LOG_DEBUG("Successfully closed file reader device for filename '" + << m_FileName << "'"); + m_PcapDescriptor = nullptr; + } - m_DeviceOpened = false; + m_DeviceOpened = false; } - // ~~~~~~~~~~~~~~~~~~~~~~~~~ // IFileReaderDevice members // ~~~~~~~~~~~~~~~~~~~~~~~~~ -IFileReaderDevice::IFileReaderDevice(const std::string& fileName) : IFileDevice(fileName) -{ - m_NumOfPacketsNotParsed = 0; - m_NumOfPacketsRead = 0; +IFileReaderDevice::IFileReaderDevice(const std::string& fileName) + : IFileDevice(fileName) { + m_NumOfPacketsNotParsed = 0; + m_NumOfPacketsRead = 0; } -IFileReaderDevice* IFileReaderDevice::getReader(const std::string& fileName) -{ - const auto extensionPos = fileName.find_last_of('.'); - const auto fileExtension = extensionPos != std::string::npos ? fileName.substr(extensionPos) : ""; +IFileReaderDevice* IFileReaderDevice::getReader(const std::string& fileName) { + const auto extensionPos = fileName.find_last_of('.'); + const auto fileExtension = + extensionPos != std::string::npos ? fileName.substr(extensionPos) : ""; - if (fileExtension == ".pcapng" || fileExtension == ".zstd" || fileExtension == ".zst") - return new PcapNgFileReaderDevice(fileName); - else if (fileExtension == ".snoop") - return new SnoopFileReaderDevice(fileName); + if (fileExtension == ".pcapng" || fileExtension == ".zstd" || + fileExtension == ".zst") + return new PcapNgFileReaderDevice(fileName); + else if (fileExtension == ".snoop") + return new SnoopFileReaderDevice(fileName); - return new PcapFileReaderDevice(fileName); + return new PcapFileReaderDevice(fileName); } -uint64_t IFileReaderDevice::getFileSize() const -{ - std::ifstream fileStream(m_FileName.c_str(), std::ifstream::ate | std::ifstream::binary); - return fileStream.tellg(); +uint64_t IFileReaderDevice::getFileSize() const { + std::ifstream fileStream(m_FileName.c_str(), + std::ifstream::ate | std::ifstream::binary); + return fileStream.tellg(); } -int IFileReaderDevice::getNextPackets(RawPacketVector& packetVec, int numOfPacketsToRead) -{ - int numOfPacketsRead = 0; +int IFileReaderDevice::getNextPackets(RawPacketVector& packetVec, + int numOfPacketsToRead) { + int numOfPacketsRead = 0; - for (; numOfPacketsToRead < 0 || numOfPacketsRead < numOfPacketsToRead; numOfPacketsRead++) - { - RawPacket* newPacket = new RawPacket(); - bool packetRead = getNextPacket(*newPacket); - if (packetRead) - { - packetVec.pushBack(newPacket); - } - else - { - delete newPacket; - break; - } - } + for (; numOfPacketsToRead < 0 || numOfPacketsRead < numOfPacketsToRead; + numOfPacketsRead++) { + RawPacket* newPacket = new RawPacket(); + bool packetRead = getNextPacket(*newPacket); + if (packetRead) { + packetVec.pushBack(newPacket); + } else { + delete newPacket; + break; + } + } - return numOfPacketsRead; + return numOfPacketsRead; } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // SnoopFileReaderDevice members // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -SnoopFileReaderDevice::~SnoopFileReaderDevice() -{ - m_snoopFile.close(); -} - -bool SnoopFileReaderDevice::open() -{ - m_NumOfPacketsRead = 0; - m_NumOfPacketsNotParsed = 0; - - m_snoopFile.open(m_FileName.c_str(), std::ifstream::binary); - if (!m_snoopFile.is_open()) - { - PCPP_LOG_ERROR("Cannot open snoop reader device for filename '" << m_FileName << "'"); - m_snoopFile.close(); - return false; - } - - snoop_file_header_t snoop_file_header; - m_snoopFile.read((char*)&snoop_file_header, sizeof(snoop_file_header_t)); - if (!m_snoopFile) - { - PCPP_LOG_ERROR("Cannot read snoop file header for '" << m_FileName << "'"); - m_snoopFile.close(); - return false; - } - - if(be64toh(snoop_file_header.identification_pattern) != 0x736e6f6f70000000 && be32toh(snoop_file_header.version_number) == 2) - return false; - - // From https://datatracker.ietf.org/doc/html/rfc1761 - static const pcpp::LinkLayerType snoop_encap[] = { - LINKTYPE_ETHERNET, /* IEEE 802.3 */ - LINKTYPE_NULL, /* IEEE 802.4 Token Bus */ - LINKTYPE_IEEE802_5, /* IEEE 802.5 */ - LINKTYPE_NULL, /* IEEE 802.6 Metro Net */ - LINKTYPE_ETHERNET, /* Ethernet */ - LINKTYPE_C_HDLC, /* HDLC */ - LINKTYPE_NULL, /* Character Synchronous, e.g. bisync */ - LINKTYPE_NULL, /* IBM Channel-to-Channel */ - LINKTYPE_FDDI /* FDDI */ - }; - uint32_t datalink_type = be32toh(snoop_file_header.datalink_type); - if (datalink_type > ARRAY_SIZE(snoop_encap) - 1) - { - PCPP_LOG_ERROR("Cannot read data link type for '" << m_FileName << "'"); - m_snoopFile.close(); - return false; - } - - m_PcapLinkLayerType = snoop_encap[datalink_type]; - - PCPP_LOG_DEBUG("Successfully opened file reader device for filename '" << m_FileName << "'"); - m_DeviceOpened = true; - return true; -} - -void SnoopFileReaderDevice::getStatistics(PcapStats& stats) const -{ - stats.packetsRecv = m_NumOfPacketsRead; - stats.packetsDrop = m_NumOfPacketsNotParsed; - stats.packetsDropByInterface = 0; - PCPP_LOG_DEBUG("Statistics received for reader device for filename '" << m_FileName << "'"); -} - -bool SnoopFileReaderDevice::getNextPacket(RawPacket& rawPacket) -{ - rawPacket.clear(); - if (m_DeviceOpened != true) - { - PCPP_LOG_ERROR("File device '" << m_FileName << "' not opened"); - return false; - } - snoop_packet_header_t snoop_packet_header; - m_snoopFile.read((char*)&snoop_packet_header, sizeof(snoop_packet_header_t)); - if(!m_snoopFile) { - return false; - } - size_t packetSize = be32toh(snoop_packet_header.included_length); - if(packetSize > 15000) { - return false; - } - std::unique_ptr packetData(new char[packetSize]); - m_snoopFile.read(packetData.get(), packetSize); - if(!m_snoopFile) { - return false; - } - timespec ts = { static_cast(be32toh(snoop_packet_header.time_sec)), static_cast(be32toh(snoop_packet_header.time_usec)) * 1000 }; - if (!rawPacket.setRawData((const uint8_t*)packetData.release(), packetSize, ts, static_cast(m_PcapLinkLayerType))) - { - PCPP_LOG_ERROR("Couldn't set data to raw packet"); - return false; - } - size_t pad = be32toh(snoop_packet_header.packet_record_length) - (sizeof(snoop_packet_header_t) + be32toh(snoop_packet_header.included_length)); - m_snoopFile.ignore(pad); - if(!m_snoopFile) { - return false; - } - - m_NumOfPacketsRead++; - return true; -} - -void SnoopFileReaderDevice::close() -{ - m_snoopFile.close(); - m_DeviceOpened = false; - PCPP_LOG_DEBUG("File reader closed for file '" << m_FileName << "'"); +SnoopFileReaderDevice::~SnoopFileReaderDevice() { m_snoopFile.close(); } + +bool SnoopFileReaderDevice::open() { + m_NumOfPacketsRead = 0; + m_NumOfPacketsNotParsed = 0; + + m_snoopFile.open(m_FileName.c_str(), std::ifstream::binary); + if (!m_snoopFile.is_open()) { + PCPP_LOG_ERROR("Cannot open snoop reader device for filename '" + << m_FileName << "'"); + m_snoopFile.close(); + return false; + } + + snoop_file_header_t snoop_file_header; + m_snoopFile.read((char*)&snoop_file_header, sizeof(snoop_file_header_t)); + if (!m_snoopFile) { + PCPP_LOG_ERROR("Cannot read snoop file header for '" << m_FileName << "'"); + m_snoopFile.close(); + return false; + } + + if (be64toh(snoop_file_header.identification_pattern) != 0x736e6f6f70000000 && + be32toh(snoop_file_header.version_number) == 2) + return false; + + // From https://datatracker.ietf.org/doc/html/rfc1761 + static const pcpp::LinkLayerType snoop_encap[] = { + LINKTYPE_ETHERNET, /* IEEE 802.3 */ + LINKTYPE_NULL, /* IEEE 802.4 Token Bus */ + LINKTYPE_IEEE802_5, /* IEEE 802.5 */ + LINKTYPE_NULL, /* IEEE 802.6 Metro Net */ + LINKTYPE_ETHERNET, /* Ethernet */ + LINKTYPE_C_HDLC, /* HDLC */ + LINKTYPE_NULL, /* Character Synchronous, e.g. bisync */ + LINKTYPE_NULL, /* IBM Channel-to-Channel */ + LINKTYPE_FDDI /* FDDI */ + }; + uint32_t datalink_type = be32toh(snoop_file_header.datalink_type); + if (datalink_type > ARRAY_SIZE(snoop_encap) - 1) { + PCPP_LOG_ERROR("Cannot read data link type for '" << m_FileName << "'"); + m_snoopFile.close(); + return false; + } + + m_PcapLinkLayerType = snoop_encap[datalink_type]; + + PCPP_LOG_DEBUG("Successfully opened file reader device for filename '" + << m_FileName << "'"); + m_DeviceOpened = true; + return true; +} + +void SnoopFileReaderDevice::getStatistics(PcapStats& stats) const { + stats.packetsRecv = m_NumOfPacketsRead; + stats.packetsDrop = m_NumOfPacketsNotParsed; + stats.packetsDropByInterface = 0; + PCPP_LOG_DEBUG("Statistics received for reader device for filename '" + << m_FileName << "'"); +} + +bool SnoopFileReaderDevice::getNextPacket(RawPacket& rawPacket) { + rawPacket.clear(); + if (m_DeviceOpened != true) { + PCPP_LOG_ERROR("File device '" << m_FileName << "' not opened"); + return false; + } + snoop_packet_header_t snoop_packet_header; + m_snoopFile.read((char*)&snoop_packet_header, sizeof(snoop_packet_header_t)); + if (!m_snoopFile) { + return false; + } + size_t packetSize = be32toh(snoop_packet_header.included_length); + if (packetSize > 15000) { + return false; + } + std::unique_ptr packetData(new char[packetSize]); + m_snoopFile.read(packetData.get(), packetSize); + if (!m_snoopFile) { + return false; + } + timespec ts = {static_cast(be32toh(snoop_packet_header.time_sec)), + static_cast(be32toh(snoop_packet_header.time_usec)) * + 1000}; + if (!rawPacket.setRawData((const uint8_t*)packetData.release(), packetSize, + ts, + static_cast(m_PcapLinkLayerType))) { + PCPP_LOG_ERROR("Couldn't set data to raw packet"); + return false; + } + size_t pad = be32toh(snoop_packet_header.packet_record_length) - + (sizeof(snoop_packet_header_t) + + be32toh(snoop_packet_header.included_length)); + m_snoopFile.ignore(pad); + if (!m_snoopFile) { + return false; + } + + m_NumOfPacketsRead++; + return true; +} + +void SnoopFileReaderDevice::close() { + m_snoopFile.close(); + m_DeviceOpened = false; + PCPP_LOG_DEBUG("File reader closed for file '" << m_FileName << "'"); } - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // PcapFileReaderDevice members // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +bool PcapFileReaderDevice::open() { + m_NumOfPacketsRead = 0; + m_NumOfPacketsNotParsed = 0; -bool PcapFileReaderDevice::open() -{ - m_NumOfPacketsRead = 0; - m_NumOfPacketsNotParsed = 0; - - if (m_PcapDescriptor != nullptr) - { - PCPP_LOG_DEBUG("Pcap descriptor already opened. Nothing to do"); - return true; - } + if (m_PcapDescriptor != nullptr) { + PCPP_LOG_DEBUG("Pcap descriptor already opened. Nothing to do"); + return true; + } - char errbuf[PCAP_ERRBUF_SIZE]; + char errbuf[PCAP_ERRBUF_SIZE]; #if defined(PCAP_TSTAMP_PRECISION_NANO) - m_PcapDescriptor = pcap_open_offline_with_tstamp_precision(m_FileName.c_str(), PCAP_TSTAMP_PRECISION_NANO, errbuf); + m_PcapDescriptor = pcap_open_offline_with_tstamp_precision( + m_FileName.c_str(), PCAP_TSTAMP_PRECISION_NANO, errbuf); #else - m_PcapDescriptor = pcap_open_offline(m_FileName.c_str(), errbuf); + m_PcapDescriptor = pcap_open_offline(m_FileName.c_str(), errbuf); #endif - if (m_PcapDescriptor == nullptr) - { - PCPP_LOG_ERROR("Cannot open file reader device for filename '" << m_FileName << "': " << errbuf); - m_DeviceOpened = false; - return false; - } - - int linkLayer = pcap_datalink(m_PcapDescriptor); - if (!RawPacket::isLinkTypeValid(linkLayer)) - { - PCPP_LOG_ERROR("Invalid link layer (" << linkLayer << ") for reader device filename '" << m_FileName << "'"); - pcap_close(m_PcapDescriptor); - m_PcapDescriptor = nullptr; - m_DeviceOpened = false; - return false; - } - - m_PcapLinkLayerType = static_cast(linkLayer); - - PCPP_LOG_DEBUG("Successfully opened file reader device for filename '" << m_FileName << "'"); - m_DeviceOpened = true; - return true; -} - -void PcapFileReaderDevice::getStatistics(PcapStats& stats) const -{ - stats.packetsRecv = m_NumOfPacketsRead; - stats.packetsDrop = m_NumOfPacketsNotParsed; - stats.packetsDropByInterface = 0; - PCPP_LOG_DEBUG("Statistics received for reader device for filename '" << m_FileName << "'"); -} - -bool PcapFileReaderDevice::getNextPacket(RawPacket& rawPacket) -{ - rawPacket.clear(); - if (m_PcapDescriptor == nullptr) - { - PCPP_LOG_ERROR("File device '" << m_FileName << "' not opened"); - return false; - } - pcap_pkthdr pkthdr; - const uint8_t* pPacketData = pcap_next(m_PcapDescriptor, &pkthdr); - if (pPacketData == nullptr) - { - PCPP_LOG_DEBUG("Packet could not be read. Probably end-of-file"); - return false; - } - - uint8_t* pMyPacketData = new uint8_t[pkthdr.caplen]; - memcpy(pMyPacketData, pPacketData, pkthdr.caplen); + if (m_PcapDescriptor == nullptr) { + PCPP_LOG_ERROR("Cannot open file reader device for filename '" + << m_FileName << "': " << errbuf); + m_DeviceOpened = false; + return false; + } + + int linkLayer = pcap_datalink(m_PcapDescriptor); + if (!RawPacket::isLinkTypeValid(linkLayer)) { + PCPP_LOG_ERROR("Invalid link layer (" << linkLayer + << ") for reader device filename '" + << m_FileName << "'"); + pcap_close(m_PcapDescriptor); + m_PcapDescriptor = nullptr; + m_DeviceOpened = false; + return false; + } + + m_PcapLinkLayerType = static_cast(linkLayer); + + PCPP_LOG_DEBUG("Successfully opened file reader device for filename '" + << m_FileName << "'"); + m_DeviceOpened = true; + return true; +} + +void PcapFileReaderDevice::getStatistics(PcapStats& stats) const { + stats.packetsRecv = m_NumOfPacketsRead; + stats.packetsDrop = m_NumOfPacketsNotParsed; + stats.packetsDropByInterface = 0; + PCPP_LOG_DEBUG("Statistics received for reader device for filename '" + << m_FileName << "'"); +} + +bool PcapFileReaderDevice::getNextPacket(RawPacket& rawPacket) { + rawPacket.clear(); + if (m_PcapDescriptor == nullptr) { + PCPP_LOG_ERROR("File device '" << m_FileName << "' not opened"); + return false; + } + pcap_pkthdr pkthdr; + const uint8_t* pPacketData = pcap_next(m_PcapDescriptor, &pkthdr); + if (pPacketData == nullptr) { + PCPP_LOG_DEBUG("Packet could not be read. Probably end-of-file"); + return false; + } + + uint8_t* pMyPacketData = new uint8_t[pkthdr.caplen]; + memcpy(pMyPacketData, pPacketData, pkthdr.caplen); #if defined(PCAP_TSTAMP_PRECISION_NANO) - timespec ts = { pkthdr.ts.tv_sec, static_cast(pkthdr.ts.tv_usec) }; //because we opened with nano second precision 'tv_usec' is actually nanos + timespec ts = { + pkthdr.ts.tv_sec, + static_cast( + pkthdr.ts.tv_usec)}; // because we opened with nano second precision + // 'tv_usec' is actually nanos #else - struct timeval ts = pkthdr.ts; + struct timeval ts = pkthdr.ts; #endif - if (!rawPacket.setRawData(pMyPacketData, pkthdr.caplen, ts, static_cast(m_PcapLinkLayerType), pkthdr.len)) - { - PCPP_LOG_ERROR("Couldn't set data to raw packet"); - return false; - } - m_NumOfPacketsRead++; - return true; + if (!rawPacket.setRawData(pMyPacketData, pkthdr.caplen, ts, + static_cast(m_PcapLinkLayerType), + pkthdr.len)) { + PCPP_LOG_ERROR("Couldn't set data to raw packet"); + return false; + } + m_NumOfPacketsRead++; + return true; } - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // PcapNgFileReaderDevice members // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -PcapNgFileReaderDevice::PcapNgFileReaderDevice(const std::string& fileName) : IFileReaderDevice(fileName) -{ - m_LightPcapNg = nullptr; +PcapNgFileReaderDevice::PcapNgFileReaderDevice(const std::string& fileName) + : IFileReaderDevice(fileName) { + m_LightPcapNg = nullptr; } -bool PcapNgFileReaderDevice::open() -{ - m_NumOfPacketsRead = 0; - m_NumOfPacketsNotParsed = 0; +bool PcapNgFileReaderDevice::open() { + m_NumOfPacketsRead = 0; + m_NumOfPacketsNotParsed = 0; - if (m_LightPcapNg != nullptr) - { - PCPP_LOG_DEBUG("pcapng descriptor already opened. Nothing to do"); - return true; - } + if (m_LightPcapNg != nullptr) { + PCPP_LOG_DEBUG("pcapng descriptor already opened. Nothing to do"); + return true; + } - m_LightPcapNg = light_pcapng_open_read(m_FileName.c_str(), LIGHT_FALSE); - if (m_LightPcapNg == nullptr) - { - PCPP_LOG_ERROR("Cannot open pcapng reader device for filename '" << m_FileName << "'"); - m_DeviceOpened = false; - return false; - } + m_LightPcapNg = light_pcapng_open_read(m_FileName.c_str(), LIGHT_FALSE); + if (m_LightPcapNg == nullptr) { + PCPP_LOG_ERROR("Cannot open pcapng reader device for filename '" + << m_FileName << "'"); + m_DeviceOpened = false; + return false; + } - PCPP_LOG_DEBUG("Successfully opened pcapng reader device for filename '" << m_FileName << "'"); - m_DeviceOpened = true; - return true; + PCPP_LOG_DEBUG("Successfully opened pcapng reader device for filename '" + << m_FileName << "'"); + m_DeviceOpened = true; + return true; } -bool PcapNgFileReaderDevice::getNextPacket(RawPacket& rawPacket, std::string& packetComment) -{ - rawPacket.clear(); - packetComment = ""; +bool PcapNgFileReaderDevice::getNextPacket(RawPacket& rawPacket, + std::string& packetComment) { + rawPacket.clear(); + packetComment = ""; + + if (m_LightPcapNg == nullptr) { + PCPP_LOG_ERROR("Pcapng file device '" << m_FileName << "' not opened"); + return false; + } - if (m_LightPcapNg == nullptr) - { - PCPP_LOG_ERROR("Pcapng file device '" << m_FileName << "' not opened"); - return false; - } - - light_packet_header pktHeader; - const uint8_t* pktData = nullptr; + light_packet_header pktHeader; + const uint8_t* pktData = nullptr; - if (!light_get_next_packet((light_pcapng_t*)m_LightPcapNg, &pktHeader, &pktData)) - { - PCPP_LOG_DEBUG("Packet could not be read. Probably end-of-file"); - return false; - } - - while (!m_BpfWrapper.matchPacketWithFilter(pktData, pktHeader.captured_length, pktHeader.timestamp, pktHeader.data_link)) - { - if (!light_get_next_packet((light_pcapng_t*)m_LightPcapNg, &pktHeader, &pktData)) - { - PCPP_LOG_DEBUG("Packet could not be read. Probably end-of-file"); - return false; - } - } - - uint8_t* myPacketData = new uint8_t[pktHeader.captured_length]; - memcpy(myPacketData, pktData, pktHeader.captured_length); - if (!rawPacket.setRawData(myPacketData, pktHeader.captured_length, pktHeader.timestamp, static_cast(pktHeader.data_link), pktHeader.original_length)) - { - PCPP_LOG_ERROR("Couldn't set data to raw packet"); - return false; - } - - if (pktHeader.comment != nullptr && pktHeader.comment_length > 0) - packetComment = std::string(pktHeader.comment, pktHeader.comment_length); - - m_NumOfPacketsRead++; - return true; -} - -bool PcapNgFileReaderDevice::getNextPacket(RawPacket& rawPacket) -{ - std::string temp; - return getNextPacket(rawPacket, temp); -} - -void PcapNgFileReaderDevice::getStatistics(PcapStats& stats) const -{ - stats.packetsRecv = m_NumOfPacketsRead; - stats.packetsDrop = m_NumOfPacketsNotParsed; - stats.packetsDropByInterface = 0; - PCPP_LOG_DEBUG("Statistics received for pcapng reader device for filename '" << m_FileName << "'"); -} + if (!light_get_next_packet((light_pcapng_t*)m_LightPcapNg, &pktHeader, + &pktData)) { + PCPP_LOG_DEBUG("Packet could not be read. Probably end-of-file"); + return false; + } + + while (!m_BpfWrapper.matchPacketWithFilter(pktData, pktHeader.captured_length, + pktHeader.timestamp, + pktHeader.data_link)) { + if (!light_get_next_packet((light_pcapng_t*)m_LightPcapNg, &pktHeader, + &pktData)) { + PCPP_LOG_DEBUG("Packet could not be read. Probably end-of-file"); + return false; + } + } -bool PcapNgFileReaderDevice::setFilter(std::string filterAsString) -{ - return m_BpfWrapper.setFilter(filterAsString); -} + uint8_t* myPacketData = new uint8_t[pktHeader.captured_length]; + memcpy(myPacketData, pktData, pktHeader.captured_length); + if (!rawPacket.setRawData(myPacketData, pktHeader.captured_length, + pktHeader.timestamp, + static_cast(pktHeader.data_link), + pktHeader.original_length)) { + PCPP_LOG_ERROR("Couldn't set data to raw packet"); + return false; + } + + if (pktHeader.comment != nullptr && pktHeader.comment_length > 0) + packetComment = std::string(pktHeader.comment, pktHeader.comment_length); + + m_NumOfPacketsRead++; + return true; +} -void PcapNgFileReaderDevice::close() -{ - if (m_LightPcapNg == nullptr) - return; +bool PcapNgFileReaderDevice::getNextPacket(RawPacket& rawPacket) { + std::string temp; + return getNextPacket(rawPacket, temp); +} - light_pcapng_close((light_pcapng_t*)m_LightPcapNg); - m_LightPcapNg = nullptr; +void PcapNgFileReaderDevice::getStatistics(PcapStats& stats) const { + stats.packetsRecv = m_NumOfPacketsRead; + stats.packetsDrop = m_NumOfPacketsNotParsed; + stats.packetsDropByInterface = 0; + PCPP_LOG_DEBUG("Statistics received for pcapng reader device for filename '" + << m_FileName << "'"); +} - m_DeviceOpened = false; - PCPP_LOG_DEBUG("File reader closed for file '" << m_FileName << "'"); -} +bool PcapNgFileReaderDevice::setFilter(std::string filterAsString) { + return m_BpfWrapper.setFilter(filterAsString); +} +void PcapNgFileReaderDevice::close() { + if (m_LightPcapNg == nullptr) + return; -std::string PcapNgFileReaderDevice::getOS() const -{ - if (m_LightPcapNg == nullptr) - { - PCPP_LOG_ERROR("Pcapng file device '" << m_FileName << "' not opened"); - return ""; - } - - light_pcapng_file_info* fileInfo = light_pcang_get_file_info((light_pcapng_t*)m_LightPcapNg); - if (fileInfo == nullptr) - return ""; - char* res = fileInfo->os_desc; - size_t len = fileInfo->os_desc_size; - if (len == 0 || res == nullptr) - return ""; - - return std::string(res, len); -} + light_pcapng_close((light_pcapng_t*)m_LightPcapNg); + m_LightPcapNg = nullptr; -std::string PcapNgFileReaderDevice::getHardware() const -{ - if (m_LightPcapNg == nullptr) - { - PCPP_LOG_ERROR("Pcapng file device '" << m_FileName << "' not opened"); - return ""; - } + m_DeviceOpened = false; + PCPP_LOG_DEBUG("File reader closed for file '" << m_FileName << "'"); +} + +std::string PcapNgFileReaderDevice::getOS() const { + if (m_LightPcapNg == nullptr) { + PCPP_LOG_ERROR("Pcapng file device '" << m_FileName << "' not opened"); + return ""; + } + + light_pcapng_file_info* fileInfo = + light_pcang_get_file_info((light_pcapng_t*)m_LightPcapNg); + if (fileInfo == nullptr) + return ""; + char* res = fileInfo->os_desc; + size_t len = fileInfo->os_desc_size; + if (len == 0 || res == nullptr) + return ""; + + return std::string(res, len); +} - light_pcapng_file_info* fileInfo = light_pcang_get_file_info((light_pcapng_t*)m_LightPcapNg); - if (fileInfo == nullptr) - return ""; - char* res = fileInfo->hardware_desc; - size_t len = fileInfo->hardware_desc_size; - if (len == 0 || res == nullptr) - return ""; +std::string PcapNgFileReaderDevice::getHardware() const { + if (m_LightPcapNg == nullptr) { + PCPP_LOG_ERROR("Pcapng file device '" << m_FileName << "' not opened"); + return ""; + } - return std::string(res, len); + light_pcapng_file_info* fileInfo = + light_pcang_get_file_info((light_pcapng_t*)m_LightPcapNg); + if (fileInfo == nullptr) + return ""; + char* res = fileInfo->hardware_desc; + size_t len = fileInfo->hardware_desc_size; + if (len == 0 || res == nullptr) + return ""; + + return std::string(res, len); } -std::string PcapNgFileReaderDevice::getCaptureApplication() const -{ - if (m_LightPcapNg == nullptr) - { - PCPP_LOG_ERROR("Pcapng file device '" << m_FileName << "' not opened"); - return ""; - } +std::string PcapNgFileReaderDevice::getCaptureApplication() const { + if (m_LightPcapNg == nullptr) { + PCPP_LOG_ERROR("Pcapng file device '" << m_FileName << "' not opened"); + return ""; + } - light_pcapng_file_info* fileInfo = light_pcang_get_file_info((light_pcapng_t*)m_LightPcapNg); - if (fileInfo == nullptr) - return ""; - char* res = fileInfo->user_app_desc; - size_t len = fileInfo->user_app_desc_size; - if (len == 0 || res == nullptr) - return ""; + light_pcapng_file_info* fileInfo = + light_pcang_get_file_info((light_pcapng_t*)m_LightPcapNg); + if (fileInfo == nullptr) + return ""; + char* res = fileInfo->user_app_desc; + size_t len = fileInfo->user_app_desc_size; + if (len == 0 || res == nullptr) + return ""; - return std::string(res, len); + return std::string(res, len); } -std::string PcapNgFileReaderDevice::getCaptureFileComment() const -{ - if (m_LightPcapNg == nullptr) - { - PCPP_LOG_ERROR("Pcapng file device '" << m_FileName << "' not opened"); - return ""; - } +std::string PcapNgFileReaderDevice::getCaptureFileComment() const { + if (m_LightPcapNg == nullptr) { + PCPP_LOG_ERROR("Pcapng file device '" << m_FileName << "' not opened"); + return ""; + } - light_pcapng_file_info* fileInfo = light_pcang_get_file_info((light_pcapng_t*)m_LightPcapNg); - if (fileInfo == nullptr) - return ""; - char* res = fileInfo->file_comment; - size_t len = fileInfo->file_comment_size; - if (len == 0 || res == nullptr) - return ""; + light_pcapng_file_info* fileInfo = + light_pcang_get_file_info((light_pcapng_t*)m_LightPcapNg); + if (fileInfo == nullptr) + return ""; + char* res = fileInfo->file_comment; + size_t len = fileInfo->file_comment_size; + if (len == 0 || res == nullptr) + return ""; - return std::string(res, len); + return std::string(res, len); } - // ~~~~~~~~~~~~~~~~~~~~~~~~~ // IFileWriterDevice members // ~~~~~~~~~~~~~~~~~~~~~~~~~ -IFileWriterDevice:: IFileWriterDevice(const std::string& fileName) : IFileDevice(fileName) -{ - m_NumOfPacketsNotWritten = 0; - m_NumOfPacketsWritten = 0; +IFileWriterDevice::IFileWriterDevice(const std::string& fileName) + : IFileDevice(fileName) { + m_NumOfPacketsNotWritten = 0; + m_NumOfPacketsWritten = 0; } - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // PcapFileWriterDevice members // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -PcapFileWriterDevice::PcapFileWriterDevice(const std::string& fileName, LinkLayerType linkLayerType) : IFileWriterDevice(fileName) -{ - m_PcapDumpHandler = nullptr; - m_NumOfPacketsNotWritten = 0; - m_NumOfPacketsWritten = 0; - m_PcapLinkLayerType = linkLayerType; - m_AppendMode = false; - m_File = nullptr; -} - -void PcapFileWriterDevice::closeFile() -{ - if (m_AppendMode && m_File != nullptr) - { - fclose(m_File); - m_File = nullptr; - } -} - -bool PcapFileWriterDevice::writePacket(RawPacket const& packet) -{ - if ((!m_AppendMode && m_PcapDescriptor == nullptr) || (m_PcapDumpHandler == nullptr)) - { - PCPP_LOG_ERROR("Device not opened"); - m_NumOfPacketsNotWritten++; - return false; - } - - if (packet.getLinkLayerType() != m_PcapLinkLayerType) - { - PCPP_LOG_ERROR("Cannot write a packet with a different link layer type"); - m_NumOfPacketsNotWritten++; - return false; - } - - pcap_pkthdr pktHdr; - pktHdr.caplen = ((RawPacket&)packet).getRawDataLen(); - pktHdr.len = ((RawPacket&)packet).getFrameLength(); - timespec packet_timestamp = ((RawPacket&)packet).getPacketTimeStamp(); - TIMESPEC_TO_TIMEVAL(&pktHdr.ts, &packet_timestamp); - if (!m_AppendMode) - pcap_dump((uint8_t*)m_PcapDumpHandler, &pktHdr, ((RawPacket&)packet).getRawData()); - else - { - // Below are actually the lines run by pcap_dump. The reason I had to put them instead pcap_dump is that on Windows using WinPcap/Npcap - // you can't pass pointers between libraries compiled with different compilers. In this case - PcapPlusPlus and WinPcap/Npcap weren't - // compiled with the same compiler so it's impossible to fopen a file in PcapPlusPlus, pass the pointer to WinPcap/Npcap and use the - // FILE* pointer there. Doing this throws an exception. So the only option when implementing append to pcap is to write all relevant - // WinPcap/Npcap code that handles opening/closing/writing to pcap files inside PcapPlusPlus code - - // the reason to create this packet_header struct is timeval has different sizes in 32-bit and 64-bit systems, - // but pcap format uses the 32-bit timeval version, so we need to align timeval to that - packet_header pktHdrTemp; - pktHdrTemp.tv_sec = pktHdr.ts.tv_sec; - pktHdrTemp.tv_usec = pktHdr.ts.tv_usec; - pktHdrTemp.caplen = pktHdr.caplen; - pktHdrTemp.len = pktHdr.len; - fwrite(&pktHdrTemp, sizeof(pktHdrTemp), 1, m_File); - fwrite(((RawPacket&)packet).getRawData(), pktHdrTemp.caplen, 1, m_File); - } - PCPP_LOG_DEBUG("Packet written successfully to '" << m_FileName << "'"); - m_NumOfPacketsWritten++; - return true; -} - -bool PcapFileWriterDevice::writePackets(const RawPacketVector& packets) -{ - for (RawPacketVector::ConstVectorIterator iter = packets.begin(); iter != packets.end(); iter++) - { - if (!writePacket(**iter)) - return false; - } - - return true; -} - -bool PcapFileWriterDevice::open() -{ - if (m_PcapDescriptor != nullptr) - { - PCPP_LOG_DEBUG("Pcap descriptor already opened. Nothing to do"); - return true; - } - - switch(m_PcapLinkLayerType) - { - case LINKTYPE_RAW: - case LINKTYPE_DLT_RAW2: - PCPP_LOG_ERROR("The only Raw IP link type supported in libpcap/WinPcap/Npcap is LINKTYPE_DLT_RAW1, please use that instead"); - return false; - default: - break; - } - - m_NumOfPacketsNotWritten = 0; - m_NumOfPacketsWritten = 0; - - m_PcapDescriptor = pcap_open_dead(m_PcapLinkLayerType, PCPP_MAX_PACKET_SIZE); - if (m_PcapDescriptor == nullptr) - { - PCPP_LOG_ERROR("Error opening file writer device for file '" << m_FileName << "': pcap_open_dead returned NULL"); - m_DeviceOpened = false; - return false; - } - - - m_PcapDumpHandler = pcap_dump_open(m_PcapDescriptor, m_FileName.c_str()); - if (m_PcapDumpHandler == nullptr) - { - PCPP_LOG_ERROR("Error opening file writer device for file '" << m_FileName << "': pcap_dump_open returned NULL with error: '" << pcap_geterr(m_PcapDescriptor) << "'"); - m_DeviceOpened = false; - return false; - } - - m_DeviceOpened = true; - PCPP_LOG_DEBUG("File writer device for file '" << m_FileName << "' opened successfully"); - return true; -} - -void PcapFileWriterDevice::flush() -{ - if (!m_DeviceOpened) - return; - - if (!m_AppendMode && pcap_dump_flush(m_PcapDumpHandler) == -1) - { - PCPP_LOG_ERROR("Error while flushing the packets to file"); - } - // in append mode it's impossible to use pcap_dump_flush, see comment above pcap_dump - else if (m_AppendMode && fflush(m_File) == EOF) - { - PCPP_LOG_ERROR("Error while flushing the packets to file"); - } - -} - -void PcapFileWriterDevice::close() -{ - if (!m_DeviceOpened) - return; - - flush(); - - IFileDevice::close(); - - if (!m_AppendMode && m_PcapDumpHandler != nullptr) - { - pcap_dump_close(m_PcapDumpHandler); - } - else if (m_AppendMode && m_File != nullptr) - { - // in append mode it's impossible to use pcap_dump_close, see comment above pcap_dump - fclose(m_File); - } - - m_PcapDumpHandler = nullptr; - m_File = nullptr; - PCPP_LOG_DEBUG("File writer closed for file '" << m_FileName << "'"); -} - -void PcapFileWriterDevice::getStatistics(PcapStats& stats) const -{ - stats.packetsRecv = m_NumOfPacketsWritten; - stats.packetsDrop = m_NumOfPacketsNotWritten; - stats.packetsDropByInterface = 0; - PCPP_LOG_DEBUG("Statistics received for writer device for filename '" << m_FileName << "'"); -} - -bool PcapFileWriterDevice::open(bool appendMode) -{ - if (!appendMode) - return open(); - - m_AppendMode = appendMode; +PcapFileWriterDevice::PcapFileWriterDevice(const std::string& fileName, + LinkLayerType linkLayerType) + : IFileWriterDevice(fileName) { + m_PcapDumpHandler = nullptr; + m_NumOfPacketsNotWritten = 0; + m_NumOfPacketsWritten = 0; + m_PcapLinkLayerType = linkLayerType; + m_AppendMode = false; + m_File = nullptr; +} + +void PcapFileWriterDevice::closeFile() { + if (m_AppendMode && m_File != nullptr) { + fclose(m_File); + m_File = nullptr; + } +} + +bool PcapFileWriterDevice::writePacket(RawPacket const& packet) { + if ((!m_AppendMode && m_PcapDescriptor == nullptr) || + (m_PcapDumpHandler == nullptr)) { + PCPP_LOG_ERROR("Device not opened"); + m_NumOfPacketsNotWritten++; + return false; + } + + if (packet.getLinkLayerType() != m_PcapLinkLayerType) { + PCPP_LOG_ERROR("Cannot write a packet with a different link layer type"); + m_NumOfPacketsNotWritten++; + return false; + } + + pcap_pkthdr pktHdr; + pktHdr.caplen = ((RawPacket&)packet).getRawDataLen(); + pktHdr.len = ((RawPacket&)packet).getFrameLength(); + timespec packet_timestamp = ((RawPacket&)packet).getPacketTimeStamp(); + TIMESPEC_TO_TIMEVAL(&pktHdr.ts, &packet_timestamp); + if (!m_AppendMode) + pcap_dump((uint8_t*)m_PcapDumpHandler, &pktHdr, + ((RawPacket&)packet).getRawData()); + else { + // Below are actually the lines run by pcap_dump. The reason I had to put + // them instead pcap_dump is that on Windows using WinPcap/Npcap you can't + // pass pointers between libraries compiled with different compilers. In + // this case - PcapPlusPlus and WinPcap/Npcap weren't compiled with the same + // compiler so it's impossible to fopen a file in PcapPlusPlus, pass the + // pointer to WinPcap/Npcap and use the FILE* pointer there. Doing this + // throws an exception. So the only option when implementing append to pcap + // is to write all relevant WinPcap/Npcap code that handles + // opening/closing/writing to pcap files inside PcapPlusPlus code + + // the reason to create this packet_header struct is timeval has different + // sizes in 32-bit and 64-bit systems, but pcap format uses the 32-bit + // timeval version, so we need to align timeval to that + packet_header pktHdrTemp; + pktHdrTemp.tv_sec = pktHdr.ts.tv_sec; + pktHdrTemp.tv_usec = pktHdr.ts.tv_usec; + pktHdrTemp.caplen = pktHdr.caplen; + pktHdrTemp.len = pktHdr.len; + fwrite(&pktHdrTemp, sizeof(pktHdrTemp), 1, m_File); + fwrite(((RawPacket&)packet).getRawData(), pktHdrTemp.caplen, 1, m_File); + } + PCPP_LOG_DEBUG("Packet written successfully to '" << m_FileName << "'"); + m_NumOfPacketsWritten++; + return true; +} + +bool PcapFileWriterDevice::writePackets(const RawPacketVector& packets) { + for (RawPacketVector::ConstVectorIterator iter = packets.begin(); + iter != packets.end(); iter++) { + if (!writePacket(**iter)) + return false; + } + + return true; +} + +bool PcapFileWriterDevice::open() { + if (m_PcapDescriptor != nullptr) { + PCPP_LOG_DEBUG("Pcap descriptor already opened. Nothing to do"); + return true; + } + + switch (m_PcapLinkLayerType) { + case LINKTYPE_RAW: + case LINKTYPE_DLT_RAW2: + PCPP_LOG_ERROR( + "The only Raw IP link type supported in libpcap/WinPcap/Npcap is " + "LINKTYPE_DLT_RAW1, please use that instead"); + return false; + default: + break; + } + + m_NumOfPacketsNotWritten = 0; + m_NumOfPacketsWritten = 0; + + m_PcapDescriptor = pcap_open_dead(m_PcapLinkLayerType, PCPP_MAX_PACKET_SIZE); + if (m_PcapDescriptor == nullptr) { + PCPP_LOG_ERROR("Error opening file writer device for file '" + << m_FileName << "': pcap_open_dead returned NULL"); + m_DeviceOpened = false; + return false; + } + + m_PcapDumpHandler = pcap_dump_open(m_PcapDescriptor, m_FileName.c_str()); + if (m_PcapDumpHandler == nullptr) { + PCPP_LOG_ERROR("Error opening file writer device for file '" + << m_FileName + << "': pcap_dump_open returned NULL with error: '" + << pcap_geterr(m_PcapDescriptor) << "'"); + m_DeviceOpened = false; + return false; + } + + m_DeviceOpened = true; + PCPP_LOG_DEBUG("File writer device for file '" << m_FileName + << "' opened successfully"); + return true; +} + +void PcapFileWriterDevice::flush() { + if (!m_DeviceOpened) + return; + + if (!m_AppendMode && pcap_dump_flush(m_PcapDumpHandler) == -1) { + PCPP_LOG_ERROR("Error while flushing the packets to file"); + } + // in append mode it's impossible to use pcap_dump_flush, see comment above + // pcap_dump + else if (m_AppendMode && fflush(m_File) == EOF) { + PCPP_LOG_ERROR("Error while flushing the packets to file"); + } +} + +void PcapFileWriterDevice::close() { + if (!m_DeviceOpened) + return; + + flush(); + + IFileDevice::close(); + + if (!m_AppendMode && m_PcapDumpHandler != nullptr) { + pcap_dump_close(m_PcapDumpHandler); + } else if (m_AppendMode && m_File != nullptr) { + // in append mode it's impossible to use pcap_dump_close, see comment above + // pcap_dump + fclose(m_File); + } + + m_PcapDumpHandler = nullptr; + m_File = nullptr; + PCPP_LOG_DEBUG("File writer closed for file '" << m_FileName << "'"); +} + +void PcapFileWriterDevice::getStatistics(PcapStats& stats) const { + stats.packetsRecv = m_NumOfPacketsWritten; + stats.packetsDrop = m_NumOfPacketsNotWritten; + stats.packetsDropByInterface = 0; + PCPP_LOG_DEBUG("Statistics received for writer device for filename '" + << m_FileName << "'"); +} + +bool PcapFileWriterDevice::open(bool appendMode) { + if (!appendMode) + return open(); + + m_AppendMode = appendMode; #if !defined(_WIN32) - m_File = fopen(m_FileName.c_str(), "r+"); + m_File = fopen(m_FileName.c_str(), "r+"); #else - m_File = fopen(m_FileName.c_str(), "rb+"); + m_File = fopen(m_FileName.c_str(), "rb+"); #endif - if (m_File == nullptr) - { - PCPP_LOG_ERROR("Cannot open '" << m_FileName << "' for reading and writing"); - return false; - } - - pcap_file_header pcapFileHeader; - int amountRead = fread(&pcapFileHeader, 1, sizeof(pcapFileHeader), m_File); - if (amountRead != sizeof(pcap_file_header)) - { - if (ferror(m_File)) - PCPP_LOG_ERROR("Cannot read pcap header from file '" << m_FileName << "', error was: " << errno); - else - PCPP_LOG_ERROR("Cannot read pcap header from file '" << m_FileName << "', unknown error"); - - closeFile(); - return false; - } - - LinkLayerType linkLayerType = static_cast(pcapFileHeader.linktype); - if (linkLayerType != m_PcapLinkLayerType) - { - PCPP_LOG_ERROR("Pcap file has a different link layer type than the one chosen in PcapFileWriterDevice c'tor, " << linkLayerType << ", " << m_PcapLinkLayerType); - closeFile(); - return false; - } - - if (fseek(m_File, 0, SEEK_END) == -1) - { - PCPP_LOG_ERROR("Cannot read pcap file '" << m_FileName << "' to it's end, error was: " << errno); - closeFile(); - return false; - } - - m_PcapDumpHandler = ((pcap_dumper_t *)m_File); - - m_DeviceOpened = true; - PCPP_LOG_DEBUG("File writer device for file '" << m_FileName << "' opened successfully in append mode"); - return true; + if (m_File == nullptr) { + PCPP_LOG_ERROR("Cannot open '" << m_FileName + << "' for reading and writing"); + return false; + } + + pcap_file_header pcapFileHeader; + int amountRead = fread(&pcapFileHeader, 1, sizeof(pcapFileHeader), m_File); + if (amountRead != sizeof(pcap_file_header)) { + if (ferror(m_File)) + PCPP_LOG_ERROR("Cannot read pcap header from file '" + << m_FileName << "', error was: " << errno); + else + PCPP_LOG_ERROR("Cannot read pcap header from file '" + << m_FileName << "', unknown error"); + + closeFile(); + return false; + } + + LinkLayerType linkLayerType = + static_cast(pcapFileHeader.linktype); + if (linkLayerType != m_PcapLinkLayerType) { + PCPP_LOG_ERROR("Pcap file has a different link layer type than the one " + "chosen in PcapFileWriterDevice c'tor, " + << linkLayerType << ", " << m_PcapLinkLayerType); + closeFile(); + return false; + } + + if (fseek(m_File, 0, SEEK_END) == -1) { + PCPP_LOG_ERROR("Cannot read pcap file '" + << m_FileName << "' to it's end, error was: " << errno); + closeFile(); + return false; + } + + m_PcapDumpHandler = ((pcap_dumper_t*)m_File); + + m_DeviceOpened = true; + PCPP_LOG_DEBUG("File writer device for file '" + << m_FileName << "' opened successfully in append mode"); + return true; } - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // PcapNgFileWriterDevice members // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -PcapNgFileWriterDevice::PcapNgFileWriterDevice(const std::string& fileName, int compressionLevel) : IFileWriterDevice(fileName) -{ - m_LightPcapNg = nullptr; - m_CompressionLevel = compressionLevel; -} - -bool PcapNgFileWriterDevice::open(const std::string& os, const std::string& hardware, const std::string& captureApp, const std::string& fileComment) -{ - if (m_LightPcapNg != nullptr) - { - PCPP_LOG_DEBUG("Pcap-ng descriptor already opened. Nothing to do"); - return true; - } +PcapNgFileWriterDevice::PcapNgFileWriterDevice(const std::string& fileName, + int compressionLevel) + : IFileWriterDevice(fileName) { + m_LightPcapNg = nullptr; + m_CompressionLevel = compressionLevel; +} - m_NumOfPacketsNotWritten = 0; - m_NumOfPacketsWritten = 0; +bool PcapNgFileWriterDevice::open(const std::string& os, + const std::string& hardware, + const std::string& captureApp, + const std::string& fileComment) { + if (m_LightPcapNg != nullptr) { + PCPP_LOG_DEBUG("Pcap-ng descriptor already opened. Nothing to do"); + return true; + } - light_pcapng_file_info* info = light_create_file_info(os.c_str(), hardware.c_str(), captureApp.c_str(), fileComment.c_str()); + m_NumOfPacketsNotWritten = 0; + m_NumOfPacketsWritten = 0; - m_LightPcapNg = light_pcapng_open_write(m_FileName.c_str(), info, m_CompressionLevel); - if (m_LightPcapNg == nullptr) - { - PCPP_LOG_ERROR("Error opening file writer device for file '" << m_FileName << "': light_pcapng_open_write returned NULL"); + light_pcapng_file_info* info = light_create_file_info( + os.c_str(), hardware.c_str(), captureApp.c_str(), fileComment.c_str()); - light_free_file_info(info); + m_LightPcapNg = + light_pcapng_open_write(m_FileName.c_str(), info, m_CompressionLevel); + if (m_LightPcapNg == nullptr) { + PCPP_LOG_ERROR("Error opening file writer device for file '" + << m_FileName << "': light_pcapng_open_write returned NULL"); - m_DeviceOpened = false; - return false; - } + light_free_file_info(info); - m_DeviceOpened = true; - PCPP_LOG_DEBUG("pcap-ng writer device for file '" << m_FileName << "' opened successfully"); - return true; + m_DeviceOpened = false; + return false; + } + + m_DeviceOpened = true; + PCPP_LOG_DEBUG("pcap-ng writer device for file '" << m_FileName + << "' opened successfully"); + return true; } -bool PcapNgFileWriterDevice::writePacket(RawPacket const& packet, const std::string& comment) -{ - if (m_LightPcapNg == nullptr) - { - PCPP_LOG_ERROR("Device not opened"); - m_NumOfPacketsNotWritten++; - return false; - } - - if (!m_BpfWrapper.matchPacketWithFilter(&packet)) - { - return false; - } +bool PcapNgFileWriterDevice::writePacket(RawPacket const& packet, + const std::string& comment) { + if (m_LightPcapNg == nullptr) { + PCPP_LOG_ERROR("Device not opened"); + m_NumOfPacketsNotWritten++; + return false; + } + + if (!m_BpfWrapper.matchPacketWithFilter(&packet)) { + return false; + } - light_packet_header pktHeader; - pktHeader.captured_length = ((RawPacket&)packet).getRawDataLen(); - pktHeader.original_length = ((RawPacket&)packet).getFrameLength(); - pktHeader.timestamp = ((RawPacket&)packet).getPacketTimeStamp(); - pktHeader.data_link = (uint16_t)packet.getLinkLayerType(); - pktHeader.interface_id = 0; - if (!comment.empty()) - { - pktHeader.comment = (char*)comment.c_str(); - pktHeader.comment_length = static_cast(comment.size()); - } - else - { - pktHeader.comment = nullptr; - pktHeader.comment_length = 0; - } + light_packet_header pktHeader; + pktHeader.captured_length = ((RawPacket&)packet).getRawDataLen(); + pktHeader.original_length = ((RawPacket&)packet).getFrameLength(); + pktHeader.timestamp = ((RawPacket&)packet).getPacketTimeStamp(); + pktHeader.data_link = (uint16_t)packet.getLinkLayerType(); + pktHeader.interface_id = 0; + if (!comment.empty()) { + pktHeader.comment = (char*)comment.c_str(); + pktHeader.comment_length = static_cast(comment.size()); + } else { + pktHeader.comment = nullptr; + pktHeader.comment_length = 0; + } - const uint8_t* pktData = ((RawPacket&)packet).getRawData(); + const uint8_t* pktData = ((RawPacket&)packet).getRawData(); - light_write_packet((light_pcapng_t*)m_LightPcapNg, &pktHeader, pktData); - m_NumOfPacketsWritten++; - return true; + light_write_packet((light_pcapng_t*)m_LightPcapNg, &pktHeader, pktData); + m_NumOfPacketsWritten++; + return true; } -bool PcapNgFileWriterDevice::writePacket(RawPacket const& packet) -{ - return writePacket(packet, std::string()); +bool PcapNgFileWriterDevice::writePacket(RawPacket const& packet) { + return writePacket(packet, std::string()); } -bool PcapNgFileWriterDevice::writePackets(const RawPacketVector& packets) -{ - for (RawPacketVector::ConstVectorIterator iter = packets.begin(); iter != packets.end(); iter++) - { - if (!writePacket(**iter)) - return false; - } +bool PcapNgFileWriterDevice::writePackets(const RawPacketVector& packets) { + for (RawPacketVector::ConstVectorIterator iter = packets.begin(); + iter != packets.end(); iter++) { + if (!writePacket(**iter)) + return false; + } - return true; + return true; } -bool PcapNgFileWriterDevice::open() -{ - if (m_LightPcapNg != nullptr) - { - PCPP_LOG_DEBUG("Pcap-ng descriptor already opened. Nothing to do"); - return true; - } +bool PcapNgFileWriterDevice::open() { + if (m_LightPcapNg != nullptr) { + PCPP_LOG_DEBUG("Pcap-ng descriptor already opened. Nothing to do"); + return true; + } - m_NumOfPacketsNotWritten = 0; - m_NumOfPacketsWritten = 0; + m_NumOfPacketsNotWritten = 0; + m_NumOfPacketsWritten = 0; - light_pcapng_file_info* info = light_create_default_file_info(); + light_pcapng_file_info* info = light_create_default_file_info(); - m_LightPcapNg = light_pcapng_open_write(m_FileName.c_str(), info, m_CompressionLevel); - if (m_LightPcapNg == nullptr) - { - PCPP_LOG_ERROR("Error opening file writer device for file '" << m_FileName << "': light_pcapng_open_write returned NULL"); + m_LightPcapNg = + light_pcapng_open_write(m_FileName.c_str(), info, m_CompressionLevel); + if (m_LightPcapNg == nullptr) { + PCPP_LOG_ERROR("Error opening file writer device for file '" + << m_FileName << "': light_pcapng_open_write returned NULL"); - light_free_file_info(info); + light_free_file_info(info); - m_DeviceOpened = false; - return false; - } + m_DeviceOpened = false; + return false; + } - m_DeviceOpened = true; - PCPP_LOG_DEBUG("pcap-ng writer device for file '" << m_FileName << "' opened successfully"); - return true; + m_DeviceOpened = true; + PCPP_LOG_DEBUG("pcap-ng writer device for file '" << m_FileName + << "' opened successfully"); + return true; } -bool PcapNgFileWriterDevice::open(bool appendMode) -{ - if (!appendMode) - return open(); +bool PcapNgFileWriterDevice::open(bool appendMode) { + if (!appendMode) + return open(); - m_NumOfPacketsNotWritten = 0; - m_NumOfPacketsWritten = 0; + m_NumOfPacketsNotWritten = 0; + m_NumOfPacketsWritten = 0; - m_LightPcapNg = light_pcapng_open_append(m_FileName.c_str()); - if (m_LightPcapNg == nullptr) - { - PCPP_LOG_ERROR("Error opening file writer device in append mode for file '" << m_FileName << "': light_pcapng_open_append returned NULL"); - m_DeviceOpened = false; - return false; - } - - m_DeviceOpened = true; - PCPP_LOG_DEBUG("pcap-ng writer device for file '" << m_FileName << "' opened successfully"); - return true; + m_LightPcapNg = light_pcapng_open_append(m_FileName.c_str()); + if (m_LightPcapNg == nullptr) { + PCPP_LOG_ERROR("Error opening file writer device in append mode for file '" + << m_FileName + << "': light_pcapng_open_append returned NULL"); + m_DeviceOpened = false; + return false; + } + m_DeviceOpened = true; + PCPP_LOG_DEBUG("pcap-ng writer device for file '" << m_FileName + << "' opened successfully"); + return true; } -void PcapNgFileWriterDevice::flush() -{ - if (!m_DeviceOpened || m_LightPcapNg == nullptr) - return; +void PcapNgFileWriterDevice::flush() { + if (!m_DeviceOpened || m_LightPcapNg == nullptr) + return; - light_pcapng_flush((light_pcapng_t*)m_LightPcapNg); - PCPP_LOG_DEBUG("File writer flushed to file '" << m_FileName << "'"); + light_pcapng_flush((light_pcapng_t*)m_LightPcapNg); + PCPP_LOG_DEBUG("File writer flushed to file '" << m_FileName << "'"); } -void PcapNgFileWriterDevice::close() -{ - if (m_LightPcapNg == nullptr) - return; +void PcapNgFileWriterDevice::close() { + if (m_LightPcapNg == nullptr) + return; - light_pcapng_close((light_pcapng_t*)m_LightPcapNg); - m_LightPcapNg = nullptr; + light_pcapng_close((light_pcapng_t*)m_LightPcapNg); + m_LightPcapNg = nullptr; - m_DeviceOpened = false; - PCPP_LOG_DEBUG("File writer closed for file '" << m_FileName << "'"); + m_DeviceOpened = false; + PCPP_LOG_DEBUG("File writer closed for file '" << m_FileName << "'"); } -void PcapNgFileWriterDevice::getStatistics(PcapStats& stats) const -{ - stats.packetsRecv = m_NumOfPacketsWritten; - stats.packetsDrop = m_NumOfPacketsNotWritten; - stats.packetsDropByInterface = 0; - PCPP_LOG_DEBUG("Statistics received for pcap-ng writer device for filename '" << m_FileName << "'"); +void PcapNgFileWriterDevice::getStatistics(PcapStats& stats) const { + stats.packetsRecv = m_NumOfPacketsWritten; + stats.packetsDrop = m_NumOfPacketsNotWritten; + stats.packetsDropByInterface = 0; + PCPP_LOG_DEBUG("Statistics received for pcap-ng writer device for filename '" + << m_FileName << "'"); } -bool PcapNgFileWriterDevice::setFilter(std::string filterAsString) -{ - return m_BpfWrapper.setFilter(filterAsString); +bool PcapNgFileWriterDevice::setFilter(std::string filterAsString) { + return m_BpfWrapper.setFilter(filterAsString); } - } // namespace pcpp diff --git a/Pcap++/src/PcapFilter.cpp b/Pcap++/src/PcapFilter.cpp index 747e29d649..8d1f4ffb56 100644 --- a/Pcap++/src/PcapFilter.cpp +++ b/Pcap++/src/PcapFilter.cpp @@ -1,469 +1,428 @@ #define LOG_MODULE PcapLogModuleLiveDevice #include "PcapFilter.h" -#include "Logger.h" #include "IPv4Layer.h" +#include "Logger.h" #include #if defined(_WIN32) #include #endif -#include "pcap.h" #include "RawPacket.h" #include "TimespecTimeval.h" +#include "pcap.h" -namespace pcpp -{ +namespace pcpp { static const int DEFAULT_SNAPLEN = 9000; -bool GeneralFilter::matchPacketWithFilter(RawPacket* rawPacket) -{ - std::string filterStr; - parseToString(filterStr); +bool GeneralFilter::matchPacketWithFilter(RawPacket* rawPacket) { + std::string filterStr; + parseToString(filterStr); - if (!m_BpfWrapper.setFilter(filterStr)) - return false; + if (!m_BpfWrapper.setFilter(filterStr)) + return false; - return m_BpfWrapper.matchPacketWithFilter(rawPacket); + return m_BpfWrapper.matchPacketWithFilter(rawPacket); } -BpfFilterWrapper::BpfFilterWrapper() -{ - m_Program = nullptr; - m_LinkType = LINKTYPE_ETHERNET; +BpfFilterWrapper::BpfFilterWrapper() { + m_Program = nullptr; + m_LinkType = LINKTYPE_ETHERNET; +} + +BpfFilterWrapper::~BpfFilterWrapper() { freeProgram(); } + +bool BpfFilterWrapper::setFilter(const std::string& filter, + LinkLayerType linkType) { + if (filter.empty()) { + freeProgram(); + return true; + } + + if (filter != m_FilterStr || linkType != m_LinkType) { + pcap_t* pcap = pcap_open_dead(linkType, DEFAULT_SNAPLEN); + if (pcap == nullptr) { + return false; + } + + bpf_program* newProg = new bpf_program; + int ret = pcap_compile(pcap, newProg, filter.c_str(), 1, 0); + pcap_close(pcap); + if (ret < 0) { + delete newProg; + return false; + } + + freeProgram(); + m_Program = newProg; + m_FilterStr = filter; + m_LinkType = linkType; + } + + return true; +} + +void BpfFilterWrapper::freeProgram() { + if (m_Program != nullptr) { + pcap_freecode(m_Program); + delete m_Program; + m_Program = nullptr; + m_FilterStr.clear(); + } +} + +bool BpfFilterWrapper::matchPacketWithFilter(const RawPacket* rawPacket) { + return matchPacketWithFilter( + rawPacket->getRawData(), rawPacket->getRawDataLen(), + rawPacket->getPacketTimeStamp(), rawPacket->getLinkLayerType()); +} + +bool BpfFilterWrapper::matchPacketWithFilter(const uint8_t* packetData, + uint32_t packetDataLength, + timespec packetTimestamp, + uint16_t linkType) { + if (m_FilterStr.empty()) + return true; + + if (!setFilter(std::string(m_FilterStr), + static_cast(linkType))) { + return false; + } + + struct pcap_pkthdr pktHdr; + pktHdr.caplen = packetDataLength; + pktHdr.len = packetDataLength; + TIMESPEC_TO_TIMEVAL(&pktHdr.ts, &packetTimestamp); + + return (pcap_offline_filter(m_Program, &pktHdr, packetData) != 0); } -BpfFilterWrapper::~BpfFilterWrapper() -{ - freeProgram(); +void BPFStringFilter::parseToString(std::string& result) { + result = m_FilterStr; +} + +bool BPFStringFilter::verifyFilter() { + return m_BpfWrapper.setFilter(m_FilterStr); +} + +void IFilterWithDirection::parseDirection(std::string& directionAsString) { + switch (m_Dir) { + case SRC: + directionAsString = "src"; + break; + case DST: + directionAsString = "dst"; + break; + default: // SRC_OR_DST: + directionAsString = "src or dst"; + break; + } +} + +std::string IFilterWithOperator::parseOperator() { + switch (m_Operator) { + case EQUALS: + return "="; + case NOT_EQUALS: + return "!="; + case GREATER_THAN: + return ">"; + case GREATER_OR_EQUAL: + return ">="; + case LESS_THAN: + return "<"; + case LESS_OR_EQUAL: + return "<="; + default: + return ""; + } +} + +void IPFilter::convertToIPAddressWithMask(std::string& ipAddrmodified, + std::string& mask) const { + if (m_IPv4Mask.empty()) + return; + + // Handle the mask + + // The following code lines verify both ipAddress and ipv4Mask are valid IPv4 + // addresses The IPv4 limitation comes from the fact libPcap/WinPcap/Npcap + // doesn't support mask for IPv6 addresses + + IPv4Address ipAddr(m_Address); + if (!ipAddr.isValid()) { + PCPP_LOG_ERROR("IP filter with mask must be used with IPv4 valid address. " + "Setting the mask to an empty value"); + mask.clear(); + return; + } + + IPv4Address maskAsAddr(m_IPv4Mask); + if (!maskAsAddr.isValid()) { + PCPP_LOG_ERROR("Invalid IPv4 mask. Setting the mask to an empty"); + mask.clear(); + return; + } + + // If all addresses are IPv4 valid addresses, make sure ipAddress matches the + // mask. If it's not, mask the address with the mask The reason for doing that + // is libPcap/WinPcap/Npcap doesn't allow filtering an IP address that doesn't + // match the mask + + uint32_t addrAsIntAfterMask = ipAddr.toInt() & maskAsAddr.toInt(); + ipAddrmodified = IPv4Address(addrAsIntAfterMask).toString(); +} + +void IPFilter::convertToIPAddressWithLen(std::string& ipAddrmodified) const { + if (m_Len == 0) + return; + + // Handle the length + + // The following code lines verify IP address is valid (IPv4 or IPv6) + + IPAddress ipAddr = IPAddress(ipAddrmodified); + if (!ipAddr.isValid()) { + PCPP_LOG_ERROR("Invalid IP address '" << ipAddrmodified + << "', setting len to zero"); + return; + } + + if (ipAddr.getType() == IPAddress::IPv4AddressType) { + uint32_t addrAsInt = ipAddr.getIPv4().toInt(); + uint32_t mask = ((uint32_t)-1) >> ((sizeof(uint32_t) * 8) - m_Len); + addrAsInt &= mask; + ipAddrmodified = IPv4Address(addrAsInt).toString(); + } +} + +void IPFilter::parseToString(std::string& result) { + std::string dir; + std::string ipAddr = m_Address; + std::string mask = m_IPv4Mask; + convertToIPAddressWithMask(ipAddr, mask); + convertToIPAddressWithLen(ipAddr); + parseDirection(dir); + result = "ip and " + dir + " net " + ipAddr; + if (m_IPv4Mask != "") + result += " mask " + mask; + else if (m_Len > 0) { + std::ostringstream stream; + stream << m_Len; + result += '/' + stream.str(); + } +} + +void IPv4IDFilter::parseToString(std::string& result) { + std::string op = parseOperator(); + std::ostringstream stream; + stream << m_IpID; + result = "ip[4:2] " + op + ' ' + stream.str(); +} + +void IPv4TotalLengthFilter::parseToString(std::string& result) { + std::string op = parseOperator(); + std::ostringstream stream; + stream << m_TotalLength; + result = "ip[2:2] " + op + ' ' + stream.str(); +} + +void PortFilter::portToString(uint16_t portAsInt) { + std::ostringstream stream; + stream << portAsInt; + m_Port = stream.str(); } -bool BpfFilterWrapper::setFilter(const std::string& filter, LinkLayerType linkType) -{ - if (filter.empty()) - { - freeProgram(); - return true; - } - - if (filter != m_FilterStr || linkType != m_LinkType) - { - pcap_t* pcap = pcap_open_dead(linkType, DEFAULT_SNAPLEN); - if (pcap == nullptr) - { - return false; - } - - bpf_program* newProg = new bpf_program; - int ret = pcap_compile(pcap, newProg, filter.c_str(), 1, 0); - pcap_close(pcap); - if (ret < 0) - { - delete newProg; - return false; - } - - freeProgram(); - m_Program = newProg; - m_FilterStr = filter; - m_LinkType = linkType; - } - - return true; -} - -void BpfFilterWrapper::freeProgram() -{ - if (m_Program != nullptr) - { - pcap_freecode(m_Program); - delete m_Program; - m_Program = nullptr; - m_FilterStr.clear(); - } -} - -bool BpfFilterWrapper::matchPacketWithFilter(const RawPacket* rawPacket) -{ - return matchPacketWithFilter(rawPacket->getRawData(), rawPacket->getRawDataLen(), rawPacket->getPacketTimeStamp(), rawPacket->getLinkLayerType()); -} - -bool BpfFilterWrapper::matchPacketWithFilter(const uint8_t* packetData, uint32_t packetDataLength, timespec packetTimestamp, uint16_t linkType) -{ - if (m_FilterStr.empty()) - return true; - - if (!setFilter(std::string(m_FilterStr), static_cast(linkType))) - { - return false; - } - - struct pcap_pkthdr pktHdr; - pktHdr.caplen = packetDataLength; - pktHdr.len = packetDataLength; - TIMESPEC_TO_TIMEVAL(&pktHdr.ts, &packetTimestamp); - - return (pcap_offline_filter(m_Program, &pktHdr, packetData) != 0); -} - -void BPFStringFilter::parseToString(std::string& result) -{ - result = m_FilterStr; -} - -bool BPFStringFilter::verifyFilter() -{ - return m_BpfWrapper.setFilter(m_FilterStr); -} - -void IFilterWithDirection::parseDirection(std::string& directionAsString) -{ - switch (m_Dir) - { - case SRC: - directionAsString = "src"; - break; - case DST: - directionAsString = "dst"; - break; - default: //SRC_OR_DST: - directionAsString = "src or dst"; - break; - } -} - -std::string IFilterWithOperator::parseOperator() -{ - switch(m_Operator) - { - case EQUALS: - return "="; - case NOT_EQUALS: - return "!="; - case GREATER_THAN: - return ">"; - case GREATER_OR_EQUAL: - return ">="; - case LESS_THAN: - return "<"; - case LESS_OR_EQUAL: - return "<="; - default: - return ""; - } -} - -void IPFilter::convertToIPAddressWithMask(std::string& ipAddrmodified, std::string& mask) const -{ - if (m_IPv4Mask.empty()) - return; - - // Handle the mask - - // The following code lines verify both ipAddress and ipv4Mask are valid IPv4 addresses - // The IPv4 limitation comes from the fact libPcap/WinPcap/Npcap doesn't support mask for IPv6 addresses - - IPv4Address ipAddr(m_Address); - if (!ipAddr.isValid()) - { - PCPP_LOG_ERROR("IP filter with mask must be used with IPv4 valid address. Setting the mask to an empty value"); - mask.clear(); - return; - } - - IPv4Address maskAsAddr(m_IPv4Mask); - if (!maskAsAddr.isValid()) - { - PCPP_LOG_ERROR("Invalid IPv4 mask. Setting the mask to an empty"); - mask.clear(); - return; - } - - // If all addresses are IPv4 valid addresses, make sure ipAddress matches the mask. If it's not, mask the address with the mask - // The reason for doing that is libPcap/WinPcap/Npcap doesn't allow filtering an IP address that doesn't match the mask - - uint32_t addrAsIntAfterMask = ipAddr.toInt() & maskAsAddr.toInt(); - ipAddrmodified = IPv4Address(addrAsIntAfterMask).toString(); -} - -void IPFilter::convertToIPAddressWithLen(std::string& ipAddrmodified) const -{ - if (m_Len == 0) - return; - - // Handle the length - - // The following code lines verify IP address is valid (IPv4 or IPv6) - - IPAddress ipAddr = IPAddress(ipAddrmodified); - if (!ipAddr.isValid()) - { - PCPP_LOG_ERROR("Invalid IP address '" << ipAddrmodified << "', setting len to zero"); - return; - } - - if (ipAddr.getType() == IPAddress::IPv4AddressType) - { - uint32_t addrAsInt = ipAddr.getIPv4().toInt(); - uint32_t mask = ((uint32_t) - 1) >> ((sizeof(uint32_t) * 8) - m_Len); - addrAsInt &= mask; - ipAddrmodified = IPv4Address(addrAsInt).toString(); - } -} - -void IPFilter::parseToString(std::string& result) -{ - std::string dir; - std::string ipAddr = m_Address; - std::string mask = m_IPv4Mask; - convertToIPAddressWithMask(ipAddr, mask); - convertToIPAddressWithLen(ipAddr); - parseDirection(dir); - result = "ip and " + dir + " net " + ipAddr; - if (m_IPv4Mask != "") - result += " mask " + mask; - else if (m_Len > 0) - { - std::ostringstream stream; - stream << m_Len; - result += '/' + stream.str(); - } -} - -void IPv4IDFilter::parseToString(std::string& result) -{ - std::string op = parseOperator(); - std::ostringstream stream; - stream << m_IpID; - result = "ip[4:2] " + op + ' ' + stream.str(); -} - -void IPv4TotalLengthFilter::parseToString(std::string& result) -{ - std::string op = parseOperator(); - std::ostringstream stream; - stream << m_TotalLength; - result = "ip[2:2] " + op + ' ' + stream.str(); -} - -void PortFilter::portToString(uint16_t portAsInt) -{ - std::ostringstream stream; - stream << portAsInt; - m_Port = stream.str(); -} - -PortFilter::PortFilter(uint16_t port, Direction dir) : IFilterWithDirection(dir) -{ - portToString(port); -} - -void PortFilter::parseToString(std::string& result) -{ - std::string dir; - parseDirection(dir); - result = dir + " port " + m_Port; -} - -void PortRangeFilter::parseToString(std::string& result) -{ - std::string dir; - parseDirection(dir); - - std::ostringstream fromPortStream; - fromPortStream << (int)m_FromPort; - std::ostringstream toPortStream; - toPortStream << (int)m_ToPort; - - result = dir + " portrange " + fromPortStream.str() + '-' + toPortStream.str(); -} - -void MacAddressFilter::parseToString(std::string& result) -{ - if (getDir() != SRC_OR_DST) - { - std::string dir; - parseDirection(dir); - result = "ether " + dir + ' ' + m_MacAddress.toString(); - } - else - result = "ether host " + m_MacAddress.toString(); -} - -void EtherTypeFilter::parseToString(std::string& result) -{ - std::ostringstream stream; - stream << "0x" << std::hex << m_EtherType; - result = "ether proto " + stream.str(); -} - -AndFilter::AndFilter(std::vector& filters) -{ - for(std::vector::iterator it = filters.begin(); it != filters.end(); ++it) - { - m_FilterList.push_back(*it); - } -} - -void AndFilter::setFilters(std::vector& filters) -{ - m_FilterList.clear(); - - for(std::vector::iterator it = filters.begin(); it != filters.end(); ++it) - { - m_FilterList.push_back(*it); - } -} - -void AndFilter::parseToString(std::string& result) -{ - result.clear(); - for(std::vector::iterator it = m_FilterList.begin(); it != m_FilterList.end(); ++it) - { - std::string innerFilter; - (*it)->parseToString(innerFilter); - result += '(' + innerFilter + ')'; - if (m_FilterList.back() != *it) - { - result += " and "; - } - } -} - -OrFilter::OrFilter(std::vector& filters) -{ - for(std::vector::iterator it = filters.begin(); it != filters.end(); ++it) - { - m_FilterList.push_back(*it); - } -} - -void OrFilter::parseToString(std::string& result) -{ - result.clear(); - for(std::vector::iterator it = m_FilterList.begin(); it != m_FilterList.end(); ++it) - { - std::string innerFilter; - (*it)->parseToString(innerFilter); - result += '(' + innerFilter + ')'; - if (m_FilterList.back() != *it) - { - result += " or "; - } - } -} - -void NotFilter::parseToString(std::string& result) -{ - std::string innerFilterAsString; - m_FilterToInverse->parseToString(innerFilterAsString); - result = "not (" + innerFilterAsString + ')'; -} - -void ProtoFilter::parseToString(std::string& result) -{ - std::ostringstream stream; - - switch (m_Proto) - { - case TCP: - result = "tcp"; - break; - case UDP: - result = "udp"; - break; - case ICMP: - result = "icmp"; - break; - case VLAN: - result = "vlan"; - break; - case IPv4: - result = "ip"; - break; - case IPv6: - result = "ip6"; - break; - case ARP: - result = "arp"; - break; - case Ethernet: - result = "ether"; - break; - case GRE: - stream << "proto " << PACKETPP_IPPROTO_GRE; - result = stream.str(); - break; - case IGMP: - stream << "proto " << PACKETPP_IPPROTO_IGMP; - result = stream.str(); - break; - default: - break; - } -} - -void ArpFilter::parseToString(std::string& result) -{ - std::ostringstream sstream; - sstream << "arp[7] = " << m_OpCode; - result += sstream.str(); -} - -void VlanFilter::parseToString(std::string& result) -{ - std::ostringstream stream; - stream << m_VlanID; - result = "vlan " + stream.str(); -} - -void TcpFlagsFilter::parseToString(std::string& result) -{ - if (m_TcpFlagsBitMask == 0) - { - result.clear(); - return; - } - - result = "tcp[tcpflags] & ("; - if ((m_TcpFlagsBitMask & tcpFin) != 0) - result += "tcp-fin|"; - if ((m_TcpFlagsBitMask & tcpSyn) != 0) - result += "tcp-syn|"; - if ((m_TcpFlagsBitMask & tcpRst) != 0) - result += "tcp-rst|"; - if ((m_TcpFlagsBitMask & tcpPush) != 0) - result += "tcp-push|"; - if ((m_TcpFlagsBitMask & tcpAck) != 0) - result += "tcp-ack|"; - if ((m_TcpFlagsBitMask & tcpUrg) != 0) - result += "tcp-urg|"; - - // replace the last '|' character - result[result.size() - 1] = ')'; - - if (m_MatchOption == MatchOneAtLeast) - result += " != 0"; - else //m_MatchOption == MatchAll - { - std::ostringstream stream; - stream << (int)m_TcpFlagsBitMask; - result += " = " + stream.str(); - } -} - -void TcpWindowSizeFilter::parseToString(std::string& result) -{ - std::ostringstream stream; - stream << m_WindowSize; - result = "tcp[14:2] " + parseOperator() + ' ' + stream.str(); -} - -void UdpLengthFilter::parseToString(std::string& result) -{ - std::ostringstream stream; - stream << m_Length; - result = "udp[4:2] " + parseOperator() + ' ' + stream.str(); +PortFilter::PortFilter(uint16_t port, Direction dir) + : IFilterWithDirection(dir) { + portToString(port); +} + +void PortFilter::parseToString(std::string& result) { + std::string dir; + parseDirection(dir); + result = dir + " port " + m_Port; +} + +void PortRangeFilter::parseToString(std::string& result) { + std::string dir; + parseDirection(dir); + + std::ostringstream fromPortStream; + fromPortStream << (int)m_FromPort; + std::ostringstream toPortStream; + toPortStream << (int)m_ToPort; + + result = + dir + " portrange " + fromPortStream.str() + '-' + toPortStream.str(); +} + +void MacAddressFilter::parseToString(std::string& result) { + if (getDir() != SRC_OR_DST) { + std::string dir; + parseDirection(dir); + result = "ether " + dir + ' ' + m_MacAddress.toString(); + } else + result = "ether host " + m_MacAddress.toString(); +} + +void EtherTypeFilter::parseToString(std::string& result) { + std::ostringstream stream; + stream << "0x" << std::hex << m_EtherType; + result = "ether proto " + stream.str(); +} + +AndFilter::AndFilter(std::vector& filters) { + for (std::vector::iterator it = filters.begin(); + it != filters.end(); ++it) { + m_FilterList.push_back(*it); + } +} + +void AndFilter::setFilters(std::vector& filters) { + m_FilterList.clear(); + + for (std::vector::iterator it = filters.begin(); + it != filters.end(); ++it) { + m_FilterList.push_back(*it); + } +} + +void AndFilter::parseToString(std::string& result) { + result.clear(); + for (std::vector::iterator it = m_FilterList.begin(); + it != m_FilterList.end(); ++it) { + std::string innerFilter; + (*it)->parseToString(innerFilter); + result += '(' + innerFilter + ')'; + if (m_FilterList.back() != *it) { + result += " and "; + } + } +} + +OrFilter::OrFilter(std::vector& filters) { + for (std::vector::iterator it = filters.begin(); + it != filters.end(); ++it) { + m_FilterList.push_back(*it); + } +} + +void OrFilter::parseToString(std::string& result) { + result.clear(); + for (std::vector::iterator it = m_FilterList.begin(); + it != m_FilterList.end(); ++it) { + std::string innerFilter; + (*it)->parseToString(innerFilter); + result += '(' + innerFilter + ')'; + if (m_FilterList.back() != *it) { + result += " or "; + } + } +} + +void NotFilter::parseToString(std::string& result) { + std::string innerFilterAsString; + m_FilterToInverse->parseToString(innerFilterAsString); + result = "not (" + innerFilterAsString + ')'; +} + +void ProtoFilter::parseToString(std::string& result) { + std::ostringstream stream; + + switch (m_Proto) { + case TCP: + result = "tcp"; + break; + case UDP: + result = "udp"; + break; + case ICMP: + result = "icmp"; + break; + case VLAN: + result = "vlan"; + break; + case IPv4: + result = "ip"; + break; + case IPv6: + result = "ip6"; + break; + case ARP: + result = "arp"; + break; + case Ethernet: + result = "ether"; + break; + case GRE: + stream << "proto " << PACKETPP_IPPROTO_GRE; + result = stream.str(); + break; + case IGMP: + stream << "proto " << PACKETPP_IPPROTO_IGMP; + result = stream.str(); + break; + default: + break; + } +} + +void ArpFilter::parseToString(std::string& result) { + std::ostringstream sstream; + sstream << "arp[7] = " << m_OpCode; + result += sstream.str(); +} + +void VlanFilter::parseToString(std::string& result) { + std::ostringstream stream; + stream << m_VlanID; + result = "vlan " + stream.str(); +} + +void TcpFlagsFilter::parseToString(std::string& result) { + if (m_TcpFlagsBitMask == 0) { + result.clear(); + return; + } + + result = "tcp[tcpflags] & ("; + if ((m_TcpFlagsBitMask & tcpFin) != 0) + result += "tcp-fin|"; + if ((m_TcpFlagsBitMask & tcpSyn) != 0) + result += "tcp-syn|"; + if ((m_TcpFlagsBitMask & tcpRst) != 0) + result += "tcp-rst|"; + if ((m_TcpFlagsBitMask & tcpPush) != 0) + result += "tcp-push|"; + if ((m_TcpFlagsBitMask & tcpAck) != 0) + result += "tcp-ack|"; + if ((m_TcpFlagsBitMask & tcpUrg) != 0) + result += "tcp-urg|"; + + // replace the last '|' character + result[result.size() - 1] = ')'; + + if (m_MatchOption == MatchOneAtLeast) + result += " != 0"; + else // m_MatchOption == MatchAll + { + std::ostringstream stream; + stream << (int)m_TcpFlagsBitMask; + result += " = " + stream.str(); + } +} + +void TcpWindowSizeFilter::parseToString(std::string& result) { + std::ostringstream stream; + stream << m_WindowSize; + result = "tcp[14:2] " + parseOperator() + ' ' + stream.str(); +} + +void UdpLengthFilter::parseToString(std::string& result) { + std::ostringstream stream; + stream << m_Length; + result = "udp[4:2] " + parseOperator() + ' ' + stream.str(); } } // namespace pcpp diff --git a/Pcap++/src/PcapLiveDevice.cpp b/Pcap++/src/PcapLiveDevice.cpp index c799dc9aea..fce4ee7651 100644 --- a/Pcap++/src/PcapLiveDevice.cpp +++ b/Pcap++/src/PcapLiveDevice.cpp @@ -1,1025 +1,962 @@ #define LOG_MODULE PcapLogModuleLiveDevice -#include "IpUtils.h" #include "PcapLiveDevice.h" -#include "PcapLiveDeviceList.h" +#include "IpUtils.h" #include "Packet.h" -#ifndef _MSC_VER +#include "PcapLiveDeviceList.h" +#ifndef _MSC_VER #include #endif // ! _MSC_VER -#include "pcap.h" -#include #include "Logger.h" #include "SystemUtils.h" -#include -#include +#include "pcap.h" #include +#include #include +#include +#include #if defined(_WIN32) -// The definition of BPF_MAJOR_VERSION is required to support Npcap. In Npcap there are -// compilation errors due to struct redefinition when including both Packet32.h and pcap.h -// This define statement eliminates these errors +// The definition of BPF_MAJOR_VERSION is required to support Npcap. In Npcap +// there are compilation errors due to struct redefinition when including both +// Packet32.h and pcap.h This define statement eliminates these errors #ifndef BPF_MAJOR_VERSION #define BPF_MAJOR_VERSION 1 #endif // BPF_MAJOR_VERSION -#include #include -#include #include +#include +#include #else #include -#include #include +#include #endif // if defined(_WIN32) #if defined(__APPLE__) || defined(__FreeBSD__) #include #include #endif -// On Mac OS X and FreeBSD timeout of -1 causes pcap_open_live to fail so value of 1ms is set here. -// On Linux and Windows this is not the case so we keep the -1 value +// On Mac OS X and FreeBSD timeout of -1 causes pcap_open_live to fail so value +// of 1ms is set here. On Linux and Windows this is not the case so we keep the +// -1 value #if defined(__APPLE__) || defined(__FreeBSD__) #define LIBPCAP_OPEN_LIVE_TIMEOUT 1 #else #define LIBPCAP_OPEN_LIVE_TIMEOUT -1 #endif -static const char *NFLOG_IFACE = "nflog"; +static const char* NFLOG_IFACE = "nflog"; static const int DEFAULT_SNAPLEN = 9000; -namespace pcpp -{ +namespace pcpp { #ifdef HAS_SET_DIRECTION_ENABLED -static pcap_direction_t directionTypeMap(PcapLiveDevice::PcapDirection direction) -{ - switch (direction) - { - case PcapLiveDevice::PCPP_IN: return PCAP_D_IN; - case PcapLiveDevice::PCPP_OUT: return PCAP_D_OUT; - case PcapLiveDevice::PCPP_INOUT: return PCAP_D_INOUT; - } +static pcap_direction_t +directionTypeMap(PcapLiveDevice::PcapDirection direction) { + switch (direction) { + case PcapLiveDevice::PCPP_IN: + return PCAP_D_IN; + case PcapLiveDevice::PCPP_OUT: + return PCAP_D_OUT; + case PcapLiveDevice::PCPP_INOUT: + return PCAP_D_INOUT; + } } #endif - - -PcapLiveDevice::PcapLiveDevice(pcap_if_t* pInterface, bool calculateMTU, bool calculateMacAddress, bool calculateDefaultGateway) : IPcapDevice(), - m_MacAddress(""), m_DefaultGateway(IPv4Address::Zero) -{ - m_DeviceMtu = 0; - m_LinkType = LINKTYPE_ETHERNET; - - m_IsLoopback = (pInterface->flags & 0x1) == PCAP_IF_LOOPBACK; - - m_Name = pInterface->name; - if (pInterface->description != nullptr) - m_Description = pInterface->description; - PCPP_LOG_DEBUG("Added live device: name=" << m_Name << "; desc=" << m_Description); - PCPP_LOG_DEBUG(" Addresses:"); - while (pInterface->addresses != nullptr) - { - m_Addresses.insert(m_Addresses.end(), *(pInterface->addresses)); - pInterface->addresses = pInterface->addresses->next; - if (Logger::getInstance().isDebugEnabled(PcapLogModuleLiveDevice) && pInterface->addresses != nullptr && pInterface->addresses->addr != nullptr) - { - char addrAsString[INET6_ADDRSTRLEN]; - internal::sockaddr2string(pInterface->addresses->addr, addrAsString); - PCPP_LOG_DEBUG(" " << addrAsString); - } - } - - if (calculateMTU) - { - setDeviceMtu(); - PCPP_LOG_DEBUG(" MTU: " << m_DeviceMtu); - } - - if (calculateDefaultGateway) - { - setDefaultGateway(); - PCPP_LOG_DEBUG(" Default Gateway: " << m_DefaultGateway); - } - - //init all other members - m_CaptureThreadStarted = false; - m_StatsThreadStarted = false; - m_StopThread = false; - m_CaptureThread = {}; - m_StatsThread = {}; - m_cbOnPacketArrives = nullptr; - m_cbOnStatsUpdate = nullptr; - m_cbOnPacketArrivesBlockingMode = nullptr; - m_cbOnPacketArrivesBlockingModeUserCookie = nullptr; - m_IntervalToUpdateStats = 0; - m_cbOnPacketArrivesUserCookie = nullptr; - m_cbOnStatsUpdateUserCookie = nullptr; - m_CaptureCallbackMode = true; - m_CapturedPackets = nullptr; - if (calculateMacAddress) - { - setDeviceMacAddress(); - if (m_MacAddress.isValid()) - PCPP_LOG_DEBUG(" MAC addr: " << m_MacAddress); - } -} - -void PcapLiveDevice::onPacketArrives(uint8_t* user, const struct pcap_pkthdr* pkthdr, const uint8_t* packet) -{ - PcapLiveDevice* pThis = (PcapLiveDevice*)user; - if (pThis == nullptr) - { - PCPP_LOG_ERROR("Unable to extract PcapLiveDevice instance"); - return; - } - - RawPacket rawPacket(packet, pkthdr->caplen, pkthdr->ts, false, pThis->getLinkType()); - - if (pThis->m_cbOnPacketArrives != nullptr) - pThis->m_cbOnPacketArrives(&rawPacket, pThis, pThis->m_cbOnPacketArrivesUserCookie); -} - -void PcapLiveDevice::onPacketArrivesNoCallback(uint8_t* user, const struct pcap_pkthdr* pkthdr, const uint8_t* packet) -{ - PcapLiveDevice* pThis = (PcapLiveDevice*)user; - if (pThis == nullptr) - { - PCPP_LOG_ERROR("Unable to extract PcapLiveDevice instance"); - return; - } - - uint8_t* packetData = new uint8_t[pkthdr->caplen]; - memcpy(packetData, packet, pkthdr->caplen); - RawPacket* rawPacketPtr = new RawPacket(packetData, pkthdr->caplen, pkthdr->ts, true, pThis->getLinkType()); - pThis->m_CapturedPackets->pushBack(rawPacketPtr); -} - -void PcapLiveDevice::onPacketArrivesBlockingMode(uint8_t* user, const struct pcap_pkthdr* pkthdr, const uint8_t* packet) -{ - PcapLiveDevice* pThis = (PcapLiveDevice*)user; - if (pThis == nullptr) - { - PCPP_LOG_ERROR("Unable to extract PcapLiveDevice instance"); - return; - } - - RawPacket rawPacket(packet, pkthdr->caplen, pkthdr->ts, false, pThis->getLinkType()); - - if (pThis->m_cbOnPacketArrivesBlockingMode != nullptr) - if (pThis->m_cbOnPacketArrivesBlockingMode(&rawPacket, pThis, pThis->m_cbOnPacketArrivesBlockingModeUserCookie)) - pThis->m_StopThread = true; -} - -void PcapLiveDevice::captureThreadMain() -{ - PCPP_LOG_DEBUG("Started capture thread for device '" << m_Name << "'"); - if (m_CaptureCallbackMode) - { - while (!m_StopThread) - { - if (pcap_dispatch(m_PcapDescriptor, -1, onPacketArrives, reinterpret_cast(this)) == -1) - { - PCPP_LOG_ERROR("pcap_dispatch returned an error: " << pcap_geterr(m_PcapDescriptor)); - m_StopThread = true; - } - } - } - else - { - while (!m_StopThread) - { - if (pcap_dispatch(m_PcapDescriptor, 100, onPacketArrivesNoCallback, reinterpret_cast(this)) == -1) - { - PCPP_LOG_ERROR("pcap_dispatch returned an error: " << pcap_geterr(m_PcapDescriptor)); - m_StopThread = true; - } - } - } - PCPP_LOG_DEBUG("Ended capture thread for device '" << m_Name << "'"); -} - -void PcapLiveDevice::statsThreadMain() -{ - PCPP_LOG_DEBUG("Started stats thread for device '" << m_Name << "'"); - while (!m_StopThread) - { - PcapStats stats; - getStatistics(stats); - m_cbOnStatsUpdate(stats, m_cbOnStatsUpdateUserCookie); - multiPlatformSleep(m_IntervalToUpdateStats); - } - PCPP_LOG_DEBUG("Ended stats thread for device '" << m_Name << "'"); -} - -pcap_t* PcapLiveDevice::doOpen(const DeviceConfiguration& config) -{ - char errbuf[PCAP_ERRBUF_SIZE] = {'\0'}; - std::string device_name = m_Name; - - if (device_name == NFLOG_IFACE) - { - device_name += ":" + std::to_string(config.nflogGroup & 0xffff); - } - - pcap_t* pcap = pcap_create(device_name.c_str(), errbuf); - if (!pcap) - { - PCPP_LOG_ERROR(errbuf); - return pcap; - } - int ret = pcap_set_snaplen(pcap, config.snapshotLength <= 0 ? DEFAULT_SNAPLEN : config.snapshotLength); - if (ret != 0) - { - PCPP_LOG_ERROR(pcap_geterr(pcap)); - } - ret = pcap_set_promisc(pcap, config.mode); - if (ret != 0) - { - PCPP_LOG_ERROR(pcap_geterr(pcap)); - } - - int timeout = (config.packetBufferTimeoutMs <= 0 ? LIBPCAP_OPEN_LIVE_TIMEOUT : config.packetBufferTimeoutMs); - ret = pcap_set_timeout(pcap, timeout); - if (ret != 0) - { - PCPP_LOG_ERROR(pcap_geterr(pcap)); - } - - if (config.packetBufferSize >= 100) - { - ret = pcap_set_buffer_size(pcap, config.packetBufferSize); - if (ret != 0) - { - PCPP_LOG_ERROR(pcap_geterr(pcap)); - } - } +PcapLiveDevice::PcapLiveDevice(pcap_if_t* pInterface, bool calculateMTU, + bool calculateMacAddress, + bool calculateDefaultGateway) + : IPcapDevice(), m_MacAddress(""), m_DefaultGateway(IPv4Address::Zero) { + m_DeviceMtu = 0; + m_LinkType = LINKTYPE_ETHERNET; + + m_IsLoopback = (pInterface->flags & 0x1) == PCAP_IF_LOOPBACK; + + m_Name = pInterface->name; + if (pInterface->description != nullptr) + m_Description = pInterface->description; + PCPP_LOG_DEBUG("Added live device: name=" << m_Name + << "; desc=" << m_Description); + PCPP_LOG_DEBUG(" Addresses:"); + while (pInterface->addresses != nullptr) { + m_Addresses.insert(m_Addresses.end(), *(pInterface->addresses)); + pInterface->addresses = pInterface->addresses->next; + if (Logger::getInstance().isDebugEnabled(PcapLogModuleLiveDevice) && + pInterface->addresses != nullptr && + pInterface->addresses->addr != nullptr) { + char addrAsString[INET6_ADDRSTRLEN]; + internal::sockaddr2string(pInterface->addresses->addr, addrAsString); + PCPP_LOG_DEBUG(" " << addrAsString); + } + } + + if (calculateMTU) { + setDeviceMtu(); + PCPP_LOG_DEBUG(" MTU: " << m_DeviceMtu); + } + + if (calculateDefaultGateway) { + setDefaultGateway(); + PCPP_LOG_DEBUG(" Default Gateway: " << m_DefaultGateway); + } + + // init all other members + m_CaptureThreadStarted = false; + m_StatsThreadStarted = false; + m_StopThread = false; + m_CaptureThread = {}; + m_StatsThread = {}; + m_cbOnPacketArrives = nullptr; + m_cbOnStatsUpdate = nullptr; + m_cbOnPacketArrivesBlockingMode = nullptr; + m_cbOnPacketArrivesBlockingModeUserCookie = nullptr; + m_IntervalToUpdateStats = 0; + m_cbOnPacketArrivesUserCookie = nullptr; + m_cbOnStatsUpdateUserCookie = nullptr; + m_CaptureCallbackMode = true; + m_CapturedPackets = nullptr; + if (calculateMacAddress) { + setDeviceMacAddress(); + if (m_MacAddress.isValid()) + PCPP_LOG_DEBUG(" MAC addr: " << m_MacAddress); + } +} + +void PcapLiveDevice::onPacketArrives(uint8_t* user, + const struct pcap_pkthdr* pkthdr, + const uint8_t* packet) { + PcapLiveDevice* pThis = (PcapLiveDevice*)user; + if (pThis == nullptr) { + PCPP_LOG_ERROR("Unable to extract PcapLiveDevice instance"); + return; + } + + RawPacket rawPacket(packet, pkthdr->caplen, pkthdr->ts, false, + pThis->getLinkType()); + + if (pThis->m_cbOnPacketArrives != nullptr) + pThis->m_cbOnPacketArrives(&rawPacket, pThis, + pThis->m_cbOnPacketArrivesUserCookie); +} + +void PcapLiveDevice::onPacketArrivesNoCallback(uint8_t* user, + const struct pcap_pkthdr* pkthdr, + const uint8_t* packet) { + PcapLiveDevice* pThis = (PcapLiveDevice*)user; + if (pThis == nullptr) { + PCPP_LOG_ERROR("Unable to extract PcapLiveDevice instance"); + return; + } + + uint8_t* packetData = new uint8_t[pkthdr->caplen]; + memcpy(packetData, packet, pkthdr->caplen); + RawPacket* rawPacketPtr = new RawPacket( + packetData, pkthdr->caplen, pkthdr->ts, true, pThis->getLinkType()); + pThis->m_CapturedPackets->pushBack(rawPacketPtr); +} + +void PcapLiveDevice::onPacketArrivesBlockingMode( + uint8_t* user, const struct pcap_pkthdr* pkthdr, const uint8_t* packet) { + PcapLiveDevice* pThis = (PcapLiveDevice*)user; + if (pThis == nullptr) { + PCPP_LOG_ERROR("Unable to extract PcapLiveDevice instance"); + return; + } + + RawPacket rawPacket(packet, pkthdr->caplen, pkthdr->ts, false, + pThis->getLinkType()); + + if (pThis->m_cbOnPacketArrivesBlockingMode != nullptr) + if (pThis->m_cbOnPacketArrivesBlockingMode( + &rawPacket, pThis, + pThis->m_cbOnPacketArrivesBlockingModeUserCookie)) + pThis->m_StopThread = true; +} + +void PcapLiveDevice::captureThreadMain() { + PCPP_LOG_DEBUG("Started capture thread for device '" << m_Name << "'"); + if (m_CaptureCallbackMode) { + while (!m_StopThread) { + if (pcap_dispatch(m_PcapDescriptor, -1, onPacketArrives, + reinterpret_cast(this)) == -1) { + PCPP_LOG_ERROR("pcap_dispatch returned an error: " + << pcap_geterr(m_PcapDescriptor)); + m_StopThread = true; + } + } + } else { + while (!m_StopThread) { + if (pcap_dispatch(m_PcapDescriptor, 100, onPacketArrivesNoCallback, + reinterpret_cast(this)) == -1) { + PCPP_LOG_ERROR("pcap_dispatch returned an error: " + << pcap_geterr(m_PcapDescriptor)); + m_StopThread = true; + } + } + } + PCPP_LOG_DEBUG("Ended capture thread for device '" << m_Name << "'"); +} + +void PcapLiveDevice::statsThreadMain() { + PCPP_LOG_DEBUG("Started stats thread for device '" << m_Name << "'"); + while (!m_StopThread) { + PcapStats stats; + getStatistics(stats); + m_cbOnStatsUpdate(stats, m_cbOnStatsUpdateUserCookie); + multiPlatformSleep(m_IntervalToUpdateStats); + } + PCPP_LOG_DEBUG("Ended stats thread for device '" << m_Name << "'"); +} + +pcap_t* PcapLiveDevice::doOpen(const DeviceConfiguration& config) { + char errbuf[PCAP_ERRBUF_SIZE] = {'\0'}; + std::string device_name = m_Name; + + if (device_name == NFLOG_IFACE) { + device_name += ":" + std::to_string(config.nflogGroup & 0xffff); + } + + pcap_t* pcap = pcap_create(device_name.c_str(), errbuf); + if (!pcap) { + PCPP_LOG_ERROR(errbuf); + return pcap; + } + int ret = pcap_set_snaplen(pcap, config.snapshotLength <= 0 + ? DEFAULT_SNAPLEN + : config.snapshotLength); + if (ret != 0) { + PCPP_LOG_ERROR(pcap_geterr(pcap)); + } + ret = pcap_set_promisc(pcap, config.mode); + if (ret != 0) { + PCPP_LOG_ERROR(pcap_geterr(pcap)); + } + + int timeout = + (config.packetBufferTimeoutMs <= 0 ? LIBPCAP_OPEN_LIVE_TIMEOUT + : config.packetBufferTimeoutMs); + ret = pcap_set_timeout(pcap, timeout); + if (ret != 0) { + PCPP_LOG_ERROR(pcap_geterr(pcap)); + } + + if (config.packetBufferSize >= 100) { + ret = pcap_set_buffer_size(pcap, config.packetBufferSize); + if (ret != 0) { + PCPP_LOG_ERROR(pcap_geterr(pcap)); + } + } #ifdef HAS_PCAP_IMMEDIATE_MODE - ret = pcap_set_immediate_mode(pcap, 1); - if (ret == 0) - { - PCPP_LOG_DEBUG("Immediate mode is activated"); - } - else - { - PCPP_LOG_ERROR("Failed to activate immediate mode, error code: '" << ret << "', error message: '" << pcap_geterr(pcap) << "'"); - } + ret = pcap_set_immediate_mode(pcap, 1); + if (ret == 0) { + PCPP_LOG_DEBUG("Immediate mode is activated"); + } else { + PCPP_LOG_ERROR("Failed to activate immediate mode, error code: '" + << ret << "', error message: '" << pcap_geterr(pcap) << "'"); + } #endif - ret = pcap_activate(pcap); - if (ret != 0) - { - PCPP_LOG_ERROR(pcap_geterr(pcap)); - pcap_close(pcap); - return nullptr; - } + ret = pcap_activate(pcap); + if (ret != 0) { + PCPP_LOG_ERROR(pcap_geterr(pcap)); + pcap_close(pcap); + return nullptr; + } #ifdef HAS_SET_DIRECTION_ENABLED - pcap_direction_t directionToSet = directionTypeMap(config.direction); - ret = pcap_setdirection(pcap, directionToSet); - if (ret == 0) - { - if (config.direction == PCPP_IN) - { - PCPP_LOG_DEBUG("Only incoming traffics will be captured"); - } - else if (config.direction == PCPP_OUT) - { - PCPP_LOG_DEBUG("Only outgoing traffics will be captured"); - } - else - { - PCPP_LOG_DEBUG("Both incoming and outgoing traffics will be captured"); - } - } - else - { - PCPP_LOG_ERROR("Failed to set direction for capturing packets, error code: '" << ret << "', error message: '" << pcap_geterr(pcap) << "'"); - } + pcap_direction_t directionToSet = directionTypeMap(config.direction); + ret = pcap_setdirection(pcap, directionToSet); + if (ret == 0) { + if (config.direction == PCPP_IN) { + PCPP_LOG_DEBUG("Only incoming traffics will be captured"); + } else if (config.direction == PCPP_OUT) { + PCPP_LOG_DEBUG("Only outgoing traffics will be captured"); + } else { + PCPP_LOG_DEBUG("Both incoming and outgoing traffics will be captured"); + } + } else { + PCPP_LOG_ERROR( + "Failed to set direction for capturing packets, error code: '" + << ret << "', error message: '" << pcap_geterr(pcap) << "'"); + } #endif - if (pcap) - { - int dlt = pcap_datalink(pcap); - const char* dlt_name = pcap_datalink_val_to_name(dlt); - if (dlt_name) - { - PCPP_LOG_DEBUG("link-type " << dlt << ": " << dlt_name << " (" << pcap_datalink_val_to_description(dlt) << ")"); - } - else - { - PCPP_LOG_DEBUG("link-type " << dlt); - } - - m_LinkType = static_cast(dlt); - } - return pcap; -} - -bool PcapLiveDevice::open(const DeviceConfiguration& config) -{ - if (m_DeviceOpened) - { - PCPP_LOG_DEBUG("Device '" << m_Name << "' already opened"); - return true; - } - - m_PcapDescriptor = doOpen(config); - - // It's not possible to have two open instances of the same NFLOG device:group - if (m_Name == NFLOG_IFACE) - { - m_PcapSendDescriptor = nullptr; - } - else - { - m_PcapSendDescriptor = doOpen(config); - } - - if (m_PcapDescriptor == nullptr || (m_Name != NFLOG_IFACE && m_PcapSendDescriptor == nullptr)) - { - m_DeviceOpened = false; - return false; - } - - PCPP_LOG_DEBUG("Device '" << m_Name << "' opened"); - - m_DeviceOpened = true; - - return true; -} - -bool PcapLiveDevice::open() -{ - DeviceConfiguration defaultConfig; - return open(defaultConfig); -} - -void PcapLiveDevice::close() -{ - if (m_PcapDescriptor == nullptr && m_PcapSendDescriptor == nullptr) - { - PCPP_LOG_DEBUG("Device '" << m_Name << "' already closed"); - return; - } - - bool sameDescriptor = (m_PcapDescriptor == m_PcapSendDescriptor); - pcap_close(m_PcapDescriptor); - PCPP_LOG_DEBUG("Receive pcap descriptor closed"); - if (!sameDescriptor) - { - pcap_close(m_PcapSendDescriptor); - PCPP_LOG_DEBUG("Send pcap descriptor closed"); - } - - m_DeviceOpened = false; - PCPP_LOG_DEBUG("Device '" << m_Name << "' closed"); -} - -PcapLiveDevice* PcapLiveDevice::clone() -{ - PcapLiveDevice *retval = nullptr; - - pcap_if_t *interfaceList; - char errbuf[PCAP_ERRBUF_SIZE]; - int err = pcap_findalldevs(&interfaceList, errbuf); - if (err < 0) - { - PCPP_LOG_ERROR("Error searching for devices: " << errbuf); - return nullptr; - } - - pcap_if_t* currInterface = interfaceList; - while (currInterface != nullptr) - { - if(!strcmp(currInterface->name, getName().c_str())) - break; - currInterface = currInterface->next; - } - - if(currInterface) - retval = new PcapLiveDevice(currInterface, true, true, true); - else - PCPP_LOG_ERROR("Can't find interface " << getName().c_str()); - - pcap_freealldevs(interfaceList); - return retval; -} - -bool PcapLiveDevice::startCapture(OnPacketArrivesCallback onPacketArrives, void* onPacketArrivesUserCookie) -{ - return startCapture(onPacketArrives, onPacketArrivesUserCookie, 0, nullptr, nullptr); -} - -bool PcapLiveDevice::startCapture(int intervalInSecondsToUpdateStats, OnStatsUpdateCallback onStatsUpdate, void* onStatsUpdateUserCookie) -{ - return startCapture(nullptr, nullptr, intervalInSecondsToUpdateStats, onStatsUpdate, onStatsUpdateUserCookie); -} - -bool PcapLiveDevice::startCapture(OnPacketArrivesCallback onPacketArrives, void* onPacketArrivesUserCookie, int intervalInSecondsToUpdateStats, OnStatsUpdateCallback onStatsUpdate, void* onStatsUpdateUserCookie) -{ - if (!m_DeviceOpened || m_PcapDescriptor == nullptr) - { - PCPP_LOG_ERROR("Device '" << m_Name << "' not opened"); - return false; - } - - if (m_CaptureThreadStarted) - { - PCPP_LOG_ERROR("Device '" << m_Name << "' already capturing traffic"); - return false; - } - - m_IntervalToUpdateStats = intervalInSecondsToUpdateStats; - - m_CaptureCallbackMode = true; - m_cbOnPacketArrives = onPacketArrives; - m_cbOnPacketArrivesUserCookie = onPacketArrivesUserCookie; - - m_CaptureThread = std::thread(&pcpp::PcapLiveDevice::captureThreadMain, this); - m_CaptureThreadStarted = true; - PCPP_LOG_DEBUG("Successfully created capture thread for device '" << m_Name << "'. Thread id: " << m_CaptureThread.get_id()); - - if (onStatsUpdate != nullptr && intervalInSecondsToUpdateStats > 0) - { - m_cbOnStatsUpdate = onStatsUpdate; - m_cbOnStatsUpdateUserCookie = onStatsUpdateUserCookie; - m_StatsThread = std::thread(&pcpp::PcapLiveDevice::statsThreadMain, this); - m_StatsThreadStarted = true; - PCPP_LOG_DEBUG("Successfully created stats thread for device '" << m_Name << "'. Thread id: " << m_StatsThread.get_id()); - } - - return true; -} - -bool PcapLiveDevice::startCapture(RawPacketVector& capturedPacketsVector) -{ - if (!m_DeviceOpened || m_PcapDescriptor == nullptr) - { - PCPP_LOG_ERROR("Device '" << m_Name << "' not opened"); - return false; - } - - if (m_CaptureThreadStarted) - { - PCPP_LOG_ERROR("Device '" << m_Name << "' already capturing traffic"); - return false; - } - - m_CapturedPackets = &capturedPacketsVector; - m_CapturedPackets->clear(); - - m_CaptureCallbackMode = false; - m_CaptureThread = std::thread(&pcpp::PcapLiveDevice::captureThreadMain, this); - m_CaptureThreadStarted = true; - PCPP_LOG_DEBUG("Successfully created capture thread for device '" << m_Name << "'. Thread id: " << m_CaptureThread.get_id()); - - return true; -} - - -int PcapLiveDevice::startCaptureBlockingMode(OnPacketArrivesStopBlocking onPacketArrives, void* userCookie, int timeout) -{ - if (!m_DeviceOpened || m_PcapDescriptor == nullptr) - { - PCPP_LOG_ERROR("Device '" << m_Name << "' not opened"); - return 0; - } - - if (m_CaptureThreadStarted) - { - PCPP_LOG_ERROR("Device '" << m_Name << "' already capturing traffic"); - return 0; - } - - m_cbOnPacketArrives = nullptr; - m_cbOnStatsUpdate = nullptr; - m_cbOnPacketArrivesUserCookie = nullptr; - m_cbOnStatsUpdateUserCookie = nullptr; - - m_cbOnPacketArrivesBlockingMode = onPacketArrives; - m_cbOnPacketArrivesBlockingModeUserCookie = userCookie; - - long startTimeSec = 0, startTimeNSec = 0; - clockGetTime(startTimeSec, startTimeNSec); - - long curTimeSec = 0; - - m_CaptureThreadStarted = true; - m_StopThread = false; - - bool pcapDispatchError = false; - - if (timeout <= 0) - { - while (!m_StopThread) - { - if (pcap_dispatch(m_PcapDescriptor, -1, onPacketArrivesBlockingMode, reinterpret_cast(this)) == -1) - { - PCPP_LOG_ERROR("pcap_dispatch returned an error: " << pcap_geterr(m_PcapDescriptor)); - pcapDispatchError = true; - m_StopThread = true; - } - } - curTimeSec = startTimeSec + timeout; - } - else - { - while (!m_StopThread && curTimeSec <= (startTimeSec + timeout)) - { - long curTimeNSec = 0; - if (pcap_dispatch(m_PcapDescriptor, -1, onPacketArrivesBlockingMode, reinterpret_cast(this)) == -1) - { - PCPP_LOG_ERROR("pcap_dispatch returned an error: " << pcap_geterr(m_PcapDescriptor)); - pcapDispatchError = true; - m_StopThread = true; - } - clockGetTime(curTimeSec, curTimeNSec); - } - } - - m_CaptureThreadStarted = false; - - m_StopThread = false; - - m_cbOnPacketArrivesBlockingMode = nullptr; - m_cbOnPacketArrivesBlockingModeUserCookie = nullptr; - - if (pcapDispatchError) - { - return 0; - } - - if (curTimeSec > (startTimeSec + timeout)) - return -1; - return 1; -} - -void PcapLiveDevice::stopCapture() -{ - // in blocking mode stop capture isn't relevant - if (m_cbOnPacketArrivesBlockingMode != nullptr) - return; - - m_StopThread = true; - if (m_CaptureThreadStarted) - { - pcap_breakloop(m_PcapDescriptor); - PCPP_LOG_DEBUG("Stopping capture thread, waiting for it to join..."); - m_CaptureThread.join(); - m_CaptureThreadStarted = false; - PCPP_LOG_DEBUG("Capture thread stopped for device '" << m_Name << "'"); - } - PCPP_LOG_DEBUG("Capture thread stopped for device '" << m_Name << "'"); - if (m_StatsThreadStarted) - { - PCPP_LOG_DEBUG("Stopping stats thread, waiting for it to join..."); - m_StatsThread.join(); - m_StatsThreadStarted = false; - PCPP_LOG_DEBUG("Stats thread stopped for device '" << m_Name << "'"); - } - - m_StopThread = false; -} - -bool PcapLiveDevice::captureActive() -{ - return m_CaptureThreadStarted; -} - -void PcapLiveDevice::getStatistics(PcapStats& stats) const -{ - pcap_stat pcapStats; - if (pcap_stats(m_PcapDescriptor, &pcapStats) < 0) - { - PCPP_LOG_ERROR("Error getting statistics from live device '" << m_Name << "'"); - } - - stats.packetsRecv = pcapStats.ps_recv; - stats.packetsDrop = pcapStats.ps_drop; - stats.packetsDropByInterface = pcapStats.ps_ifdrop; -} + if (pcap) { + int dlt = pcap_datalink(pcap); + const char* dlt_name = pcap_datalink_val_to_name(dlt); + if (dlt_name) { + PCPP_LOG_DEBUG("link-type " << dlt << ": " << dlt_name << " (" + << pcap_datalink_val_to_description(dlt) + << ")"); + } else { + PCPP_LOG_DEBUG("link-type " << dlt); + } -bool PcapLiveDevice::doMtuCheck(int packetPayloadLength) -{ - if (packetPayloadLength > (int)m_DeviceMtu) - { - PCPP_LOG_ERROR("Payload length [" << packetPayloadLength << "] is larger than device MTU [" << m_DeviceMtu << "]"); - return false; - } - return true; -} + m_LinkType = static_cast(dlt); + } + return pcap; +} + +bool PcapLiveDevice::open(const DeviceConfiguration& config) { + if (m_DeviceOpened) { + PCPP_LOG_DEBUG("Device '" << m_Name << "' already opened"); + return true; + } -bool PcapLiveDevice::sendPacket(RawPacket const& rawPacket, bool checkMtu) -{ - if (checkMtu) - { - RawPacket *rPacket = (RawPacket *)&rawPacket; - Packet parsedPacket = Packet(rPacket, OsiModelDataLinkLayer); - return sendPacket(&parsedPacket, true); - } - // Send packet without Mtu check - return sendPacket(((RawPacket&)rawPacket).getRawData(), ((RawPacket&)rawPacket).getRawDataLen()); -} + m_PcapDescriptor = doOpen(config); -bool PcapLiveDevice::sendPacket(const uint8_t* packetData, int packetDataLength, int packetPayloadLength) -{ - return doMtuCheck(packetPayloadLength) && sendPacket(packetData, packetDataLength); -} + // It's not possible to have two open instances of the same NFLOG device:group + if (m_Name == NFLOG_IFACE) { + m_PcapSendDescriptor = nullptr; + } else { + m_PcapSendDescriptor = doOpen(config); + } + + if (m_PcapDescriptor == nullptr || + (m_Name != NFLOG_IFACE && m_PcapSendDescriptor == nullptr)) { + m_DeviceOpened = false; + return false; + } + + PCPP_LOG_DEBUG("Device '" << m_Name << "' opened"); + + m_DeviceOpened = true; + + return true; +} + +bool PcapLiveDevice::open() { + DeviceConfiguration defaultConfig; + return open(defaultConfig); +} + +void PcapLiveDevice::close() { + if (m_PcapDescriptor == nullptr && m_PcapSendDescriptor == nullptr) { + PCPP_LOG_DEBUG("Device '" << m_Name << "' already closed"); + return; + } + + bool sameDescriptor = (m_PcapDescriptor == m_PcapSendDescriptor); + pcap_close(m_PcapDescriptor); + PCPP_LOG_DEBUG("Receive pcap descriptor closed"); + if (!sameDescriptor) { + pcap_close(m_PcapSendDescriptor); + PCPP_LOG_DEBUG("Send pcap descriptor closed"); + } + + m_DeviceOpened = false; + PCPP_LOG_DEBUG("Device '" << m_Name << "' closed"); +} + +PcapLiveDevice* PcapLiveDevice::clone() { + PcapLiveDevice* retval = nullptr; -bool PcapLiveDevice::sendPacket(const uint8_t* packetData, int packetDataLength, bool checkMtu, pcpp::LinkLayerType linkType) -{ - if (checkMtu) - { - timeval time; - gettimeofday(&time, nullptr); - pcpp::RawPacket rawPacket(packetData, packetDataLength, time, false, linkType); - Packet parsedPacket = Packet(&rawPacket, pcpp::OsiModelDataLinkLayer); - return sendPacket(&parsedPacket, true); - } - - if (!m_DeviceOpened) - { - PCPP_LOG_ERROR("Device '" << m_Name << "' not opened!"); - return false; - } - - if (packetDataLength == 0) - { - PCPP_LOG_ERROR("Trying to send a packet with length 0"); - return false; - } - - if (pcap_sendpacket(m_PcapSendDescriptor, packetData, packetDataLength) == -1) - { - PCPP_LOG_ERROR("Error sending packet: " << pcap_geterr(m_PcapSendDescriptor)); - return false; - } - - PCPP_LOG_DEBUG("Packet sent successfully. Packet length: " << packetDataLength); - return true; -} + pcap_if_t* interfaceList; + char errbuf[PCAP_ERRBUF_SIZE]; + int err = pcap_findalldevs(&interfaceList, errbuf); + if (err < 0) { + PCPP_LOG_ERROR("Error searching for devices: " << errbuf); + return nullptr; + } + + pcap_if_t* currInterface = interfaceList; + while (currInterface != nullptr) { + if (!strcmp(currInterface->name, getName().c_str())) + break; + currInterface = currInterface->next; + } + + if (currInterface) + retval = new PcapLiveDevice(currInterface, true, true, true); + else + PCPP_LOG_ERROR("Can't find interface " << getName().c_str()); + + pcap_freealldevs(interfaceList); + return retval; +} -bool PcapLiveDevice::sendPacket(Packet* packet, bool checkMtu) -{ - RawPacket* rawPacket = packet->getRawPacket(); - if (checkMtu) - { - int packetPayloadLength = 0; - switch (packet->getFirstLayer()->getOsiModelLayer()) - { - case (pcpp::OsiModelDataLinkLayer): - packetPayloadLength = (int)packet->getFirstLayer()->getLayerPayloadSize(); - break; - case (pcpp::OsiModelNetworkLayer): - packetPayloadLength = (int)packet->getFirstLayer()->getDataLen(); - break; - default: - // if packet layer is not known, do not perform MTU check. - return sendPacket(*rawPacket, false); - } - return doMtuCheck(packetPayloadLength) && sendPacket(*rawPacket, false); - } - return sendPacket(*rawPacket, false); +bool PcapLiveDevice::startCapture(OnPacketArrivesCallback onPacketArrives, + void* onPacketArrivesUserCookie) { + return startCapture(onPacketArrives, onPacketArrivesUserCookie, 0, nullptr, + nullptr); } -int PcapLiveDevice::sendPackets(RawPacket* rawPacketsArr, int arrLength, bool checkMtu) -{ - int packetsSent = 0; - for (int i = 0; i < arrLength; i++) - { - if (sendPacket(rawPacketsArr[i], checkMtu)) - packetsSent++; - } - - PCPP_LOG_DEBUG(packetsSent << " packets sent successfully. " << arrLength-packetsSent << " packets not sent"); - return packetsSent; -} - -int PcapLiveDevice::sendPackets(Packet** packetsArr, int arrLength, bool checkMtu) -{ - int packetsSent = 0; - for (int i = 0; i < arrLength; i++) - { - if (sendPacket(packetsArr[i], checkMtu)) - packetsSent++; - } - - PCPP_LOG_DEBUG(packetsSent << " packets sent successfully. " << arrLength-packetsSent << " packets not sent"); - return packetsSent; -} - -int PcapLiveDevice::sendPackets(const RawPacketVector& rawPackets, bool checkMtu) -{ - int packetsSent = 0; - for (RawPacketVector::ConstVectorIterator iter = rawPackets.begin(); iter != rawPackets.end(); iter++) - { - if (sendPacket(**iter, checkMtu)) - packetsSent++; - } - - PCPP_LOG_DEBUG(packetsSent << " packets sent successfully. " << (rawPackets.size()-packetsSent) << " packets not sent"); - return packetsSent; -} - -void PcapLiveDevice::setDeviceMtu() -{ +bool PcapLiveDevice::startCapture(int intervalInSecondsToUpdateStats, + OnStatsUpdateCallback onStatsUpdate, + void* onStatsUpdateUserCookie) { + return startCapture(nullptr, nullptr, intervalInSecondsToUpdateStats, + onStatsUpdate, onStatsUpdateUserCookie); +} + +bool PcapLiveDevice::startCapture(OnPacketArrivesCallback onPacketArrives, + void* onPacketArrivesUserCookie, + int intervalInSecondsToUpdateStats, + OnStatsUpdateCallback onStatsUpdate, + void* onStatsUpdateUserCookie) { + if (!m_DeviceOpened || m_PcapDescriptor == nullptr) { + PCPP_LOG_ERROR("Device '" << m_Name << "' not opened"); + return false; + } + + if (m_CaptureThreadStarted) { + PCPP_LOG_ERROR("Device '" << m_Name << "' already capturing traffic"); + return false; + } + + m_IntervalToUpdateStats = intervalInSecondsToUpdateStats; + + m_CaptureCallbackMode = true; + m_cbOnPacketArrives = onPacketArrives; + m_cbOnPacketArrivesUserCookie = onPacketArrivesUserCookie; + + m_CaptureThread = std::thread(&pcpp::PcapLiveDevice::captureThreadMain, this); + m_CaptureThreadStarted = true; + PCPP_LOG_DEBUG("Successfully created capture thread for device '" + << m_Name << "'. Thread id: " << m_CaptureThread.get_id()); + + if (onStatsUpdate != nullptr && intervalInSecondsToUpdateStats > 0) { + m_cbOnStatsUpdate = onStatsUpdate; + m_cbOnStatsUpdateUserCookie = onStatsUpdateUserCookie; + m_StatsThread = std::thread(&pcpp::PcapLiveDevice::statsThreadMain, this); + m_StatsThreadStarted = true; + PCPP_LOG_DEBUG("Successfully created stats thread for device '" + << m_Name << "'. Thread id: " << m_StatsThread.get_id()); + } + + return true; +} + +bool PcapLiveDevice::startCapture(RawPacketVector& capturedPacketsVector) { + if (!m_DeviceOpened || m_PcapDescriptor == nullptr) { + PCPP_LOG_ERROR("Device '" << m_Name << "' not opened"); + return false; + } + + if (m_CaptureThreadStarted) { + PCPP_LOG_ERROR("Device '" << m_Name << "' already capturing traffic"); + return false; + } + + m_CapturedPackets = &capturedPacketsVector; + m_CapturedPackets->clear(); + + m_CaptureCallbackMode = false; + m_CaptureThread = std::thread(&pcpp::PcapLiveDevice::captureThreadMain, this); + m_CaptureThreadStarted = true; + PCPP_LOG_DEBUG("Successfully created capture thread for device '" + << m_Name << "'. Thread id: " << m_CaptureThread.get_id()); + + return true; +} + +int PcapLiveDevice::startCaptureBlockingMode( + OnPacketArrivesStopBlocking onPacketArrives, void* userCookie, + int timeout) { + if (!m_DeviceOpened || m_PcapDescriptor == nullptr) { + PCPP_LOG_ERROR("Device '" << m_Name << "' not opened"); + return 0; + } + + if (m_CaptureThreadStarted) { + PCPP_LOG_ERROR("Device '" << m_Name << "' already capturing traffic"); + return 0; + } + + m_cbOnPacketArrives = nullptr; + m_cbOnStatsUpdate = nullptr; + m_cbOnPacketArrivesUserCookie = nullptr; + m_cbOnStatsUpdateUserCookie = nullptr; + + m_cbOnPacketArrivesBlockingMode = onPacketArrives; + m_cbOnPacketArrivesBlockingModeUserCookie = userCookie; + + long startTimeSec = 0, startTimeNSec = 0; + clockGetTime(startTimeSec, startTimeNSec); + + long curTimeSec = 0; + + m_CaptureThreadStarted = true; + m_StopThread = false; + + bool pcapDispatchError = false; + + if (timeout <= 0) { + while (!m_StopThread) { + if (pcap_dispatch(m_PcapDescriptor, -1, onPacketArrivesBlockingMode, + reinterpret_cast(this)) == -1) { + PCPP_LOG_ERROR("pcap_dispatch returned an error: " + << pcap_geterr(m_PcapDescriptor)); + pcapDispatchError = true; + m_StopThread = true; + } + } + curTimeSec = startTimeSec + timeout; + } else { + while (!m_StopThread && curTimeSec <= (startTimeSec + timeout)) { + long curTimeNSec = 0; + if (pcap_dispatch(m_PcapDescriptor, -1, onPacketArrivesBlockingMode, + reinterpret_cast(this)) == -1) { + PCPP_LOG_ERROR("pcap_dispatch returned an error: " + << pcap_geterr(m_PcapDescriptor)); + pcapDispatchError = true; + m_StopThread = true; + } + clockGetTime(curTimeSec, curTimeNSec); + } + } + + m_CaptureThreadStarted = false; + + m_StopThread = false; + + m_cbOnPacketArrivesBlockingMode = nullptr; + m_cbOnPacketArrivesBlockingModeUserCookie = nullptr; + + if (pcapDispatchError) { + return 0; + } + + if (curTimeSec > (startTimeSec + timeout)) + return -1; + return 1; +} + +void PcapLiveDevice::stopCapture() { + // in blocking mode stop capture isn't relevant + if (m_cbOnPacketArrivesBlockingMode != nullptr) + return; + + m_StopThread = true; + if (m_CaptureThreadStarted) { + pcap_breakloop(m_PcapDescriptor); + PCPP_LOG_DEBUG("Stopping capture thread, waiting for it to join..."); + m_CaptureThread.join(); + m_CaptureThreadStarted = false; + PCPP_LOG_DEBUG("Capture thread stopped for device '" << m_Name << "'"); + } + PCPP_LOG_DEBUG("Capture thread stopped for device '" << m_Name << "'"); + if (m_StatsThreadStarted) { + PCPP_LOG_DEBUG("Stopping stats thread, waiting for it to join..."); + m_StatsThread.join(); + m_StatsThreadStarted = false; + PCPP_LOG_DEBUG("Stats thread stopped for device '" << m_Name << "'"); + } + + m_StopThread = false; +} + +bool PcapLiveDevice::captureActive() { return m_CaptureThreadStarted; } + +void PcapLiveDevice::getStatistics(PcapStats& stats) const { + pcap_stat pcapStats; + if (pcap_stats(m_PcapDescriptor, &pcapStats) < 0) { + PCPP_LOG_ERROR("Error getting statistics from live device '" << m_Name + << "'"); + } + + stats.packetsRecv = pcapStats.ps_recv; + stats.packetsDrop = pcapStats.ps_drop; + stats.packetsDropByInterface = pcapStats.ps_ifdrop; +} + +bool PcapLiveDevice::doMtuCheck(int packetPayloadLength) { + if (packetPayloadLength > (int)m_DeviceMtu) { + PCPP_LOG_ERROR("Payload length [" << packetPayloadLength + << "] is larger than device MTU [" + << m_DeviceMtu << "]"); + return false; + } + return true; +} + +bool PcapLiveDevice::sendPacket(RawPacket const& rawPacket, bool checkMtu) { + if (checkMtu) { + RawPacket* rPacket = (RawPacket*)&rawPacket; + Packet parsedPacket = Packet(rPacket, OsiModelDataLinkLayer); + return sendPacket(&parsedPacket, true); + } + // Send packet without Mtu check + return sendPacket(((RawPacket&)rawPacket).getRawData(), + ((RawPacket&)rawPacket).getRawDataLen()); +} + +bool PcapLiveDevice::sendPacket(const uint8_t* packetData, int packetDataLength, + int packetPayloadLength) { + return doMtuCheck(packetPayloadLength) && + sendPacket(packetData, packetDataLength); +} + +bool PcapLiveDevice::sendPacket(const uint8_t* packetData, int packetDataLength, + bool checkMtu, pcpp::LinkLayerType linkType) { + if (checkMtu) { + timeval time; + gettimeofday(&time, nullptr); + pcpp::RawPacket rawPacket(packetData, packetDataLength, time, false, + linkType); + Packet parsedPacket = Packet(&rawPacket, pcpp::OsiModelDataLinkLayer); + return sendPacket(&parsedPacket, true); + } + + if (!m_DeviceOpened) { + PCPP_LOG_ERROR("Device '" << m_Name << "' not opened!"); + return false; + } + + if (packetDataLength == 0) { + PCPP_LOG_ERROR("Trying to send a packet with length 0"); + return false; + } + + if (pcap_sendpacket(m_PcapSendDescriptor, packetData, packetDataLength) == + -1) { + PCPP_LOG_ERROR( + "Error sending packet: " << pcap_geterr(m_PcapSendDescriptor)); + return false; + } + + PCPP_LOG_DEBUG( + "Packet sent successfully. Packet length: " << packetDataLength); + return true; +} + +bool PcapLiveDevice::sendPacket(Packet* packet, bool checkMtu) { + RawPacket* rawPacket = packet->getRawPacket(); + if (checkMtu) { + int packetPayloadLength = 0; + switch (packet->getFirstLayer()->getOsiModelLayer()) { + case (pcpp::OsiModelDataLinkLayer): + packetPayloadLength = (int)packet->getFirstLayer()->getLayerPayloadSize(); + break; + case (pcpp::OsiModelNetworkLayer): + packetPayloadLength = (int)packet->getFirstLayer()->getDataLen(); + break; + default: + // if packet layer is not known, do not perform MTU check. + return sendPacket(*rawPacket, false); + } + return doMtuCheck(packetPayloadLength) && sendPacket(*rawPacket, false); + } + return sendPacket(*rawPacket, false); +} + +int PcapLiveDevice::sendPackets(RawPacket* rawPacketsArr, int arrLength, + bool checkMtu) { + int packetsSent = 0; + for (int i = 0; i < arrLength; i++) { + if (sendPacket(rawPacketsArr[i], checkMtu)) + packetsSent++; + } + + PCPP_LOG_DEBUG(packetsSent << " packets sent successfully. " + << arrLength - packetsSent << " packets not sent"); + return packetsSent; +} + +int PcapLiveDevice::sendPackets(Packet** packetsArr, int arrLength, + bool checkMtu) { + int packetsSent = 0; + for (int i = 0; i < arrLength; i++) { + if (sendPacket(packetsArr[i], checkMtu)) + packetsSent++; + } + + PCPP_LOG_DEBUG(packetsSent << " packets sent successfully. " + << arrLength - packetsSent << " packets not sent"); + return packetsSent; +} + +int PcapLiveDevice::sendPackets(const RawPacketVector& rawPackets, + bool checkMtu) { + int packetsSent = 0; + for (RawPacketVector::ConstVectorIterator iter = rawPackets.begin(); + iter != rawPackets.end(); iter++) { + if (sendPacket(**iter, checkMtu)) + packetsSent++; + } + + PCPP_LOG_DEBUG(packetsSent << " packets sent successfully. " + << (rawPackets.size() - packetsSent) + << " packets not sent"); + return packetsSent; +} + +void PcapLiveDevice::setDeviceMtu() { #if defined(_WIN32) - if (m_IsLoopback) - { - PCPP_LOG_DEBUG("Npcap Loopback Adapter - MTU is insignificant, setting MTU to max value (0xffffffff)"); - m_DeviceMtu = 0xffffffff; - return; - } - - uint32_t mtuValue = 0; - LPADAPTER adapter = PacketOpenAdapter((char*)m_Name.c_str()); - if (adapter == NULL) - { - PCPP_LOG_ERROR("Error in retrieving MTU: Adapter is NULL"); - return; - } - - uint8_t buffer[512]; - PACKET_OID_DATA* oidData = (PACKET_OID_DATA*)buffer; - oidData->Oid = OID_GEN_MAXIMUM_TOTAL_SIZE; - oidData->Length = sizeof(uint32_t); - memcpy(oidData->Data, &mtuValue, sizeof(uint32_t)); - if (PacketRequest(adapter, false, oidData)) - { - if (oidData->Length <= sizeof(uint32_t)) - { - /* copy value from driver */ - memcpy(&mtuValue, oidData->Data, oidData->Length); - // Sometimes the query gives a wrong number that includes the link header size - // A very common value is 1514 - if identify this value just reduce to 1500. - // TODO: think of a better way to always get the right value - if (mtuValue == 1514) - { - mtuValue = 1500; - } - m_DeviceMtu = mtuValue; - } - else - { - /* the driver returned a value that is longer than expected (and longer than the given buffer) */ - PCPP_LOG_ERROR("Error in retrieving MTU: Size of Oid larger than uint32_t, OidLen: " << oidData->Length); - return; - } - } - else - { - PCPP_LOG_ERROR("Error in retrieving MTU: PacketRequest failed"); - } + if (m_IsLoopback) { + PCPP_LOG_DEBUG("Npcap Loopback Adapter - MTU is insignificant, setting MTU " + "to max value (0xffffffff)"); + m_DeviceMtu = 0xffffffff; + return; + } + + uint32_t mtuValue = 0; + LPADAPTER adapter = PacketOpenAdapter((char*)m_Name.c_str()); + if (adapter == NULL) { + PCPP_LOG_ERROR("Error in retrieving MTU: Adapter is NULL"); + return; + } + + uint8_t buffer[512]; + PACKET_OID_DATA* oidData = (PACKET_OID_DATA*)buffer; + oidData->Oid = OID_GEN_MAXIMUM_TOTAL_SIZE; + oidData->Length = sizeof(uint32_t); + memcpy(oidData->Data, &mtuValue, sizeof(uint32_t)); + if (PacketRequest(adapter, false, oidData)) { + if (oidData->Length <= sizeof(uint32_t)) { + /* copy value from driver */ + memcpy(&mtuValue, oidData->Data, oidData->Length); + // Sometimes the query gives a wrong number that includes the link header + // size A very common value is 1514 - if identify this value just reduce + // to 1500. + // TODO: think of a better way to always get the right value + if (mtuValue == 1514) { + mtuValue = 1500; + } + m_DeviceMtu = mtuValue; + } else { + /* the driver returned a value that is longer than expected (and longer + * than the given buffer) */ + PCPP_LOG_ERROR( + "Error in retrieving MTU: Size of Oid larger than uint32_t, OidLen: " + << oidData->Length); + return; + } + } else { + PCPP_LOG_ERROR("Error in retrieving MTU: PacketRequest failed"); + } #else - struct ifreq ifr; + struct ifreq ifr; - memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, m_Name.c_str(), sizeof(ifr.ifr_name) - 1); + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, m_Name.c_str(), sizeof(ifr.ifr_name) - 1); - int socketfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); - if (ioctl(socketfd, SIOCGIFMTU, &ifr) == -1) - { - PCPP_LOG_DEBUG("Error in retrieving MTU: ioctl() returned -1"); - m_DeviceMtu = 0; - return; - } + int socketfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (ioctl(socketfd, SIOCGIFMTU, &ifr) == -1) { + PCPP_LOG_DEBUG("Error in retrieving MTU: ioctl() returned -1"); + m_DeviceMtu = 0; + return; + } - m_DeviceMtu = ifr.ifr_mtu; + m_DeviceMtu = ifr.ifr_mtu; #endif } -void PcapLiveDevice::setDeviceMacAddress() -{ +void PcapLiveDevice::setDeviceMacAddress() { #if defined(_WIN32) - LPADAPTER adapter = PacketOpenAdapter((char*)m_Name.c_str()); - if (adapter == NULL) - { - PCPP_LOG_ERROR("Error in retrieving MAC address: Adapter is NULL"); - return; - } - - uint8_t buffer[512]; - PACKET_OID_DATA* oidData = (PACKET_OID_DATA*)buffer; - oidData->Oid = OID_802_3_CURRENT_ADDRESS; - oidData->Length = 6; - oidData->Data[0] = 0; - if (PacketRequest(adapter, false, oidData)) - { - if (oidData->Length == 6) - { + LPADAPTER adapter = PacketOpenAdapter((char*)m_Name.c_str()); + if (adapter == NULL) { + PCPP_LOG_ERROR("Error in retrieving MAC address: Adapter is NULL"); + return; + } + + uint8_t buffer[512]; + PACKET_OID_DATA* oidData = (PACKET_OID_DATA*)buffer; + oidData->Oid = OID_802_3_CURRENT_ADDRESS; + oidData->Length = 6; + oidData->Data[0] = 0; + if (PacketRequest(adapter, false, oidData)) { + if (oidData->Length == 6) { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Warray-bounds" - /* copy value from driver */ - m_MacAddress = MacAddress(oidData->Data[0], oidData->Data[1], oidData->Data[2], oidData->Data[3], oidData->Data[4], oidData->Data[5]); + /* copy value from driver */ + m_MacAddress = + MacAddress(oidData->Data[0], oidData->Data[1], oidData->Data[2], + oidData->Data[3], oidData->Data[4], oidData->Data[5]); #pragma GCC diagnostic pop - PCPP_LOG_DEBUG(" MAC address: " << m_MacAddress); - } - else - { - /* the driver returned a value that is longer than expected (and longer than the given buffer) */ - PCPP_LOG_DEBUG("Error in retrieving MAC address: Size of Oid larger than 6, OidLen: " << oidData->Length); - return; - } - } - else - { - PCPP_LOG_DEBUG("Error in retrieving MAC address: PacketRequest failed"); - } + PCPP_LOG_DEBUG(" MAC address: " << m_MacAddress); + } else { + /* the driver returned a value that is longer than expected (and longer + * than the given buffer) */ + PCPP_LOG_DEBUG( + "Error in retrieving MAC address: Size of Oid larger than 6, OidLen: " + << oidData->Length); + return; + } + } else { + PCPP_LOG_DEBUG("Error in retrieving MAC address: PacketRequest failed"); + } #elif defined(__linux__) - struct ifreq ifr; + struct ifreq ifr; - memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, m_Name.c_str(), sizeof(ifr.ifr_name) - 1); + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, m_Name.c_str(), sizeof(ifr.ifr_name) - 1); - int socketfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); - if (ioctl(socketfd, SIOCGIFHWADDR, &ifr) == -1) - { - PCPP_LOG_DEBUG("Error in retrieving MAC address: ioctl() returned -1"); - return; - } + int socketfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (ioctl(socketfd, SIOCGIFHWADDR, &ifr) == -1) { + PCPP_LOG_DEBUG("Error in retrieving MAC address: ioctl() returned -1"); + return; + } - m_MacAddress = MacAddress(ifr.ifr_hwaddr.sa_data[0], ifr.ifr_hwaddr.sa_data[1], ifr.ifr_hwaddr.sa_data[2], ifr.ifr_hwaddr.sa_data[3], ifr.ifr_hwaddr.sa_data[4], ifr.ifr_hwaddr.sa_data[5]); + m_MacAddress = + MacAddress(ifr.ifr_hwaddr.sa_data[0], ifr.ifr_hwaddr.sa_data[1], + ifr.ifr_hwaddr.sa_data[2], ifr.ifr_hwaddr.sa_data[3], + ifr.ifr_hwaddr.sa_data[4], ifr.ifr_hwaddr.sa_data[5]); #elif defined(__APPLE__) || defined(__FreeBSD__) - int mib[6]; - size_t len; - - mib[0] = CTL_NET; - mib[1] = AF_ROUTE; - mib[2] = 0; - mib[3] = AF_LINK; - mib[4] = NET_RT_IFLIST; - mib[5] = if_nametoindex(m_Name.c_str()); - - if (mib[5] == 0){ - PCPP_LOG_DEBUG("Error in retrieving MAC address: if_nametoindex error"); - return; - } - - if (sysctl(mib, 6, nullptr, &len, nullptr, 0) < 0) - { - PCPP_LOG_DEBUG("Error in retrieving MAC address: sysctl 1 error"); - return; - } - - uint8_t buf[len]; - - if (sysctl(mib, 6, buf, &len, nullptr, 0) < 0) - { - PCPP_LOG_DEBUG("Error in retrieving MAC address: sysctl 2 error"); - return; - } - - struct if_msghdr*ifm = (struct if_msghdr *)buf; - struct sockaddr_dl* sdl = (struct sockaddr_dl *)(ifm + 1); - uint8_t* ptr = (uint8_t*)LLADDR(sdl); - m_MacAddress = MacAddress(ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5]); + int mib[6]; + size_t len; + + mib[0] = CTL_NET; + mib[1] = AF_ROUTE; + mib[2] = 0; + mib[3] = AF_LINK; + mib[4] = NET_RT_IFLIST; + mib[5] = if_nametoindex(m_Name.c_str()); + + if (mib[5] == 0) { + PCPP_LOG_DEBUG("Error in retrieving MAC address: if_nametoindex error"); + return; + } + + if (sysctl(mib, 6, nullptr, &len, nullptr, 0) < 0) { + PCPP_LOG_DEBUG("Error in retrieving MAC address: sysctl 1 error"); + return; + } + + uint8_t buf[len]; + + if (sysctl(mib, 6, buf, &len, nullptr, 0) < 0) { + PCPP_LOG_DEBUG("Error in retrieving MAC address: sysctl 2 error"); + return; + } + + struct if_msghdr* ifm = (struct if_msghdr*)buf; + struct sockaddr_dl* sdl = (struct sockaddr_dl*)(ifm + 1); + uint8_t* ptr = (uint8_t*)LLADDR(sdl); + m_MacAddress = MacAddress(ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5]); #endif } -void PcapLiveDevice::setDefaultGateway() -{ +void PcapLiveDevice::setDefaultGateway() { #if defined(_WIN32) - ULONG outBufLen = sizeof (IP_ADAPTER_INFO); - uint8_t* buffer = new uint8_t[outBufLen]; - PIP_ADAPTER_INFO adapterInfo = (IP_ADAPTER_INFO*)buffer; - DWORD retVal = 0; - - retVal = GetAdaptersInfo(adapterInfo, &outBufLen); - uint8_t* buffer2 = new uint8_t[outBufLen]; - if (retVal == ERROR_BUFFER_OVERFLOW) - adapterInfo = (IP_ADAPTER_INFO *)buffer2; - - retVal = GetAdaptersInfo(adapterInfo, &outBufLen); - - if (retVal == NO_ERROR) - { - PIP_ADAPTER_INFO curAdapterInfo = adapterInfo; - while (curAdapterInfo != NULL) - { - if (m_Name.find(curAdapterInfo->AdapterName) != std::string::npos) - m_DefaultGateway = IPv4Address(curAdapterInfo->GatewayList.IpAddress.String); - - curAdapterInfo = curAdapterInfo->Next; - } - } - else - { - PCPP_LOG_ERROR("Error retrieving default gateway address"); - } - - delete[] buffer; - // cppcheck-suppress uninitdata - delete[] buffer2; + ULONG outBufLen = sizeof(IP_ADAPTER_INFO); + uint8_t* buffer = new uint8_t[outBufLen]; + PIP_ADAPTER_INFO adapterInfo = (IP_ADAPTER_INFO*)buffer; + DWORD retVal = 0; + + retVal = GetAdaptersInfo(adapterInfo, &outBufLen); + uint8_t* buffer2 = new uint8_t[outBufLen]; + if (retVal == ERROR_BUFFER_OVERFLOW) + adapterInfo = (IP_ADAPTER_INFO*)buffer2; + + retVal = GetAdaptersInfo(adapterInfo, &outBufLen); + + if (retVal == NO_ERROR) { + PIP_ADAPTER_INFO curAdapterInfo = adapterInfo; + while (curAdapterInfo != NULL) { + if (m_Name.find(curAdapterInfo->AdapterName) != std::string::npos) + m_DefaultGateway = + IPv4Address(curAdapterInfo->GatewayList.IpAddress.String); + + curAdapterInfo = curAdapterInfo->Next; + } + } else { + PCPP_LOG_ERROR("Error retrieving default gateway address"); + } + + delete[] buffer; + // cppcheck-suppress uninitdata + delete[] buffer2; #elif defined(__linux__) - std::ifstream routeFile("/proc/net/route"); - std::string line; - while (std::getline(routeFile, line)) - { - std::stringstream lineStream(line); - std::string interfaceName; - std::getline(lineStream, interfaceName, '\t'); - if (interfaceName != m_Name) - continue; - - std::string interfaceDest; - std::getline(lineStream, interfaceDest, '\t'); - if (interfaceDest != "00000000") - continue; - - std::string interfaceGateway; - std::getline(lineStream, interfaceGateway, '\t'); - - uint32_t interfaceGatewayIPInt; - std::stringstream interfaceGatewayStream; - interfaceGatewayStream << std::hex << interfaceGateway; - interfaceGatewayStream >> interfaceGatewayIPInt; - m_DefaultGateway = IPv4Address(interfaceGatewayIPInt); - } + std::ifstream routeFile("/proc/net/route"); + std::string line; + while (std::getline(routeFile, line)) { + std::stringstream lineStream(line); + std::string interfaceName; + std::getline(lineStream, interfaceName, '\t'); + if (interfaceName != m_Name) + continue; + + std::string interfaceDest; + std::getline(lineStream, interfaceDest, '\t'); + if (interfaceDest != "00000000") + continue; + + std::string interfaceGateway; + std::getline(lineStream, interfaceGateway, '\t'); + + uint32_t interfaceGatewayIPInt; + std::stringstream interfaceGatewayStream; + interfaceGatewayStream << std::hex << interfaceGateway; + interfaceGatewayStream >> interfaceGatewayIPInt; + m_DefaultGateway = IPv4Address(interfaceGatewayIPInt); + } #elif defined(__APPLE__) || defined(__FreeBSD__) - std::string command = "netstat -nr | grep default | grep " + m_Name; - std::string ifaceInfo = executeShellCommand(command); - if (ifaceInfo == "") - { - PCPP_LOG_DEBUG("Error retrieving default gateway address: couldn't get netstat output"); - return; - } + std::string command = "netstat -nr | grep default | grep " + m_Name; + std::string ifaceInfo = executeShellCommand(command); + if (ifaceInfo == "") { + PCPP_LOG_DEBUG("Error retrieving default gateway address: couldn't get " + "netstat output"); + return; + } - // remove the word "default" - ifaceInfo.erase(0, 7); + // remove the word "default" + ifaceInfo.erase(0, 7); - // remove spaces - while (ifaceInfo.at(0) == ' ') - ifaceInfo.erase(0,1); + // remove spaces + while (ifaceInfo.at(0) == ' ') + ifaceInfo.erase(0, 1); - // erase string after gateway IP address - ifaceInfo.resize(ifaceInfo.find(' ', 0)); + // erase string after gateway IP address + ifaceInfo.resize(ifaceInfo.find(' ', 0)); - m_DefaultGateway = IPv4Address(ifaceInfo); + m_DefaultGateway = IPv4Address(ifaceInfo); #endif } -IPv4Address PcapLiveDevice::getIPv4Address() const -{ - for(std::vector::const_iterator addrIter = m_Addresses.begin(); addrIter != m_Addresses.end(); addrIter++) - { - if (Logger::getInstance().isDebugEnabled(PcapLogModuleLiveDevice) && addrIter->addr != nullptr) - { - char addrAsString[INET6_ADDRSTRLEN]; - internal::sockaddr2string(addrIter->addr, addrAsString); - PCPP_LOG_DEBUG("Searching address " << addrAsString); - } - - in_addr* currAddr = internal::sockaddr2in_addr(addrIter->addr); - if (currAddr == nullptr) - { - PCPP_LOG_DEBUG("Address is NULL"); - continue; - } - - return IPv4Address(currAddr->s_addr); - } - - return IPv4Address::Zero; -} +IPv4Address PcapLiveDevice::getIPv4Address() const { + for (std::vector::const_iterator addrIter = m_Addresses.begin(); + addrIter != m_Addresses.end(); addrIter++) { + if (Logger::getInstance().isDebugEnabled(PcapLogModuleLiveDevice) && + addrIter->addr != nullptr) { + char addrAsString[INET6_ADDRSTRLEN]; + internal::sockaddr2string(addrIter->addr, addrAsString); + PCPP_LOG_DEBUG("Searching address " << addrAsString); + } + + in_addr* currAddr = internal::sockaddr2in_addr(addrIter->addr); + if (currAddr == nullptr) { + PCPP_LOG_DEBUG("Address is NULL"); + continue; + } -IPv6Address PcapLiveDevice::getIPv6Address() const -{ - for (std::vector::const_iterator addrIter = m_Addresses.begin(); addrIter != m_Addresses.end(); - addrIter++) - { - if (Logger::getInstance().isDebugEnabled(PcapLogModuleLiveDevice) && addrIter->addr != nullptr) - { - char addrAsString[INET6_ADDRSTRLEN]; - internal::sockaddr2string(addrIter->addr, addrAsString); - PCPP_LOG_DEBUG("Searching address " << addrAsString); - } - in6_addr *currAddr = internal::sockaddr2in6_addr(addrIter->addr); - if (currAddr == nullptr) - { - PCPP_LOG_DEBUG("Address is NULL"); - continue; - } - return IPv6Address(currAddr->s6_addr); - } - return IPv6Address::Zero; + return IPv4Address(currAddr->s_addr); + } + + return IPv4Address::Zero; } -IPv4Address PcapLiveDevice::getDefaultGateway() const -{ - return m_DefaultGateway; +IPv6Address PcapLiveDevice::getIPv6Address() const { + for (std::vector::const_iterator addrIter = m_Addresses.begin(); + addrIter != m_Addresses.end(); addrIter++) { + if (Logger::getInstance().isDebugEnabled(PcapLogModuleLiveDevice) && + addrIter->addr != nullptr) { + char addrAsString[INET6_ADDRSTRLEN]; + internal::sockaddr2string(addrIter->addr, addrAsString); + PCPP_LOG_DEBUG("Searching address " << addrAsString); + } + in6_addr* currAddr = internal::sockaddr2in6_addr(addrIter->addr); + if (currAddr == nullptr) { + PCPP_LOG_DEBUG("Address is NULL"); + continue; + } + return IPv6Address(currAddr->s6_addr); + } + return IPv6Address::Zero; } -const std::vector& PcapLiveDevice::getDnsServers() const -{ - return PcapLiveDeviceList::getInstance().getDnsServers(); +IPv4Address PcapLiveDevice::getDefaultGateway() const { + return m_DefaultGateway; } -PcapLiveDevice::~PcapLiveDevice() -{ +const std::vector& PcapLiveDevice::getDnsServers() const { + return PcapLiveDeviceList::getInstance().getDnsServers(); } +PcapLiveDevice::~PcapLiveDevice() {} + } // namespace pcpp diff --git a/Pcap++/src/PcapLiveDeviceList.cpp b/Pcap++/src/PcapLiveDeviceList.cpp index c6da146dfe..1f93ab8e00 100644 --- a/Pcap++/src/PcapLiveDeviceList.cpp +++ b/Pcap++/src/PcapLiveDeviceList.cpp @@ -1,17 +1,17 @@ #define LOG_MODULE PcapLogModuleLiveDevice -#include "IpUtils.h" #include "PcapLiveDeviceList.h" +#include "IpUtils.h" #include "Logger.h" #include "SystemUtils.h" #include "pcap.h" -#include -#include #include +#include +#include #if defined(_WIN32) -#include -#include #include "WinPcapLiveDevice.h" +#include +#include #elif defined(__APPLE__) #include #elif defined(__FreeBSD__) @@ -19,341 +19,337 @@ #include #endif +namespace pcpp { -namespace pcpp -{ +PcapLiveDeviceList::PcapLiveDeviceList() { init(); } -PcapLiveDeviceList::PcapLiveDeviceList() -{ - init(); +PcapLiveDeviceList::~PcapLiveDeviceList() { + for (std::vector::iterator devIter = + m_LiveDeviceList.begin(); + devIter != m_LiveDeviceList.end(); devIter++) { + delete (*devIter); + } } -PcapLiveDeviceList::~PcapLiveDeviceList() -{ - for(std::vector::iterator devIter = m_LiveDeviceList.begin(); devIter != m_LiveDeviceList.end(); devIter++) - { - delete (*devIter); - } -} +void PcapLiveDeviceList::init() { + pcap_if_t* interfaceList; + char errbuf[PCAP_ERRBUF_SIZE]; + int err = pcap_findalldevs(&interfaceList, errbuf); + if (err < 0) { + PCPP_LOG_ERROR("Error searching for devices: " << errbuf); + } + + PCPP_LOG_DEBUG( + "Pcap lib version info: " << IPcapDevice::getPcapLibVersionInfo()); -void PcapLiveDeviceList::init() -{ - pcap_if_t* interfaceList; - char errbuf[PCAP_ERRBUF_SIZE]; - int err = pcap_findalldevs(&interfaceList, errbuf); - if (err < 0) - { - PCPP_LOG_ERROR("Error searching for devices: " << errbuf); - } - - PCPP_LOG_DEBUG("Pcap lib version info: " << IPcapDevice::getPcapLibVersionInfo()); - - pcap_if_t* currInterface = interfaceList; - while (currInterface != nullptr) - { + pcap_if_t* currInterface = interfaceList; + while (currInterface != nullptr) { #if defined(_WIN32) - PcapLiveDevice* dev = new WinPcapLiveDevice(currInterface, true, true, true); + PcapLiveDevice* dev = + new WinPcapLiveDevice(currInterface, true, true, true); #else //__linux__, __APPLE__, __FreeBSD__ - PcapLiveDevice* dev = new PcapLiveDevice(currInterface, true, true, true); + PcapLiveDevice* dev = new PcapLiveDevice(currInterface, true, true, true); #endif - currInterface = currInterface->next; - m_LiveDeviceList.insert(m_LiveDeviceList.end(), dev); - } + currInterface = currInterface->next; + m_LiveDeviceList.insert(m_LiveDeviceList.end(), dev); + } - setDnsServers(); + setDnsServers(); - PCPP_LOG_DEBUG("Freeing live device data"); - pcap_freealldevs(interfaceList); + PCPP_LOG_DEBUG("Freeing live device data"); + pcap_freealldevs(interfaceList); } -void PcapLiveDeviceList::setDnsServers() -{ +void PcapLiveDeviceList::setDnsServers() { #if defined(_WIN32) - FIXED_INFO * fixedInfo; - ULONG ulOutBufLen; - DWORD dwRetVal; - IP_ADDR_STRING * pIPAddr; - - uint8_t buf1[sizeof(FIXED_INFO)]; - fixedInfo = (FIXED_INFO *) buf1; - ulOutBufLen = sizeof( FIXED_INFO ); - - dwRetVal = GetNetworkParams( fixedInfo, &ulOutBufLen ); - uint8_t* buf2 = new uint8_t[ulOutBufLen]; - if(ERROR_BUFFER_OVERFLOW == dwRetVal) - { - fixedInfo = (FIXED_INFO *)buf2; - } - - if ((dwRetVal = GetNetworkParams( fixedInfo, &ulOutBufLen )) != 0) - PCPP_LOG_ERROR("Call to GetNetworkParams failed. Return Value: " << std::hex << dwRetVal); - else - { - m_DnsServers.push_back(IPv4Address(fixedInfo->DnsServerList.IpAddress.String)); - int i = 1; - PCPP_LOG_DEBUG("Default DNS server IP #" << i++ << ": " << fixedInfo->DnsServerList.IpAddress.String); - - pIPAddr = fixedInfo->DnsServerList.Next; - while ( pIPAddr ) - { - m_DnsServers.push_back(IPv4Address(pIPAddr->IpAddress.String)); - PCPP_LOG_DEBUG("Default DNS server IP #" << i++ << ": " << pIPAddr->IpAddress.String); - pIPAddr = pIPAddr -> Next; - } - } - - delete[] buf2; + FIXED_INFO* fixedInfo; + ULONG ulOutBufLen; + DWORD dwRetVal; + IP_ADDR_STRING* pIPAddr; + + uint8_t buf1[sizeof(FIXED_INFO)]; + fixedInfo = (FIXED_INFO*)buf1; + ulOutBufLen = sizeof(FIXED_INFO); + + dwRetVal = GetNetworkParams(fixedInfo, &ulOutBufLen); + uint8_t* buf2 = new uint8_t[ulOutBufLen]; + if (ERROR_BUFFER_OVERFLOW == dwRetVal) { + fixedInfo = (FIXED_INFO*)buf2; + } + + if ((dwRetVal = GetNetworkParams(fixedInfo, &ulOutBufLen)) != 0) + PCPP_LOG_ERROR("Call to GetNetworkParams failed. Return Value: " + << std::hex << dwRetVal); + else { + m_DnsServers.push_back( + IPv4Address(fixedInfo->DnsServerList.IpAddress.String)); + int i = 1; + PCPP_LOG_DEBUG("Default DNS server IP #" + << i++ << ": " << fixedInfo->DnsServerList.IpAddress.String); + + pIPAddr = fixedInfo->DnsServerList.Next; + while (pIPAddr) { + m_DnsServers.push_back(IPv4Address(pIPAddr->IpAddress.String)); + PCPP_LOG_DEBUG("Default DNS server IP #" << i++ << ": " + << pIPAddr->IpAddress.String); + pIPAddr = pIPAddr->Next; + } + } + + delete[] buf2; #elif defined(__linux__) - // verify that nmcli exist - std::string command = "command -v nmcli >/dev/null 2>&1 || { echo 'nmcli not installed'; }"; - std::string nmcliExists = executeShellCommand(command); - if (nmcliExists != "") - { - PCPP_LOG_DEBUG("Error retrieving DNS server list: nmcli doesn't exist"); - return; - } - - // check nmcli major version (0 or 1) - command = "nmcli -v | awk -F' ' '{print $NF}' | awk -F'.' '{print $1}'"; - std::string nmcliMajorVer = executeShellCommand(command); - nmcliMajorVer.erase(std::remove(nmcliMajorVer.begin(), nmcliMajorVer.end(), '\n'), nmcliMajorVer.end()); - PCPP_LOG_DEBUG("Found nmcli. nmcli major version is: '" << nmcliMajorVer << "'"); - - // build nmcli command according to its major version - if (nmcliMajorVer == "0") - command = "nmcli dev list | grep IP4.DNS"; - else - command = "nmcli dev show | grep IP4.DNS"; - - std::string dnsServersInfo = executeShellCommand(command); - if (dnsServersInfo == "") - { - PCPP_LOG_DEBUG("Error retrieving DNS server list: call to nmcli gave no output"); - return; - } - - std::istringstream stream(dnsServersInfo); - std::string line; - int i = 1; - while(std::getline(stream, line)) - { - std::istringstream lineStream(line); - std::string headline; - std::string dnsIP; - lineStream >> headline; - lineStream >> dnsIP; - IPv4Address dnsIPAddr(dnsIP); - if (!dnsIPAddr.isValid()) - continue; - - if (std::find(m_DnsServers.begin(), m_DnsServers.end(), dnsIPAddr) == m_DnsServers.end()) - { - m_DnsServers.push_back(dnsIPAddr); - PCPP_LOG_DEBUG("Default DNS server IP #" << i++ << ": " << dnsIPAddr); - } - } + // verify that nmcli exist + std::string command = + "command -v nmcli >/dev/null 2>&1 || { echo 'nmcli not installed'; }"; + std::string nmcliExists = executeShellCommand(command); + if (nmcliExists != "") { + PCPP_LOG_DEBUG("Error retrieving DNS server list: nmcli doesn't exist"); + return; + } + + // check nmcli major version (0 or 1) + command = "nmcli -v | awk -F' ' '{print $NF}' | awk -F'.' '{print $1}'"; + std::string nmcliMajorVer = executeShellCommand(command); + nmcliMajorVer.erase( + std::remove(nmcliMajorVer.begin(), nmcliMajorVer.end(), '\n'), + nmcliMajorVer.end()); + PCPP_LOG_DEBUG("Found nmcli. nmcli major version is: '" << nmcliMajorVer + << "'"); + + // build nmcli command according to its major version + if (nmcliMajorVer == "0") + command = "nmcli dev list | grep IP4.DNS"; + else + command = "nmcli dev show | grep IP4.DNS"; + + std::string dnsServersInfo = executeShellCommand(command); + if (dnsServersInfo == "") { + PCPP_LOG_DEBUG( + "Error retrieving DNS server list: call to nmcli gave no output"); + return; + } + + std::istringstream stream(dnsServersInfo); + std::string line; + int i = 1; + while (std::getline(stream, line)) { + std::istringstream lineStream(line); + std::string headline; + std::string dnsIP; + lineStream >> headline; + lineStream >> dnsIP; + IPv4Address dnsIPAddr(dnsIP); + if (!dnsIPAddr.isValid()) + continue; + + if (std::find(m_DnsServers.begin(), m_DnsServers.end(), dnsIPAddr) == + m_DnsServers.end()) { + m_DnsServers.push_back(dnsIPAddr); + PCPP_LOG_DEBUG("Default DNS server IP #" << i++ << ": " << dnsIPAddr); + } + } #elif defined(__APPLE__) - SCDynamicStoreRef dynRef = SCDynamicStoreCreate(kCFAllocatorSystemDefault, CFSTR("iked"), nullptr, nullptr); - if (dynRef == nullptr) - { - PCPP_LOG_DEBUG("Couldn't set DNS server list: failed to retrieve SCDynamicStore"); - return; - } - - CFDictionaryRef dnsDict = (CFDictionaryRef)SCDynamicStoreCopyValue(dynRef,CFSTR("State:/Network/Global/DNS")); - - if (dnsDict == nullptr) - { - PCPP_LOG_DEBUG("Couldn't set DNS server list: failed to get DNS dictionary"); - CFRelease(dynRef); - return; - } - - CFArrayRef serverAddresses = (CFArrayRef)CFDictionaryGetValue(dnsDict, CFSTR("ServerAddresses")); - - if (serverAddresses == nullptr) - { - PCPP_LOG_DEBUG("Couldn't set DNS server list: server addresses array is null"); - CFRelease(dynRef); - CFRelease(dnsDict); - return; - } - - CFIndex count = CFArrayGetCount(serverAddresses); - for (CFIndex i = 0; i < count; i++) - { - CFStringRef serverAddress = (CFStringRef)CFArrayGetValueAtIndex(serverAddresses, i); - - if (serverAddress == nullptr) - continue; - - uint8_t buf[20]; - char* serverAddressCString = (char*)buf; - CFStringGetCString(serverAddress, serverAddressCString, 20, kCFStringEncodingUTF8); - m_DnsServers.push_back(IPv4Address(serverAddressCString)); - PCPP_LOG_DEBUG("Default DNS server IP #" << (int)(i+1) << ": " << serverAddressCString); - } - - CFRelease(dynRef); - CFRelease(dnsDict); + SCDynamicStoreRef dynRef = SCDynamicStoreCreate( + kCFAllocatorSystemDefault, CFSTR("iked"), nullptr, nullptr); + if (dynRef == nullptr) { + PCPP_LOG_DEBUG( + "Couldn't set DNS server list: failed to retrieve SCDynamicStore"); + return; + } + + CFDictionaryRef dnsDict = (CFDictionaryRef)SCDynamicStoreCopyValue( + dynRef, CFSTR("State:/Network/Global/DNS")); + + if (dnsDict == nullptr) { + PCPP_LOG_DEBUG( + "Couldn't set DNS server list: failed to get DNS dictionary"); + CFRelease(dynRef); + return; + } + + CFArrayRef serverAddresses = + (CFArrayRef)CFDictionaryGetValue(dnsDict, CFSTR("ServerAddresses")); + + if (serverAddresses == nullptr) { + PCPP_LOG_DEBUG( + "Couldn't set DNS server list: server addresses array is null"); + CFRelease(dynRef); + CFRelease(dnsDict); + return; + } + + CFIndex count = CFArrayGetCount(serverAddresses); + for (CFIndex i = 0; i < count; i++) { + CFStringRef serverAddress = + (CFStringRef)CFArrayGetValueAtIndex(serverAddresses, i); + + if (serverAddress == nullptr) + continue; + + uint8_t buf[20]; + char* serverAddressCString = (char*)buf; + CFStringGetCString(serverAddress, serverAddressCString, 20, + kCFStringEncodingUTF8); + m_DnsServers.push_back(IPv4Address(serverAddressCString)); + PCPP_LOG_DEBUG("Default DNS server IP #" << (int)(i + 1) << ": " + << serverAddressCString); + } + + CFRelease(dynRef); + CFRelease(dnsDict); #elif defined(__FreeBSD__) - res_init(); + res_init(); - for (int i = 0; i < _res.nscount; i++) - { - sockaddr* saddr = (sockaddr*)&_res.nsaddr_list[i]; - if (saddr == NULL) - continue; - in_addr* inaddr = internal::sockaddr2in_addr(saddr); - if (inaddr == NULL) - continue; - m_DnsServers.push_back(IPv4Address(internal::in_addr2int(*inaddr))); - } + for (int i = 0; i < _res.nscount; i++) { + sockaddr* saddr = (sockaddr*)&_res.nsaddr_list[i]; + if (saddr == NULL) + continue; + in_addr* inaddr = internal::sockaddr2in_addr(saddr); + if (inaddr == NULL) + continue; + m_DnsServers.push_back(IPv4Address(internal::in_addr2int(*inaddr))); + } #endif } -PcapLiveDevice* PcapLiveDeviceList::getPcapLiveDeviceByIp(const IPAddress& ipAddr) const -{ - if (ipAddr.getType() == IPAddress::IPv4AddressType) - { - return getPcapLiveDeviceByIp(ipAddr.getIPv4()); - } - else //IPAddress::IPv6AddressType - { - return getPcapLiveDeviceByIp(ipAddr.getIPv6()); - } +PcapLiveDevice* +PcapLiveDeviceList::getPcapLiveDeviceByIp(const IPAddress& ipAddr) const { + if (ipAddr.getType() == IPAddress::IPv4AddressType) { + return getPcapLiveDeviceByIp(ipAddr.getIPv4()); + } else // IPAddress::IPv6AddressType + { + return getPcapLiveDeviceByIp(ipAddr.getIPv6()); + } } -PcapLiveDevice* PcapLiveDeviceList::getPcapLiveDeviceByIp(const IPv4Address& ipAddr) const -{ - PCPP_LOG_DEBUG("Searching all live devices..."); - for(std::vector::const_iterator devIter = m_LiveDeviceList.begin(); devIter != m_LiveDeviceList.end(); devIter++) - { - PCPP_LOG_DEBUG("Searching device '" << (*devIter)->m_Name << "'. Searching all addresses..."); - for(std::vector::iterator addrIter = (*devIter)->m_Addresses.begin(); addrIter != (*devIter)->m_Addresses.end(); addrIter++) - { - if (Logger::getInstance().isDebugEnabled(PcapLogModuleLiveDevice) && addrIter->addr != nullptr) - { - char addrAsString[INET6_ADDRSTRLEN]; - internal::sockaddr2string(addrIter->addr, addrAsString); - PCPP_LOG_DEBUG("Searching address " << addrAsString); - } - - in_addr* currAddr = internal::sockaddr2in_addr(addrIter->addr); - if (currAddr == nullptr) - { - PCPP_LOG_DEBUG("Address is NULL"); - continue; - } - - if (currAddr->s_addr == ipAddr.toInt()) - { - PCPP_LOG_DEBUG("Found matched address!"); - return (*devIter); - } - } - } - - return nullptr; +PcapLiveDevice* +PcapLiveDeviceList::getPcapLiveDeviceByIp(const IPv4Address& ipAddr) const { + PCPP_LOG_DEBUG("Searching all live devices..."); + for (std::vector::const_iterator devIter = + m_LiveDeviceList.begin(); + devIter != m_LiveDeviceList.end(); devIter++) { + PCPP_LOG_DEBUG("Searching device '" << (*devIter)->m_Name + << "'. Searching all addresses..."); + for (std::vector::iterator addrIter = + (*devIter)->m_Addresses.begin(); + addrIter != (*devIter)->m_Addresses.end(); addrIter++) { + if (Logger::getInstance().isDebugEnabled(PcapLogModuleLiveDevice) && + addrIter->addr != nullptr) { + char addrAsString[INET6_ADDRSTRLEN]; + internal::sockaddr2string(addrIter->addr, addrAsString); + PCPP_LOG_DEBUG("Searching address " << addrAsString); + } + + in_addr* currAddr = internal::sockaddr2in_addr(addrIter->addr); + if (currAddr == nullptr) { + PCPP_LOG_DEBUG("Address is NULL"); + continue; + } + + if (currAddr->s_addr == ipAddr.toInt()) { + PCPP_LOG_DEBUG("Found matched address!"); + return (*devIter); + } + } + } + + return nullptr; } -PcapLiveDevice* PcapLiveDeviceList::getPcapLiveDeviceByIp(const IPv6Address& ip6Addr) const -{ - PCPP_LOG_DEBUG("Searching all live devices..."); - for(std::vector::const_iterator devIter = m_LiveDeviceList.begin(); devIter != m_LiveDeviceList.end(); devIter++) - { - PCPP_LOG_DEBUG("Searching device '" << (*devIter)->m_Name << "'. Searching all addresses..."); - for(std::vector::iterator addrIter = (*devIter)->m_Addresses.begin(); addrIter != (*devIter)->m_Addresses.end(); addrIter++) - { - if (Logger::getInstance().isDebugEnabled(PcapLogModuleLiveDevice) && addrIter->addr != nullptr) - { - char addrAsString[INET6_ADDRSTRLEN]; - internal::sockaddr2string(addrIter->addr, addrAsString); - PCPP_LOG_DEBUG("Searching address " << addrAsString); - } - - in6_addr* currAddr = internal::sockaddr2in6_addr(addrIter->addr); - if (currAddr == nullptr) - { - PCPP_LOG_DEBUG("Address is NULL"); - continue; - } - - uint8_t* addrAsArr; size_t addrLen; - ip6Addr.copyTo(&addrAsArr, addrLen); - if (memcmp(currAddr, addrAsArr, sizeof(struct in6_addr)) == 0) - { - PCPP_LOG_DEBUG("Found matched address!"); - delete [] addrAsArr; - return (*devIter); - } - - delete [] addrAsArr; - } - } - - return nullptr; -} - -PcapLiveDevice* PcapLiveDeviceList::getPcapLiveDeviceByIp(const std::string& ipAddrAsString) const -{ - IPAddress ipAddr(ipAddrAsString); - if (!ipAddr.isValid()) - { - PCPP_LOG_ERROR("IP address illegal"); - return nullptr; - } - - PcapLiveDevice* result = PcapLiveDeviceList::getPcapLiveDeviceByIp(ipAddr); - return result; +PcapLiveDevice* +PcapLiveDeviceList::getPcapLiveDeviceByIp(const IPv6Address& ip6Addr) const { + PCPP_LOG_DEBUG("Searching all live devices..."); + for (std::vector::const_iterator devIter = + m_LiveDeviceList.begin(); + devIter != m_LiveDeviceList.end(); devIter++) { + PCPP_LOG_DEBUG("Searching device '" << (*devIter)->m_Name + << "'. Searching all addresses..."); + for (std::vector::iterator addrIter = + (*devIter)->m_Addresses.begin(); + addrIter != (*devIter)->m_Addresses.end(); addrIter++) { + if (Logger::getInstance().isDebugEnabled(PcapLogModuleLiveDevice) && + addrIter->addr != nullptr) { + char addrAsString[INET6_ADDRSTRLEN]; + internal::sockaddr2string(addrIter->addr, addrAsString); + PCPP_LOG_DEBUG("Searching address " << addrAsString); + } + + in6_addr* currAddr = internal::sockaddr2in6_addr(addrIter->addr); + if (currAddr == nullptr) { + PCPP_LOG_DEBUG("Address is NULL"); + continue; + } + + uint8_t* addrAsArr; + size_t addrLen; + ip6Addr.copyTo(&addrAsArr, addrLen); + if (memcmp(currAddr, addrAsArr, sizeof(struct in6_addr)) == 0) { + PCPP_LOG_DEBUG("Found matched address!"); + delete[] addrAsArr; + return (*devIter); + } + + delete[] addrAsArr; + } + } + + return nullptr; } +PcapLiveDevice* PcapLiveDeviceList::getPcapLiveDeviceByIp( + const std::string& ipAddrAsString) const { + IPAddress ipAddr(ipAddrAsString); + if (!ipAddr.isValid()) { + PCPP_LOG_ERROR("IP address illegal"); + return nullptr; + } -PcapLiveDevice* PcapLiveDeviceList::getPcapLiveDeviceByName(const std::string& name) const -{ - PCPP_LOG_DEBUG("Searching all live devices..."); - for(std::vector::const_iterator devIter = m_LiveDeviceList.begin(); devIter != m_LiveDeviceList.end(); devIter++) - { - if (name == (*devIter)->getName()) - return (*devIter); - } + PcapLiveDevice* result = PcapLiveDeviceList::getPcapLiveDeviceByIp(ipAddr); + return result; +} - return nullptr; +PcapLiveDevice* +PcapLiveDeviceList::getPcapLiveDeviceByName(const std::string& name) const { + PCPP_LOG_DEBUG("Searching all live devices..."); + for (std::vector::const_iterator devIter = + m_LiveDeviceList.begin(); + devIter != m_LiveDeviceList.end(); devIter++) { + if (name == (*devIter)->getName()) + return (*devIter); + } + + return nullptr; } -PcapLiveDevice* PcapLiveDeviceList::getPcapLiveDeviceByIpOrName(const std::string& ipOrName) const -{ - IPAddress interfaceIP(ipOrName); - if (interfaceIP.isValid()) - { - return PcapLiveDeviceList::getInstance().getPcapLiveDeviceByIp(interfaceIP); - } - else - { - return PcapLiveDeviceList::getInstance().getPcapLiveDeviceByName(ipOrName); - } +PcapLiveDevice* PcapLiveDeviceList::getPcapLiveDeviceByIpOrName( + const std::string& ipOrName) const { + IPAddress interfaceIP(ipOrName); + if (interfaceIP.isValid()) { + return PcapLiveDeviceList::getInstance().getPcapLiveDeviceByIp(interfaceIP); + } else { + return PcapLiveDeviceList::getInstance().getPcapLiveDeviceByName(ipOrName); + } } -PcapLiveDeviceList* PcapLiveDeviceList::clone() -{ - return new PcapLiveDeviceList; +PcapLiveDeviceList* PcapLiveDeviceList::clone() { + return new PcapLiveDeviceList; } -void PcapLiveDeviceList::reset() -{ - for(std::vector::iterator devIter = m_LiveDeviceList.begin(); devIter != m_LiveDeviceList.end(); devIter++) - { - delete (*devIter); - } +void PcapLiveDeviceList::reset() { + for (std::vector::iterator devIter = + m_LiveDeviceList.begin(); + devIter != m_LiveDeviceList.end(); devIter++) { + delete (*devIter); + } - m_LiveDeviceList.clear(); - m_DnsServers.clear(); + m_LiveDeviceList.clear(); + m_DnsServers.clear(); - init(); + init(); } } // namespace pcpp diff --git a/Pcap++/src/PcapRemoteDevice.cpp b/Pcap++/src/PcapRemoteDevice.cpp index b08733f10c..1a94fbe99d 100644 --- a/Pcap++/src/PcapRemoteDevice.cpp +++ b/Pcap++/src/PcapRemoteDevice.cpp @@ -6,133 +6,125 @@ #include "Logger.h" #include "pcap.h" - -namespace pcpp -{ - -pcap_rmtauth PcapRemoteAuthentication::getPcapRmAuth() const -{ - pcap_rmtauth result; - result.type = RPCAP_RMTAUTH_PWD; - result.username = (char*)userName.c_str(); - result.password = (char*)password.c_str(); - return result; +namespace pcpp { + +pcap_rmtauth PcapRemoteAuthentication::getPcapRmAuth() const { + pcap_rmtauth result; + result.type = RPCAP_RMTAUTH_PWD; + result.username = (char*)userName.c_str(); + result.password = (char*)password.c_str(); + return result; } -PcapRemoteDevice::PcapRemoteDevice(pcap_if_t* iface, PcapRemoteAuthentication* remoteAuthentication, const IPAddress& remoteMachineIP, uint16_t remoteMachinePort) - : PcapLiveDevice(iface, false, false, false) -{ - PCPP_LOG_DEBUG("MTU calculation isn't supported for remote devices. Setting MTU to 1514"); - m_DeviceMtu = 1514; - m_RemoteMachineIpAddress = remoteMachineIP; - m_RemoteMachinePort = remoteMachinePort; - m_RemoteAuthentication = remoteAuthentication; +PcapRemoteDevice::PcapRemoteDevice( + pcap_if_t* iface, PcapRemoteAuthentication* remoteAuthentication, + const IPAddress& remoteMachineIP, uint16_t remoteMachinePort) + : PcapLiveDevice(iface, false, false, false) { + PCPP_LOG_DEBUG("MTU calculation isn't supported for remote devices. Setting " + "MTU to 1514"); + m_DeviceMtu = 1514; + m_RemoteMachineIpAddress = remoteMachineIP; + m_RemoteMachinePort = remoteMachinePort; + m_RemoteAuthentication = remoteAuthentication; } - -bool PcapRemoteDevice::open() -{ - char errbuf[PCAP_ERRBUF_SIZE]; - int flags = PCAP_OPENFLAG_PROMISCUOUS | PCAP_OPENFLAG_NOCAPTURE_RPCAP; //PCAP_OPENFLAG_DATATX_UDP doesn't always work - PCPP_LOG_DEBUG("Opening device '" << m_Name << "'"); - pcap_rmtauth* pRmAuth = NULL; - pcap_rmtauth rmAuth; - if (m_RemoteAuthentication != NULL) - { - rmAuth = m_RemoteAuthentication->getPcapRmAuth(); - pRmAuth = &rmAuth; - } - - m_PcapDescriptor = pcap_open(m_Name.c_str(), PCPP_MAX_PACKET_SIZE, flags, 250, pRmAuth, errbuf); - if (m_PcapDescriptor == NULL) - { - PCPP_LOG_ERROR("Error opening device. Error was: " << errbuf); - m_DeviceOpened = false; - return false; - } - - //in Remote devices there shouldn't be 2 separate descriptors - m_PcapSendDescriptor = m_PcapDescriptor; - - //setFilter requires that m_DeviceOpened == true - m_DeviceOpened = true; - - //for some reason if a filter is not set than WinPcap throws an exception. So Here is a generic filter that catches all traffic - if (!setFilter("ether proto (\\ip or \\ip6 or \\arp or \\rarp or \\decnet or \\sca or \\lat or \\mopdl or \\moprc or \\iso or \\stp or \\ipx or \\netbeui or 0x80F3)")) //0x80F3 == AARP - { - PCPP_LOG_ERROR("Error setting the filter. Error was: " << Logger::getInstance().getLastError()); - m_DeviceOpened = false; - return false; - } - - PCPP_LOG_DEBUG("Device '" << m_Name << "' opened"); - - return true; +bool PcapRemoteDevice::open() { + char errbuf[PCAP_ERRBUF_SIZE]; + int flags = PCAP_OPENFLAG_PROMISCUOUS | + PCAP_OPENFLAG_NOCAPTURE_RPCAP; // PCAP_OPENFLAG_DATATX_UDP doesn't + // always work + PCPP_LOG_DEBUG("Opening device '" << m_Name << "'"); + pcap_rmtauth* pRmAuth = NULL; + pcap_rmtauth rmAuth; + if (m_RemoteAuthentication != NULL) { + rmAuth = m_RemoteAuthentication->getPcapRmAuth(); + pRmAuth = &rmAuth; + } + + m_PcapDescriptor = pcap_open(m_Name.c_str(), PCPP_MAX_PACKET_SIZE, flags, 250, + pRmAuth, errbuf); + if (m_PcapDescriptor == NULL) { + PCPP_LOG_ERROR("Error opening device. Error was: " << errbuf); + m_DeviceOpened = false; + return false; + } + + // in Remote devices there shouldn't be 2 separate descriptors + m_PcapSendDescriptor = m_PcapDescriptor; + + // setFilter requires that m_DeviceOpened == true + m_DeviceOpened = true; + + // for some reason if a filter is not set than WinPcap throws an exception. So + // Here is a generic filter that catches all traffic + if (!setFilter("ether proto (\\ip or \\ip6 or \\arp or \\rarp or \\decnet or " + "\\sca or \\lat or \\mopdl or \\moprc or \\iso or \\stp or " + "\\ipx or \\netbeui or 0x80F3)")) // 0x80F3 == AARP + { + PCPP_LOG_ERROR("Error setting the filter. Error was: " + << Logger::getInstance().getLastError()); + m_DeviceOpened = false; + return false; + } + + PCPP_LOG_DEBUG("Device '" << m_Name << "' opened"); + + return true; } -void* PcapRemoteDevice::remoteDeviceCaptureThreadMain(void *ptr) -{ - PcapRemoteDevice* pThis = (PcapRemoteDevice*)ptr; - if (pThis == NULL) - { - PCPP_LOG_ERROR("Capture thread: Unable to extract PcapLiveDevice instance"); - return 0; - } - - PCPP_LOG_DEBUG("Started capture thread for device '" << pThis->m_Name << "'"); - - pcap_pkthdr* pkthdr; - const uint8_t* pktData; - - if (pThis->m_CaptureCallbackMode) - { - while (!pThis->m_StopThread) - { - if (pcap_next_ex(pThis->m_PcapDescriptor, &pkthdr, &pktData) > 0) - onPacketArrives((uint8_t*)pThis, pkthdr, pktData); - } - } - else - { - while (!pThis->m_StopThread) - { - if (pcap_next_ex(pThis->m_PcapDescriptor, &pkthdr, &pktData) > 0) - onPacketArrivesNoCallback((uint8_t*)pThis, pkthdr, pktData); - } - } - PCPP_LOG_DEBUG("Ended capture thread for device '" << pThis->m_Name << "'"); - return 0; +void* PcapRemoteDevice::remoteDeviceCaptureThreadMain(void* ptr) { + PcapRemoteDevice* pThis = (PcapRemoteDevice*)ptr; + if (pThis == NULL) { + PCPP_LOG_ERROR("Capture thread: Unable to extract PcapLiveDevice instance"); + return 0; + } + + PCPP_LOG_DEBUG("Started capture thread for device '" << pThis->m_Name << "'"); + + pcap_pkthdr* pkthdr; + const uint8_t* pktData; + + if (pThis->m_CaptureCallbackMode) { + while (!pThis->m_StopThread) { + if (pcap_next_ex(pThis->m_PcapDescriptor, &pkthdr, &pktData) > 0) + onPacketArrives((uint8_t*)pThis, pkthdr, pktData); + } + } else { + while (!pThis->m_StopThread) { + if (pcap_next_ex(pThis->m_PcapDescriptor, &pkthdr, &pktData) > 0) + onPacketArrivesNoCallback((uint8_t*)pThis, pkthdr, pktData); + } + } + PCPP_LOG_DEBUG("Ended capture thread for device '" << pThis->m_Name << "'"); + return 0; } -ThreadStart PcapRemoteDevice::getCaptureThreadStart() -{ - return &remoteDeviceCaptureThreadMain; +ThreadStart PcapRemoteDevice::getCaptureThreadStart() { + return &remoteDeviceCaptureThreadMain; } -void PcapRemoteDevice::getStatistics(PcapStats& stats) const -{ - int allocatedMemory; - pcap_stat* tempStats = pcap_stats_ex(m_PcapDescriptor, &allocatedMemory); - if (allocatedMemory < (int)sizeof(pcap_stat)) - { - PCPP_LOG_ERROR("Error getting statistics from live device '" << m_Name << "': WinPcap did not allocate the entire struct"); - return; - } - stats.packetsRecv = tempStats->ps_capt; - stats.packetsDrop = tempStats->ps_drop + tempStats->ps_netdrop; - stats.packetsDropByInterface = tempStats->ps_ifdrop; +void PcapRemoteDevice::getStatistics(PcapStats& stats) const { + int allocatedMemory; + pcap_stat* tempStats = pcap_stats_ex(m_PcapDescriptor, &allocatedMemory); + if (allocatedMemory < (int)sizeof(pcap_stat)) { + PCPP_LOG_ERROR("Error getting statistics from live device '" + << m_Name + << "': WinPcap did not allocate the entire struct"); + return; + } + stats.packetsRecv = tempStats->ps_capt; + stats.packetsDrop = tempStats->ps_drop + tempStats->ps_netdrop; + stats.packetsDropByInterface = tempStats->ps_ifdrop; } -uint32_t PcapRemoteDevice::getMtu() const -{ - PCPP_LOG_DEBUG("MTU isn't supported for remote devices"); - return 0; +uint32_t PcapRemoteDevice::getMtu() const { + PCPP_LOG_DEBUG("MTU isn't supported for remote devices"); + return 0; } -MacAddress PcapRemoteDevice::getMacAddress() const -{ - PCPP_LOG_ERROR("MAC address isn't supported for remote devices"); - return MacAddress::Zero; +MacAddress PcapRemoteDevice::getMacAddress() const { + PCPP_LOG_ERROR("MAC address isn't supported for remote devices"); + return MacAddress::Zero; } } // namespace pcpp diff --git a/Pcap++/src/PcapRemoteDeviceList.cpp b/Pcap++/src/PcapRemoteDeviceList.cpp index 94197d3513..2ece6e77e8 100644 --- a/Pcap++/src/PcapRemoteDeviceList.cpp +++ b/Pcap++/src/PcapRemoteDeviceList.cpp @@ -3,219 +3,215 @@ #define LOG_MODULE PcapLogModuleRemoteDevice #include "PcapRemoteDeviceList.h" -#include "Logger.h" #include "IpUtils.h" +#include "Logger.h" #include "pcap.h" #include -namespace pcpp -{ +namespace pcpp { -PcapRemoteDeviceList* PcapRemoteDeviceList::getRemoteDeviceList(const IPAddress& ipAddress, uint16_t port) -{ - return PcapRemoteDeviceList::getRemoteDeviceList(ipAddress, port, NULL); +PcapRemoteDeviceList* +PcapRemoteDeviceList::getRemoteDeviceList(const IPAddress& ipAddress, + uint16_t port) { + return PcapRemoteDeviceList::getRemoteDeviceList(ipAddress, port, NULL); } -PcapRemoteDeviceList* PcapRemoteDeviceList::getRemoteDeviceList(const IPAddress& ipAddress, uint16_t port, PcapRemoteAuthentication* remoteAuth) -{ - if (!ipAddress.isValid()) - { - PCPP_LOG_ERROR("IP address is NULL or not valid"); - return NULL; - } - - PCPP_LOG_DEBUG("Searching remote devices on IP: " << ipAddress << " and port: " << port); - char remoteCaptureString[PCAP_BUF_SIZE]; - char errbuf[PCAP_ERRBUF_SIZE]; - std::ostringstream portAsString; - portAsString << port; - if (pcap_createsrcstr(remoteCaptureString, PCAP_SRC_IFREMOTE, ipAddress.toString().c_str(), portAsString.str().c_str(), NULL, errbuf) != 0) - { - PCPP_LOG_ERROR("Error in creating the remote connection string. Error was: " << errbuf); - return NULL; - } - - PCPP_LOG_DEBUG("Remote capture string: " << remoteCaptureString); - - pcap_rmtauth* pRmAuth = NULL; - pcap_rmtauth rmAuth; - if (remoteAuth != NULL) - { - PCPP_LOG_DEBUG("Authentication requested. Username: " << remoteAuth->userName << ", Password: " << remoteAuth->password); - rmAuth = remoteAuth->getPcapRmAuth(); - pRmAuth = &rmAuth; - } - - pcap_if_t* interfaceList; - char errorBuf[PCAP_ERRBUF_SIZE]; - if (pcap_findalldevs_ex(remoteCaptureString, pRmAuth, &interfaceList, errorBuf) < 0) - { - PCPP_LOG_ERROR("Error retrieving device on remote machine. Error string is: " << errorBuf); - return NULL; - } - - PcapRemoteDeviceList* resultList = new PcapRemoteDeviceList(); - resultList->setRemoteMachineIpAddress(ipAddress); - resultList->setRemoteMachinePort(port); - resultList->setRemoteAuthentication(remoteAuth); - - pcap_if_t* currInterface = interfaceList; - while (currInterface != NULL) - { - PcapRemoteDevice* pNewRemoteDevice = new PcapRemoteDevice(currInterface, resultList->m_RemoteAuthentication, - resultList->getRemoteMachineIpAddress(), resultList->getRemoteMachinePort()); - resultList->m_RemoteDeviceList.push_back(pNewRemoteDevice); - currInterface = currInterface->next; - } - - pcap_freealldevs(interfaceList); - return resultList; +PcapRemoteDeviceList* PcapRemoteDeviceList::getRemoteDeviceList( + const IPAddress& ipAddress, uint16_t port, + PcapRemoteAuthentication* remoteAuth) { + if (!ipAddress.isValid()) { + PCPP_LOG_ERROR("IP address is NULL or not valid"); + return NULL; + } + + PCPP_LOG_DEBUG("Searching remote devices on IP: " << ipAddress + << " and port: " << port); + char remoteCaptureString[PCAP_BUF_SIZE]; + char errbuf[PCAP_ERRBUF_SIZE]; + std::ostringstream portAsString; + portAsString << port; + if (pcap_createsrcstr(remoteCaptureString, PCAP_SRC_IFREMOTE, + ipAddress.toString().c_str(), + portAsString.str().c_str(), NULL, errbuf) != 0) { + PCPP_LOG_ERROR("Error in creating the remote connection string. Error was: " + << errbuf); + return NULL; + } + + PCPP_LOG_DEBUG("Remote capture string: " << remoteCaptureString); + + pcap_rmtauth* pRmAuth = NULL; + pcap_rmtauth rmAuth; + if (remoteAuth != NULL) { + PCPP_LOG_DEBUG("Authentication requested. Username: " + << remoteAuth->userName + << ", Password: " << remoteAuth->password); + rmAuth = remoteAuth->getPcapRmAuth(); + pRmAuth = &rmAuth; + } + + pcap_if_t* interfaceList; + char errorBuf[PCAP_ERRBUF_SIZE]; + if (pcap_findalldevs_ex(remoteCaptureString, pRmAuth, &interfaceList, + errorBuf) < 0) { + PCPP_LOG_ERROR( + "Error retrieving device on remote machine. Error string is: " + << errorBuf); + return NULL; + } + + PcapRemoteDeviceList* resultList = new PcapRemoteDeviceList(); + resultList->setRemoteMachineIpAddress(ipAddress); + resultList->setRemoteMachinePort(port); + resultList->setRemoteAuthentication(remoteAuth); + + pcap_if_t* currInterface = interfaceList; + while (currInterface != NULL) { + PcapRemoteDevice* pNewRemoteDevice = + new PcapRemoteDevice(currInterface, resultList->m_RemoteAuthentication, + resultList->getRemoteMachineIpAddress(), + resultList->getRemoteMachinePort()); + resultList->m_RemoteDeviceList.push_back(pNewRemoteDevice); + currInterface = currInterface->next; + } + + pcap_freealldevs(interfaceList); + return resultList; } -PcapRemoteDevice* PcapRemoteDeviceList::getRemoteDeviceByIP(const std::string& ipAddrAsString) const -{ - IPAddress ipAddr = IPAddress(ipAddrAsString); - if (!ipAddr.isValid()) - { - PCPP_LOG_ERROR("IP address no valid"); - return NULL; - } - - PcapRemoteDevice* result = getRemoteDeviceByIP(ipAddr); - return result; -} +PcapRemoteDevice* PcapRemoteDeviceList::getRemoteDeviceByIP( + const std::string& ipAddrAsString) const { + IPAddress ipAddr = IPAddress(ipAddrAsString); + if (!ipAddr.isValid()) { + PCPP_LOG_ERROR("IP address no valid"); + return NULL; + } -PcapRemoteDevice* PcapRemoteDeviceList::getRemoteDeviceByIP(const IPAddress& ipAddr) const -{ - if (!ipAddr.isValid()) - { - PCPP_LOG_ERROR("IP address not valid"); - return NULL; - } - if (ipAddr.getType() == IPAddress::IPv4AddressType) - { - return getRemoteDeviceByIP(ipAddr.getIPv4()); - } - else //IPAddress::IPv6AddressType - { - return getRemoteDeviceByIP(ipAddr.getIPv6()); - } + PcapRemoteDevice* result = getRemoteDeviceByIP(ipAddr); + return result; } - -PcapRemoteDevice* PcapRemoteDeviceList::getRemoteDeviceByIP(const IPv4Address& ip4Addr) const -{ - PCPP_LOG_DEBUG("Searching all remote devices in list..."); - for(ConstRemoteDeviceListIterator devIter = m_RemoteDeviceList.begin(); devIter != m_RemoteDeviceList.end(); devIter++) - { - PCPP_LOG_DEBUG("Searching device '" << (*devIter)->m_Name << "'. Searching all addresses..."); - for(std::vector::iterator addrIter = (*devIter)->m_Addresses.begin(); addrIter != (*devIter)->m_Addresses.end(); addrIter++) - { - if (Logger::getInstance().isDebugEnabled(PcapLogModuleRemoteDevice) && addrIter->addr != NULL) - { - char addrAsString[INET6_ADDRSTRLEN]; - internal::sockaddr2string(addrIter->addr, addrAsString); - PCPP_LOG_DEBUG("Searching address " << addrAsString); - } - - in_addr* currAddr = internal::sockaddr2in_addr(addrIter->addr); - if (currAddr == NULL) - { - PCPP_LOG_DEBUG("Address is NULL"); - continue; - } - - if (currAddr->s_addr == ip4Addr.toInt()) - { - PCPP_LOG_DEBUG("Found matched address!"); - return (*devIter); - } - } - } - - return NULL; - +PcapRemoteDevice* +PcapRemoteDeviceList::getRemoteDeviceByIP(const IPAddress& ipAddr) const { + if (!ipAddr.isValid()) { + PCPP_LOG_ERROR("IP address not valid"); + return NULL; + } + if (ipAddr.getType() == IPAddress::IPv4AddressType) { + return getRemoteDeviceByIP(ipAddr.getIPv4()); + } else // IPAddress::IPv6AddressType + { + return getRemoteDeviceByIP(ipAddr.getIPv6()); + } } -PcapRemoteDevice* PcapRemoteDeviceList::getRemoteDeviceByIP(const IPv6Address& ip6Addr) const -{ - PCPP_LOG_DEBUG("Searching all remote devices in list..."); - for(ConstRemoteDeviceListIterator devIter = m_RemoteDeviceList.begin(); devIter != m_RemoteDeviceList.end(); devIter++) - { - PCPP_LOG_DEBUG("Searching device '" << (*devIter)->m_Name << "'. Searching all addresses..."); - for(std::vector::iterator addrIter = (*devIter)->m_Addresses.begin(); addrIter != (*devIter)->m_Addresses.end(); addrIter++) - { - if (Logger::getInstance().isDebugEnabled(PcapLogModuleRemoteDevice) && addrIter->addr != NULL) - { - char addrAsString[INET6_ADDRSTRLEN]; - internal::sockaddr2string(addrIter->addr, addrAsString); - PCPP_LOG_DEBUG("Searching address " << addrAsString); - } - - in6_addr* currAddr = internal::sockaddr2in6_addr(addrIter->addr); - if (currAddr == NULL) - { - PCPP_LOG_DEBUG("Address is NULL"); - continue; - } - - uint8_t* addrAsArr; size_t addrLen; - ip6Addr.copyTo(&addrAsArr, addrLen); - if (memcmp(currAddr, addrAsArr, sizeof(struct in6_addr)) == 0) - { - PCPP_LOG_DEBUG("Found matched address!"); - delete [] addrAsArr; - return (*devIter); - } - delete [] addrAsArr; - } - } - - return NULL; +PcapRemoteDevice* +PcapRemoteDeviceList::getRemoteDeviceByIP(const IPv4Address& ip4Addr) const { + PCPP_LOG_DEBUG("Searching all remote devices in list..."); + for (ConstRemoteDeviceListIterator devIter = m_RemoteDeviceList.begin(); + devIter != m_RemoteDeviceList.end(); devIter++) { + PCPP_LOG_DEBUG("Searching device '" << (*devIter)->m_Name + << "'. Searching all addresses..."); + for (std::vector::iterator addrIter = + (*devIter)->m_Addresses.begin(); + addrIter != (*devIter)->m_Addresses.end(); addrIter++) { + if (Logger::getInstance().isDebugEnabled(PcapLogModuleRemoteDevice) && + addrIter->addr != NULL) { + char addrAsString[INET6_ADDRSTRLEN]; + internal::sockaddr2string(addrIter->addr, addrAsString); + PCPP_LOG_DEBUG("Searching address " << addrAsString); + } + + in_addr* currAddr = internal::sockaddr2in_addr(addrIter->addr); + if (currAddr == NULL) { + PCPP_LOG_DEBUG("Address is NULL"); + continue; + } + + if (currAddr->s_addr == ip4Addr.toInt()) { + PCPP_LOG_DEBUG("Found matched address!"); + return (*devIter); + } + } + } + + return NULL; +} +PcapRemoteDevice* +PcapRemoteDeviceList::getRemoteDeviceByIP(const IPv6Address& ip6Addr) const { + PCPP_LOG_DEBUG("Searching all remote devices in list..."); + for (ConstRemoteDeviceListIterator devIter = m_RemoteDeviceList.begin(); + devIter != m_RemoteDeviceList.end(); devIter++) { + PCPP_LOG_DEBUG("Searching device '" << (*devIter)->m_Name + << "'. Searching all addresses..."); + for (std::vector::iterator addrIter = + (*devIter)->m_Addresses.begin(); + addrIter != (*devIter)->m_Addresses.end(); addrIter++) { + if (Logger::getInstance().isDebugEnabled(PcapLogModuleRemoteDevice) && + addrIter->addr != NULL) { + char addrAsString[INET6_ADDRSTRLEN]; + internal::sockaddr2string(addrIter->addr, addrAsString); + PCPP_LOG_DEBUG("Searching address " << addrAsString); + } + + in6_addr* currAddr = internal::sockaddr2in6_addr(addrIter->addr); + if (currAddr == NULL) { + PCPP_LOG_DEBUG("Address is NULL"); + continue; + } + + uint8_t* addrAsArr; + size_t addrLen; + ip6Addr.copyTo(&addrAsArr, addrLen); + if (memcmp(currAddr, addrAsArr, sizeof(struct in6_addr)) == 0) { + PCPP_LOG_DEBUG("Found matched address!"); + delete[] addrAsArr; + return (*devIter); + } + delete[] addrAsArr; + } + } + + return NULL; } -void PcapRemoteDeviceList::setRemoteMachineIpAddress(const IPAddress& ipAddress) -{ - if (!ipAddress.isValid()) - { - PCPP_LOG_ERROR("Trying to set an invalid IP address to PcapRemoteDeviceList"); - return; - } +void PcapRemoteDeviceList::setRemoteMachineIpAddress( + const IPAddress& ipAddress) { + if (!ipAddress.isValid()) { + PCPP_LOG_ERROR( + "Trying to set an invalid IP address to PcapRemoteDeviceList"); + return; + } - m_RemoteMachineIpAddress = ipAddress; + m_RemoteMachineIpAddress = ipAddress; } -void PcapRemoteDeviceList::setRemoteMachinePort(uint16_t port) -{ - m_RemoteMachinePort = port; +void PcapRemoteDeviceList::setRemoteMachinePort(uint16_t port) { + m_RemoteMachinePort = port; } -void PcapRemoteDeviceList::setRemoteAuthentication(const PcapRemoteAuthentication* remoteAuth) -{ - if (remoteAuth != NULL) - m_RemoteAuthentication = new PcapRemoteAuthentication(*remoteAuth); - else - { - if (m_RemoteAuthentication != NULL) - delete m_RemoteAuthentication; - m_RemoteAuthentication = NULL; - } +void PcapRemoteDeviceList::setRemoteAuthentication( + const PcapRemoteAuthentication* remoteAuth) { + if (remoteAuth != NULL) + m_RemoteAuthentication = new PcapRemoteAuthentication(*remoteAuth); + else { + if (m_RemoteAuthentication != NULL) + delete m_RemoteAuthentication; + m_RemoteAuthentication = NULL; + } } -PcapRemoteDeviceList::~PcapRemoteDeviceList() -{ - while (m_RemoteDeviceList.size() > 0) - { - RemoteDeviceListIterator devIter = m_RemoteDeviceList.begin(); - delete (*devIter); - m_RemoteDeviceList.erase(devIter); - } - - if (m_RemoteAuthentication != NULL) - { - delete m_RemoteAuthentication; - } +PcapRemoteDeviceList::~PcapRemoteDeviceList() { + while (m_RemoteDeviceList.size() > 0) { + RemoteDeviceListIterator devIter = m_RemoteDeviceList.begin(); + delete (*devIter); + m_RemoteDeviceList.erase(devIter); + } + + if (m_RemoteAuthentication != NULL) { + delete m_RemoteAuthentication; + } } } // namespace pcpp diff --git a/Pcap++/src/PfRingDevice.cpp b/Pcap++/src/PfRingDevice.cpp index 12bb910714..0bef39ea83 100644 --- a/Pcap++/src/PfRingDevice.cpp +++ b/Pcap++/src/PfRingDevice.cpp @@ -6,882 +6,864 @@ #include "PfRingDevice.h" #include "EthLayer.h" -#include "VlanLayer.h" #include "Logger.h" +#include "VlanLayer.h" +#include #include #include #include -#include - #define DEFAULT_PF_RING_SNAPLEN 1600 -namespace pcpp -{ - - -PfRingDevice::PfRingDevice(const char* deviceName) : m_MacAddress(MacAddress::Zero) -{ - m_NumOfOpenedRxChannels = 0; - m_DeviceOpened = false; - m_DeviceName = std::string(deviceName); - m_InterfaceIndex = -1; - m_StopThread = true; - m_OnPacketsArriveCallback = NULL; - m_OnPacketsArriveUserCookie = NULL; - m_ReentrantMode = false; - m_HwClockEnabled = false; - m_DeviceMTU = 0; - m_IsFilterCurrentlySet = false; - - m_PfRingDescriptors = new pfring*[MAX_NUM_RX_CHANNELS]; -} - -PfRingDevice::~PfRingDevice() -{ - close(); - delete [] m_PfRingDescriptors; -} - - -bool PfRingDevice::open() -{ - if (m_DeviceOpened) - { - PCPP_LOG_ERROR("Device already opened"); - return false; - } - - m_NumOfOpenedRxChannels = 0; - - PCPP_LOG_DEBUG("Trying to open device [" << m_DeviceName << "]"); - int res = openSingleRxChannel(m_DeviceName.c_str(), &m_PfRingDescriptors[0]); - if (res == 0) - { - PCPP_LOG_DEBUG("Succeeded opening device [" << m_DeviceName << "]"); - m_NumOfOpenedRxChannels = 1; - m_DeviceOpened = true; - return true; - } - else if (res == 1) - PCPP_LOG_ERROR("Couldn't open a ring on device [" << m_DeviceName << "]"); - else if (res == 2) - PCPP_LOG_ERROR("Unable to enable ring for device [" << m_DeviceName << "]"); - - return false; -} - - -bool PfRingDevice::openSingleRxChannel(uint8_t channelId) -{ - uint8_t channelIds[1] = { channelId }; - return openMultiRxChannels(channelIds, 1); -} - -int PfRingDevice::openSingleRxChannel(const char* deviceName, pfring** ring) -{ - if (m_DeviceOpened) - { - PCPP_LOG_ERROR("Device already opened"); - return false; - } - - uint32_t flags = PF_RING_PROMISC | PF_RING_HW_TIMESTAMP | PF_RING_DNA_SYMMETRIC_RSS; - *ring = pfring_open(deviceName, DEFAULT_PF_RING_SNAPLEN, flags); - - if (*ring == NULL) - { - return 1; - } - PCPP_LOG_DEBUG("pfring_open Succeeded for device [" << m_DeviceName << "]"); - - if (getIsHwClockEnable()) - { - setPfRingDeviceClock(*ring); - PCPP_LOG_DEBUG("H/W clock set for device [" << m_DeviceName << "]"); - } - - if (pfring_enable_rss_rehash(*ring) < 0 || pfring_enable_ring(*ring) < 0) - { - pfring_close(*ring); - return 2; - } - - PCPP_LOG_DEBUG("pfring enabled for device [" << m_DeviceName << "]"); - - return 0; -} - -bool PfRingDevice::setPfRingDeviceClock(pfring* ring) -{ - struct timespec ltime; - if (clock_gettime(CLOCK_REALTIME, <ime) != 0) - { - PCPP_LOG_ERROR("Could not set pfring devices clock, clock_gettime failed"); - return false; - } - - if (pfring_set_device_clock(ring, <ime) < 0) - { - PCPP_LOG_DEBUG("Could not set pfring devices clock, pfring_set_device_clock failed"); - return false; - } - - return true; -} - -bool PfRingDevice::openMultiRxChannels(const uint8_t* channelIds, int numOfChannelIds) -{ - if (m_DeviceOpened) - { - PCPP_LOG_ERROR("Device already opened"); - return false; - } - - // I needed to add this verification because PF_RING doesn't provide it. - // It allows opening the device on a channel that doesn't exist, but of course no packets will be captured - uint8_t totalChannels = getTotalNumOfRxChannels(); - for (int i = 0; i < numOfChannelIds; i++) - { - uint8_t channelId = channelIds[i]; - if (channelId >= totalChannels) - { - PCPP_LOG_ERROR("Trying to open the device with a RX channel that doesn't exist. Total RX channels are [" << (int)totalChannels << "], tried to open channel [" << (int)channelId << "]"); - return false; - } - } - - m_NumOfOpenedRxChannels = 0; - - for (int i = 0; i < numOfChannelIds; i++) - { - uint8_t channelId = channelIds[i]; - std::ostringstream ringNameStream; - ringNameStream << m_DeviceName << "@" << (int)channelId; - std::string ringName = ringNameStream.str(); - PCPP_LOG_DEBUG("Trying to open device [" << m_DeviceName << "] on channel [" << channelId << "]. Channel name [" << ringName << "]"); - int res = openSingleRxChannel(ringName.c_str(), &m_PfRingDescriptors[i]); - if (res == 0) - { - PCPP_LOG_DEBUG("Succeeded opening device [" << m_DeviceName << "] on channel [" << channelId << "]. Channel name [" << ringName << "]"); - m_NumOfOpenedRxChannels++; - continue; - } - else if (res == 1) - PCPP_LOG_ERROR("Couldn't open a ring on channel [" << (int)channelId << "] for device [" << m_DeviceName << "]"); - else if (res == 2) - PCPP_LOG_ERROR("Unable to enable ring on channel [" << (int)channelId << "] for device [" << m_DeviceName << "]"); - - break; - } - - if (m_NumOfOpenedRxChannels < numOfChannelIds) - { - // if an error occurred, close all rings from index=0 to index=m_NumOfOpenedRxChannels-1 - // there's no need to close m_PfRingDescriptors[m_NumOfOpenedRxChannels] because it has already been - // closed by openSingleRxChannel - for (int i = 0; i < m_NumOfOpenedRxChannels-1; i++) - { - pfring_close(m_PfRingDescriptors[i]); - } - - m_NumOfOpenedRxChannels = 0; - return false; - } - - m_DeviceOpened = true; - - return true; -} - -bool PfRingDevice::openMultiRxChannels(uint8_t numOfRxChannelsToOpen, ChannelDistribution dist) -{ - if (m_DeviceOpened) - { - PCPP_LOG_ERROR("Device already opened"); - return false; - } - - m_NumOfOpenedRxChannels = 0; - - if (numOfRxChannelsToOpen > MAX_NUM_RX_CHANNELS) - { - PCPP_LOG_ERROR("Cannot open more than [" << MAX_NUM_RX_CHANNELS << "] channels"); - return false; - } - - uint32_t flags = PF_RING_PROMISC | PF_RING_REENTRANT | PF_RING_HW_TIMESTAMP | PF_RING_DNA_SYMMETRIC_RSS; - - uint8_t numOfRxChannelsOnNIC = getTotalNumOfRxChannels(); - PCPP_LOG_DEBUG("NIC has " << (int)numOfRxChannelsOnNIC << " RX channels"); - - uint8_t numOfRingsPerRxChannel = numOfRxChannelsToOpen / numOfRxChannelsOnNIC; - uint8_t remainderRings = numOfRxChannelsToOpen % numOfRxChannelsOnNIC; - - cluster_type clusterType = (dist == RoundRobin) ? cluster_round_robin : cluster_per_flow; - - int ringsOpen = 0; - for (uint8_t channelId = 0; channelId < numOfRxChannelsOnNIC; channelId++) - { - // no more channels to open - if (numOfRingsPerRxChannel == 0 && remainderRings == 0) - break; - - std::ostringstream ringName; - ringName << m_DeviceName << "@" << (int)channelId; - - // open numOfRingsPerRxChannel rings per RX channel - for (uint8_t ringId = 0; ringId < numOfRingsPerRxChannel; ringId++) - { - m_PfRingDescriptors[ringsOpen] = pfring_open(ringName.str().c_str(), DEFAULT_PF_RING_SNAPLEN, flags); - if (m_PfRingDescriptors[ringsOpen] == NULL) - { - PCPP_LOG_ERROR("Couldn't open a ring on channel [" << (int)channelId << "]"); - break; - } - - // setting a cluster for all rings in the same channel to enable hashing between them - if (pfring_set_cluster(m_PfRingDescriptors[ringsOpen], channelId+1, clusterType) < 0) - { - PCPP_LOG_ERROR("Couldn't set ring [" << (int)ringId << "] in channel [" << (int)channelId << "] to the cluster [" << (int)(channelId+1) << "]"); - break; - } - - ringsOpen++; - } - - // open one more ring if remainder > 0 - if (remainderRings > 0) - { - m_PfRingDescriptors[ringsOpen] = pfring_open(ringName.str().c_str(), DEFAULT_PF_RING_SNAPLEN, flags); - if (m_PfRingDescriptors[ringsOpen] == NULL) - { - PCPP_LOG_ERROR("Couldn't open a ring on channel [" << (int)channelId << "]"); - break; - } - - // setting a cluster for all rings in the same channel to enable hashing between them - if (pfring_set_cluster(m_PfRingDescriptors[ringsOpen], channelId+1, clusterType) < 0) - { - PCPP_LOG_ERROR("Couldn't set ring [" << (int)(numOfRingsPerRxChannel+1) << "] in channel [" << (int)channelId << "] to the cluster [" << (int)(channelId+1) << "]"); - break; - } - - ringsOpen++; - remainderRings--; - PCPP_LOG_DEBUG("Opened " << (int)(numOfRingsPerRxChannel+1) << " rings on channel [" << (int)channelId << "]"); - } - else - PCPP_LOG_DEBUG("Opened " << (int)numOfRingsPerRxChannel << " rings on channel [" << (int)channelId << "]"); - } - - if (ringsOpen < numOfRxChannelsToOpen) - { - for (uint8_t i = 0; i < ringsOpen; i++) - pfring_close(m_PfRingDescriptors[i]); - return false; - } - - if (getIsHwClockEnable()) - { - for (int i = 0; i < ringsOpen; i++) - { - if (setPfRingDeviceClock(m_PfRingDescriptors[i])) - PCPP_LOG_DEBUG("H/W clock set for device [" << m_DeviceName << "]"); - } - } - - // enable all rings - for (int i = 0; i < ringsOpen; i++) - { - if (pfring_enable_rss_rehash(m_PfRingDescriptors[i]) < 0 || pfring_enable_ring(m_PfRingDescriptors[i]) < 0) - { - PCPP_LOG_ERROR("Unable to enable ring [" << i << "] for device [" << m_DeviceName << "]"); - // close all pfring's that were enabled until now - for (int j = 0; j 0) - { - uint8_t res = pfring_get_num_rx_channels(m_PfRingDescriptors[0]); - return res; - } - else - { - uint32_t flags = PF_RING_PROMISC | PF_RING_REENTRANT | PF_RING_HW_TIMESTAMP | PF_RING_DNA_SYMMETRIC_RSS; - pfring* ring = pfring_open(m_DeviceName.c_str(), DEFAULT_PF_RING_SNAPLEN, flags); - uint8_t res = pfring_get_num_rx_channels(ring); - pfring_close(ring); - return res; - } -} - - -SystemCore PfRingDevice::getCurrentCoreId() const -{ - return SystemCores::IdToSystemCore[sched_getcpu()]; -} - - -bool PfRingDevice::setFilter(std::string filterAsString) -{ - if (!m_DeviceOpened) - { - PCPP_LOG_ERROR("Device not opened"); - return false; - } - - for (int i = 0; i < m_NumOfOpenedRxChannels; i++) - { - int res = pfring_set_bpf_filter(m_PfRingDescriptors[i], (char*)filterAsString.c_str()); - if(res < 0) - { - if (res == PF_RING_ERROR_NOT_SUPPORTED) - PCPP_LOG_ERROR("BPF filtering isn't supported on current PF_RING version. Please re-compile PF_RING with the --enable-bpf flag"); - else - PCPP_LOG_ERROR("Couldn't set filter '" << filterAsString << "'"); - return false; - } - } - - m_IsFilterCurrentlySet = true; - - PCPP_LOG_DEBUG("Successfully set filter '" << filterAsString << "'"); - return true; -} - - -bool PfRingDevice::clearFilter() -{ - if (!m_IsFilterCurrentlySet) - return true; - - for (int i = 0; i < m_NumOfOpenedRxChannels; i++) - { - int res = pfring_remove_bpf_filter(m_PfRingDescriptors[i]); - if(res < 0) - { - PCPP_LOG_ERROR("Couldn't remove filter"); - return false; - } - } - - m_IsFilterCurrentlySet = false; - - PCPP_LOG_DEBUG("Successfully removed filter from all open RX channels"); - return true; -} - - -bool PfRingDevice::isFilterCurrentlySet() const -{ - return m_IsFilterCurrentlySet; -} - - -void PfRingDevice::close() -{ - for (int i = 0; i < m_NumOfOpenedRxChannels; i++) - pfring_close(m_PfRingDescriptors[i]); - m_DeviceOpened = false; - clearCoreConfiguration(); - m_NumOfOpenedRxChannels = 0; - m_IsFilterCurrentlySet = false; - PCPP_LOG_DEBUG("Device [" << m_DeviceName << "] closed"); -} - -bool PfRingDevice::initCoreConfigurationByCoreMask(CoreMask coreMask) -{ - int i = 0; - int numOfCores = getNumOfCores(); - clearCoreConfiguration(); - while ((coreMask != 0) && (i < numOfCores)) - { - if (coreMask & 1) - { - m_CoreConfiguration[i].IsInUse = true; - } - - coreMask = coreMask >> 1; - i++; - } - - if (coreMask != 0) // this mean coreMask contains a core that doesn't exist - { - PCPP_LOG_ERROR("Trying to use a core [" << i << "] that doesn't exist while machine has " << numOfCores << " cores"); - clearCoreConfiguration(); - return false; - } - - return true; -} - -bool PfRingDevice::startCaptureMultiThread(OnPfRingPacketsArriveCallback onPacketsArrive, void* onPacketsArriveUserCookie, CoreMask coreMask) -{ - if (!m_StopThread) - { - PCPP_LOG_ERROR("Device already capturing. Cannot start 2 capture sessions at the same time"); - return false; - } - - if (!initCoreConfigurationByCoreMask(coreMask)) - return false; - - if (m_NumOfOpenedRxChannels != getCoresInUseCount()) - { - PCPP_LOG_ERROR("Cannot use a different number of channels and cores. Opened " << m_NumOfOpenedRxChannels << " channels but set " << getCoresInUseCount() << " cores in core mask"); - clearCoreConfiguration(); - return false; - } - - std::mutex mutex; - std::condition_variable cond; - int startThread = 0; - - m_StopThread = false; - int rxChannel = 0; - for (int coreId = 0; coreId < MAX_NUM_OF_CORES; coreId++) - { - if (!m_CoreConfiguration[coreId].IsInUse) - continue; - - m_ReentrantMode = true; - - m_OnPacketsArriveCallback = onPacketsArrive; - m_OnPacketsArriveUserCookie = onPacketsArriveUserCookie; - - // create a new thread - m_CoreConfiguration[coreId].Channel = m_PfRingDescriptors[rxChannel++]; - m_CoreConfiguration[coreId].RxThread = std::thread(&pcpp::PfRingDevice::captureThreadMain, this, &cond, &mutex, &startThread); - - // set affinity to cores - cpu_set_t cpuset; - CPU_ZERO(&cpuset); - CPU_SET(coreId, &cpuset); - int err = pthread_setaffinity_np(m_CoreConfiguration[coreId].RxThread.native_handle(), sizeof(cpu_set_t), &cpuset); - if(err != 0) - { - PCPP_LOG_ERROR("Error while binding thread to core " << coreId << ": errno=" << err); - startThread = 1; - clearCoreConfiguration(); - return false; - } - } - - startThread = 2; - cond.notify_all(); - - return true; -} - -bool PfRingDevice::startCaptureSingleThread(OnPfRingPacketsArriveCallback onPacketsArrive, void* onPacketsArriveUserCookie) -{ - if (!m_StopThread) - { - PCPP_LOG_ERROR("Device already capturing. Cannot start 2 capture sessions at the same time"); - return false; - } - - if (m_NumOfOpenedRxChannels != 1) - { - PCPP_LOG_ERROR("Cannot start capturing on a single thread when more than 1 RX channel is opened"); - return false; - } - - PCPP_LOG_DEBUG("Trying to start capturing on a single thread for device [" << m_DeviceName << "]"); - - clearCoreConfiguration(); - - m_OnPacketsArriveCallback = onPacketsArrive; - m_OnPacketsArriveUserCookie = onPacketsArriveUserCookie; - - m_StopThread = false; - - m_ReentrantMode = false; - - std::mutex mutex; - std::condition_variable cond; - int startThread = 0; - - cpu_set_t cpuset; - CPU_ZERO(&cpuset); - CPU_SET(0, &cpuset); - m_CoreConfiguration[0].IsInUse = true; - m_CoreConfiguration[0].Channel = m_PfRingDescriptors[0]; - m_CoreConfiguration[0].RxThread = std::thread(&pcpp::PfRingDevice::captureThreadMain, this, &cond, &mutex, &startThread); - m_CoreConfiguration[0].IsAffinitySet = false; - int err = pthread_setaffinity_np(m_CoreConfiguration[0].RxThread.native_handle(), sizeof(cpu_set_t), &cpuset); - if(err != 0) - { - startThread = 1; - PCPP_LOG_ERROR("Error while binding thread to core 0: errno=" << err); - clearCoreConfiguration(); - return false; - } - startThread = 2; - cond.notify_all(); - - PCPP_LOG_DEBUG("Capturing started for device [" << m_DeviceName << "]"); - return true; -} - -void PfRingDevice::stopCapture() -{ - PCPP_LOG_DEBUG("Trying to stop capturing on device [" << m_DeviceName << "]"); - m_StopThread = true; - for (int coreId = 0; coreId < MAX_NUM_OF_CORES; coreId++) - { - if (!m_CoreConfiguration[coreId].IsInUse) - continue; - m_CoreConfiguration[coreId].RxThread.join(); - PCPP_LOG_DEBUG("Thread on core [" << coreId << "] stopped"); - } - - PCPP_LOG_DEBUG("All capturing threads stopped"); -} - -void PfRingDevice::captureThreadMain(std::condition_variable* startCond, std::mutex* startMutex, const int* startState) -{ - while (*startState == 0) - { - std::unique_lock lock(*startMutex); - startCond->wait_for(lock, std::chrono::milliseconds(100)); - } - if (*startState == 1) - { - return; - } - - int coreId = this->getCurrentCoreId().Id; - pfring* ring = NULL; - - PCPP_LOG_DEBUG("Starting capture thread " << coreId); - - ring = this->m_CoreConfiguration[coreId].Channel; - - if (ring == NULL) - { - PCPP_LOG_ERROR("Couldn't find ring for core " << coreId << ". Exiting capture thread"); - return; - } - - while (!this->m_StopThread) - { - // if buffer is NULL PF_RING avoids copy of the data - uint8_t* buffer = NULL; - uint32_t bufferLen = 0; - - // in multi-threaded mode flag PF_RING_REENTRANT is set, and this flag doesn't work with zero copy - // so I need to allocate a buffer and set buffer to point to it - if (this->m_ReentrantMode) - { - uint8_t tempBuffer[PCPP_MAX_PACKET_SIZE]; - buffer = tempBuffer; - bufferLen = PCPP_MAX_PACKET_SIZE; - } - - struct pfring_pkthdr pktHdr; - int recvRes = pfring_recv(ring, &buffer, bufferLen, &pktHdr, 0); - if (recvRes > 0) - { - // if caplen < len it means we don't have the whole packet. Treat this case as packet drop - // TODO: add this packet to dropped packet stats -// if (pktHdr.caplen != pktHdr.len) -// { -// PCPP_LOG_ERROR("Packet dropped due to len != caplen"); -// continue; -// } - - RawPacket rawPacket(buffer, pktHdr.caplen, pktHdr.ts, false); - this->m_OnPacketsArriveCallback(&rawPacket, 1, coreId, this, this->m_OnPacketsArriveUserCookie); - } - else if (recvRes < 0) - { - // cppcheck-suppress shiftNegative - PCPP_LOG_ERROR("pfring_recv returned an error: [Err=" << recvRes << "]"); - } - } - - PCPP_LOG_DEBUG("Exiting capture thread " << coreId); -} - -void PfRingDevice::getThreadStatistics(SystemCore core, PfRingStats& stats) const -{ - pfring* ring = NULL; - uint8_t coreId = core.Id; - - ring = m_CoreConfiguration[coreId].Channel; - - if (ring != NULL) - { - pfring_stat tempStats; - if (pfring_stats(ring, &tempStats) < 0) - { - PCPP_LOG_ERROR("Can't retrieve statistics for core [" << (int)coreId << "], pfring_stats failed"); - return; - } - stats.drop = (uint64_t)tempStats.drop; - stats.recv = (uint64_t)tempStats.recv; - } - else - { - PCPP_LOG_ERROR("Core [" << (int)coreId << "] is not in use, can't retrieve statistics"); - } -} - -void PfRingDevice::getCurrentThreadStatistics(PfRingStats& stats) const -{ - getThreadStatistics(getCurrentCoreId(), stats); -} - -void PfRingDevice::getStatistics(PfRingStats& stats) const -{ - stats.drop = 0; - stats.recv = 0; - - for (int coreId = 0; coreId < MAX_NUM_OF_CORES; coreId++) - { - if (!m_CoreConfiguration[coreId].IsInUse) - continue; - - PfRingStats tempStat = {}; - getThreadStatistics(SystemCores::IdToSystemCore[coreId], tempStat); - stats.drop += tempStat.drop; - stats.recv += tempStat.recv; - - if (!m_CoreConfiguration[coreId].IsAffinitySet) - break; - } -} - -void PfRingDevice::clearCoreConfiguration() -{ - for (int i = 0; i < MAX_NUM_OF_CORES; i++) - m_CoreConfiguration[i].clear(); -} - -int PfRingDevice::getCoresInUseCount() const -{ - int res = 0; - for (int i = 0; i < MAX_NUM_OF_CORES; i++) - if (m_CoreConfiguration[i].IsInUse) - res++; - - return res; -} - -void PfRingDevice::setPfRingDeviceAttributes() -{ - if (m_InterfaceIndex > -1) - return; - - pfring* ring = NULL; - bool closeRing = false; - if (m_NumOfOpenedRxChannels > 0) - ring = m_PfRingDescriptors[0]; - else - { - uint32_t flags = PF_RING_PROMISC | PF_RING_DNA_SYMMETRIC_RSS; - ring = pfring_open(m_DeviceName.c_str(), DEFAULT_PF_RING_SNAPLEN, flags); - closeRing = true; - } - - if (ring == NULL) - { - PCPP_LOG_ERROR("Could not open a pfring for setting device attributes: MAC address, interface index and HW clock"); - return; - } - - // set device MAC address - - uint8_t macAddress[6]; - if (pfring_get_bound_device_address(ring, macAddress) < 0) - PCPP_LOG_ERROR("Unable to read the device MAC address for interface '" << m_DeviceName << "'"); - else - m_MacAddress = MacAddress(macAddress); - - // set interface ID - if (pfring_get_bound_device_ifindex(ring, &m_InterfaceIndex) < 0) - PCPP_LOG_ERROR("Unable to read interface index of device"); - - // try to set hardware device clock - m_HwClockEnabled = setPfRingDeviceClock(ring); - - // set interface MTU - int mtu = pfring_get_mtu_size(ring); - if (mtu < 0) - // cppcheck-suppress shiftNegative - PCPP_LOG_ERROR("Could not get MTU. pfring_get_mtu_size returned an error: " << mtu); - else - m_DeviceMTU = mtu + sizeof(ether_header) + sizeof(vlan_header); - - if (Logger::getInstance().isDebugEnabled(PcapLogModulePfRingDevice)) - { - std::string hwEnabled = (m_HwClockEnabled ? "enabled" : "disabled"); - PCPP_LOG_DEBUG("Capturing from " << m_DeviceName << " [" << m_MacAddress << "][ifIndex: " << m_InterfaceIndex << "][MTU: " << m_DeviceMTU << "], HW clock " << hwEnabled); - } - - - if (closeRing) - pfring_close(ring); -} - - -bool PfRingDevice::sendData(const uint8_t* packetData, int packetDataLength, bool flushTxQueues) -{ - if (!m_DeviceOpened) - { - PCPP_LOG_ERROR("Device is not opened. Cannot send packets"); - return false; - } - - uint8_t flushTxAsUint = (flushTxQueues? 1 : 0); - - #define MAX_TRIES 5 - - int tries = 0; - int res = 0; - while (tries < MAX_TRIES) - { - // don't allow sending of data larger than the MTU, otherwise pfring_send will fail - if (packetDataLength > m_DeviceMTU) - packetDataLength = m_DeviceMTU; - - // if the device is opened, m_PfRingDescriptors[0] will always be set and enables - res = pfring_send(m_PfRingDescriptors[0], (char*)packetData, packetDataLength, flushTxAsUint); - - // res == -1 means it's an error coming from "sendto" which is the Linux API PF_RING is using to send packets - // errno == ENOBUFS means write buffer is full. PF_RING driver expects the userspace to handle this case - // My implementation is to sleep for 10 usec and try again - if (res == -1 && errno == ENOBUFS) - { - tries++; - PCPP_LOG_DEBUG("Try #" << tries << ": Got ENOBUFS (write buffer full) error while sending packet. Sleeping 20 usec and trying again"); - usleep(2000); - } - else - break; - } - - if (tries >= MAX_TRIES) - { - PCPP_LOG_ERROR("Tried to send data " << MAX_TRIES << " times but write buffer is full"); - return false; - } - - if (res < 0) - { - // res == -1 means it's an error coming from "sendto" which is the Linux API PF_RING is using to send packets - if (res == -1) - PCPP_LOG_ERROR("Error sending packet: Linux errno: " << strerror(errno) << " [" << errno << "]"); - else - PCPP_LOG_ERROR("Error sending packet: pfring_send returned an error: " << res << " , errno: " << strerror(errno)<< " [" << errno << "]"); - return false; - } else if (res != packetDataLength) - { - PCPP_LOG_ERROR("Couldn't send all bytes, only " << res << " bytes out of " << packetDataLength << " bytes were sent"); - return false; - } - - return true; -} - -bool PfRingDevice::sendPacket(const uint8_t* packetData, int packetDataLength) -{ - return sendData(packetData, packetDataLength, true); -} - - -bool PfRingDevice::sendPacket(const RawPacket& rawPacket) -{ - return sendData(rawPacket.getRawData(), rawPacket.getRawDataLen(), true); -} - - -bool PfRingDevice::sendPacket(const Packet& packet) -{ - return sendData(packet.getRawPacketReadOnly()->getRawData(), packet.getRawPacketReadOnly()->getRawDataLen(), true); -} - - -int PfRingDevice::sendPackets(const RawPacket* rawPacketsArr, int arrLength) -{ - int packetsSent = 0; - for (int i = 0; i < arrLength; i++) - { - if (!sendData(rawPacketsArr[i].getRawData(), rawPacketsArr[i].getRawDataLen(), false)) - break; - else - packetsSent++; - } +namespace pcpp { - // The following method isn't supported in PF_RING aware drivers, probably only in DNA and ZC - pfring_flush_tx_packets(m_PfRingDescriptors[0]); +PfRingDevice::PfRingDevice(const char* deviceName) + : m_MacAddress(MacAddress::Zero) { + m_NumOfOpenedRxChannels = 0; + m_DeviceOpened = false; + m_DeviceName = std::string(deviceName); + m_InterfaceIndex = -1; + m_StopThread = true; + m_OnPacketsArriveCallback = NULL; + m_OnPacketsArriveUserCookie = NULL; + m_ReentrantMode = false; + m_HwClockEnabled = false; + m_DeviceMTU = 0; + m_IsFilterCurrentlySet = false; + + m_PfRingDescriptors = new pfring*[MAX_NUM_RX_CHANNELS]; +} + +PfRingDevice::~PfRingDevice() { + close(); + delete[] m_PfRingDescriptors; +} + +bool PfRingDevice::open() { + if (m_DeviceOpened) { + PCPP_LOG_ERROR("Device already opened"); + return false; + } + + m_NumOfOpenedRxChannels = 0; + + PCPP_LOG_DEBUG("Trying to open device [" << m_DeviceName << "]"); + int res = openSingleRxChannel(m_DeviceName.c_str(), &m_PfRingDescriptors[0]); + if (res == 0) { + PCPP_LOG_DEBUG("Succeeded opening device [" << m_DeviceName << "]"); + m_NumOfOpenedRxChannels = 1; + m_DeviceOpened = true; + return true; + } else if (res == 1) + PCPP_LOG_ERROR("Couldn't open a ring on device [" << m_DeviceName << "]"); + else if (res == 2) + PCPP_LOG_ERROR("Unable to enable ring for device [" << m_DeviceName << "]"); + + return false; +} + +bool PfRingDevice::openSingleRxChannel(uint8_t channelId) { + uint8_t channelIds[1] = {channelId}; + return openMultiRxChannels(channelIds, 1); +} + +int PfRingDevice::openSingleRxChannel(const char* deviceName, pfring** ring) { + if (m_DeviceOpened) { + PCPP_LOG_ERROR("Device already opened"); + return false; + } + + uint32_t flags = + PF_RING_PROMISC | PF_RING_HW_TIMESTAMP | PF_RING_DNA_SYMMETRIC_RSS; + *ring = pfring_open(deviceName, DEFAULT_PF_RING_SNAPLEN, flags); + + if (*ring == NULL) { + return 1; + } + PCPP_LOG_DEBUG("pfring_open Succeeded for device [" << m_DeviceName << "]"); + + if (getIsHwClockEnable()) { + setPfRingDeviceClock(*ring); + PCPP_LOG_DEBUG("H/W clock set for device [" << m_DeviceName << "]"); + } + + if (pfring_enable_rss_rehash(*ring) < 0 || pfring_enable_ring(*ring) < 0) { + pfring_close(*ring); + return 2; + } + + PCPP_LOG_DEBUG("pfring enabled for device [" << m_DeviceName << "]"); + + return 0; +} + +bool PfRingDevice::setPfRingDeviceClock(pfring* ring) { + struct timespec ltime; + if (clock_gettime(CLOCK_REALTIME, <ime) != 0) { + PCPP_LOG_ERROR("Could not set pfring devices clock, clock_gettime failed"); + return false; + } + + if (pfring_set_device_clock(ring, <ime) < 0) { + PCPP_LOG_DEBUG( + "Could not set pfring devices clock, pfring_set_device_clock failed"); + return false; + } + + return true; +} + +bool PfRingDevice::openMultiRxChannels(const uint8_t* channelIds, + int numOfChannelIds) { + if (m_DeviceOpened) { + PCPP_LOG_ERROR("Device already opened"); + return false; + } + + // I needed to add this verification because PF_RING doesn't provide it. + // It allows opening the device on a channel that doesn't exist, but of course + // no packets will be captured + uint8_t totalChannels = getTotalNumOfRxChannels(); + for (int i = 0; i < numOfChannelIds; i++) { + uint8_t channelId = channelIds[i]; + if (channelId >= totalChannels) { + PCPP_LOG_ERROR("Trying to open the device with a RX channel that doesn't " + "exist. Total RX channels are [" + << (int)totalChannels << "], tried to open channel [" + << (int)channelId << "]"); + return false; + } + } + + m_NumOfOpenedRxChannels = 0; + + for (int i = 0; i < numOfChannelIds; i++) { + uint8_t channelId = channelIds[i]; + std::ostringstream ringNameStream; + ringNameStream << m_DeviceName << "@" << (int)channelId; + std::string ringName = ringNameStream.str(); + PCPP_LOG_DEBUG("Trying to open device [" << m_DeviceName << "] on channel [" + << channelId << "]. Channel name [" + << ringName << "]"); + int res = openSingleRxChannel(ringName.c_str(), &m_PfRingDescriptors[i]); + if (res == 0) { + PCPP_LOG_DEBUG("Succeeded opening device [" + << m_DeviceName << "] on channel [" << channelId + << "]. Channel name [" << ringName << "]"); + m_NumOfOpenedRxChannels++; + continue; + } else if (res == 1) + PCPP_LOG_ERROR("Couldn't open a ring on channel [" + << (int)channelId << "] for device [" << m_DeviceName + << "]"); + else if (res == 2) + PCPP_LOG_ERROR("Unable to enable ring on channel [" + << (int)channelId << "] for device [" << m_DeviceName + << "]"); + + break; + } + + if (m_NumOfOpenedRxChannels < numOfChannelIds) { + // if an error occurred, close all rings from index=0 to + // index=m_NumOfOpenedRxChannels-1 there's no need to close + // m_PfRingDescriptors[m_NumOfOpenedRxChannels] because it has already been + // closed by openSingleRxChannel + for (int i = 0; i < m_NumOfOpenedRxChannels - 1; i++) { + pfring_close(m_PfRingDescriptors[i]); + } + + m_NumOfOpenedRxChannels = 0; + return false; + } + + m_DeviceOpened = true; + + return true; +} + +bool PfRingDevice::openMultiRxChannels(uint8_t numOfRxChannelsToOpen, + ChannelDistribution dist) { + if (m_DeviceOpened) { + PCPP_LOG_ERROR("Device already opened"); + return false; + } + + m_NumOfOpenedRxChannels = 0; + + if (numOfRxChannelsToOpen > MAX_NUM_RX_CHANNELS) { + PCPP_LOG_ERROR("Cannot open more than [" << MAX_NUM_RX_CHANNELS + << "] channels"); + return false; + } + + uint32_t flags = PF_RING_PROMISC | PF_RING_REENTRANT | PF_RING_HW_TIMESTAMP | + PF_RING_DNA_SYMMETRIC_RSS; + + uint8_t numOfRxChannelsOnNIC = getTotalNumOfRxChannels(); + PCPP_LOG_DEBUG("NIC has " << (int)numOfRxChannelsOnNIC << " RX channels"); + + uint8_t numOfRingsPerRxChannel = numOfRxChannelsToOpen / numOfRxChannelsOnNIC; + uint8_t remainderRings = numOfRxChannelsToOpen % numOfRxChannelsOnNIC; + + cluster_type clusterType = + (dist == RoundRobin) ? cluster_round_robin : cluster_per_flow; + + int ringsOpen = 0; + for (uint8_t channelId = 0; channelId < numOfRxChannelsOnNIC; channelId++) { + // no more channels to open + if (numOfRingsPerRxChannel == 0 && remainderRings == 0) + break; + + std::ostringstream ringName; + ringName << m_DeviceName << "@" << (int)channelId; + + // open numOfRingsPerRxChannel rings per RX channel + for (uint8_t ringId = 0; ringId < numOfRingsPerRxChannel; ringId++) { + m_PfRingDescriptors[ringsOpen] = + pfring_open(ringName.str().c_str(), DEFAULT_PF_RING_SNAPLEN, flags); + if (m_PfRingDescriptors[ringsOpen] == NULL) { + PCPP_LOG_ERROR("Couldn't open a ring on channel [" << (int)channelId + << "]"); + break; + } + + // setting a cluster for all rings in the same channel to enable hashing + // between them + if (pfring_set_cluster(m_PfRingDescriptors[ringsOpen], channelId + 1, + clusterType) < 0) { + PCPP_LOG_ERROR("Couldn't set ring [" + << (int)ringId << "] in channel [" << (int)channelId + << "] to the cluster [" << (int)(channelId + 1) << "]"); + break; + } + + ringsOpen++; + } + + // open one more ring if remainder > 0 + if (remainderRings > 0) { + m_PfRingDescriptors[ringsOpen] = + pfring_open(ringName.str().c_str(), DEFAULT_PF_RING_SNAPLEN, flags); + if (m_PfRingDescriptors[ringsOpen] == NULL) { + PCPP_LOG_ERROR("Couldn't open a ring on channel [" << (int)channelId + << "]"); + break; + } + + // setting a cluster for all rings in the same channel to enable hashing + // between them + if (pfring_set_cluster(m_PfRingDescriptors[ringsOpen], channelId + 1, + clusterType) < 0) { + PCPP_LOG_ERROR("Couldn't set ring [" + << (int)(numOfRingsPerRxChannel + 1) << "] in channel [" + << (int)channelId << "] to the cluster [" + << (int)(channelId + 1) << "]"); + break; + } + + ringsOpen++; + remainderRings--; + PCPP_LOG_DEBUG("Opened " << (int)(numOfRingsPerRxChannel + 1) + << " rings on channel [" << (int)channelId + << "]"); + } else + PCPP_LOG_DEBUG("Opened " << (int)numOfRingsPerRxChannel + << " rings on channel [" << (int)channelId + << "]"); + } + + if (ringsOpen < numOfRxChannelsToOpen) { + for (uint8_t i = 0; i < ringsOpen; i++) + pfring_close(m_PfRingDescriptors[i]); + return false; + } + + if (getIsHwClockEnable()) { + for (int i = 0; i < ringsOpen; i++) { + if (setPfRingDeviceClock(m_PfRingDescriptors[i])) + PCPP_LOG_DEBUG("H/W clock set for device [" << m_DeviceName << "]"); + } + } + + // enable all rings + for (int i = 0; i < ringsOpen; i++) { + if (pfring_enable_rss_rehash(m_PfRingDescriptors[i]) < 0 || + pfring_enable_ring(m_PfRingDescriptors[i]) < 0) { + PCPP_LOG_ERROR("Unable to enable ring [" << i << "] for device [" + << m_DeviceName << "]"); + // close all pfring's that were enabled until now + for (int j = 0; j < ringsOpen; j++) + pfring_close(m_PfRingDescriptors[j]); + return false; + } + } + + m_NumOfOpenedRxChannels = ringsOpen; + + m_DeviceOpened = true; + return true; +} + +uint8_t PfRingDevice::getTotalNumOfRxChannels() const { + if (m_NumOfOpenedRxChannels > 0) { + uint8_t res = pfring_get_num_rx_channels(m_PfRingDescriptors[0]); + return res; + } else { + uint32_t flags = PF_RING_PROMISC | PF_RING_REENTRANT | + PF_RING_HW_TIMESTAMP | PF_RING_DNA_SYMMETRIC_RSS; + pfring* ring = + pfring_open(m_DeviceName.c_str(), DEFAULT_PF_RING_SNAPLEN, flags); + uint8_t res = pfring_get_num_rx_channels(ring); + pfring_close(ring); + return res; + } +} + +SystemCore PfRingDevice::getCurrentCoreId() const { + return SystemCores::IdToSystemCore[sched_getcpu()]; +} + +bool PfRingDevice::setFilter(std::string filterAsString) { + if (!m_DeviceOpened) { + PCPP_LOG_ERROR("Device not opened"); + return false; + } + + for (int i = 0; i < m_NumOfOpenedRxChannels; i++) { + int res = pfring_set_bpf_filter(m_PfRingDescriptors[i], + (char*)filterAsString.c_str()); + if (res < 0) { + if (res == PF_RING_ERROR_NOT_SUPPORTED) + PCPP_LOG_ERROR( + "BPF filtering isn't supported on current PF_RING version. Please " + "re-compile PF_RING with the --enable-bpf flag"); + else + PCPP_LOG_ERROR("Couldn't set filter '" << filterAsString << "'"); + return false; + } + } + + m_IsFilterCurrentlySet = true; + + PCPP_LOG_DEBUG("Successfully set filter '" << filterAsString << "'"); + return true; +} + +bool PfRingDevice::clearFilter() { + if (!m_IsFilterCurrentlySet) + return true; + + for (int i = 0; i < m_NumOfOpenedRxChannels; i++) { + int res = pfring_remove_bpf_filter(m_PfRingDescriptors[i]); + if (res < 0) { + PCPP_LOG_ERROR("Couldn't remove filter"); + return false; + } + } + + m_IsFilterCurrentlySet = false; + + PCPP_LOG_DEBUG("Successfully removed filter from all open RX channels"); + return true; +} + +bool PfRingDevice::isFilterCurrentlySet() const { + return m_IsFilterCurrentlySet; +} + +void PfRingDevice::close() { + for (int i = 0; i < m_NumOfOpenedRxChannels; i++) + pfring_close(m_PfRingDescriptors[i]); + m_DeviceOpened = false; + clearCoreConfiguration(); + m_NumOfOpenedRxChannels = 0; + m_IsFilterCurrentlySet = false; + PCPP_LOG_DEBUG("Device [" << m_DeviceName << "] closed"); +} + +bool PfRingDevice::initCoreConfigurationByCoreMask(CoreMask coreMask) { + int i = 0; + int numOfCores = getNumOfCores(); + clearCoreConfiguration(); + while ((coreMask != 0) && (i < numOfCores)) { + if (coreMask & 1) { + m_CoreConfiguration[i].IsInUse = true; + } + + coreMask = coreMask >> 1; + i++; + } + + if (coreMask != 0) // this mean coreMask contains a core that doesn't exist + { + PCPP_LOG_ERROR("Trying to use a core [" + << i << "] that doesn't exist while machine has " + << numOfCores << " cores"); + clearCoreConfiguration(); + return false; + } + + return true; +} + +bool PfRingDevice::startCaptureMultiThread( + OnPfRingPacketsArriveCallback onPacketsArrive, + void* onPacketsArriveUserCookie, CoreMask coreMask) { + if (!m_StopThread) { + PCPP_LOG_ERROR("Device already capturing. Cannot start 2 capture sessions " + "at the same time"); + return false; + } + + if (!initCoreConfigurationByCoreMask(coreMask)) + return false; + + if (m_NumOfOpenedRxChannels != getCoresInUseCount()) { + PCPP_LOG_ERROR( + "Cannot use a different number of channels and cores. Opened " + << m_NumOfOpenedRxChannels << " channels but set " + << getCoresInUseCount() << " cores in core mask"); + clearCoreConfiguration(); + return false; + } + + std::mutex mutex; + std::condition_variable cond; + int startThread = 0; + + m_StopThread = false; + int rxChannel = 0; + for (int coreId = 0; coreId < MAX_NUM_OF_CORES; coreId++) { + if (!m_CoreConfiguration[coreId].IsInUse) + continue; + + m_ReentrantMode = true; + + m_OnPacketsArriveCallback = onPacketsArrive; + m_OnPacketsArriveUserCookie = onPacketsArriveUserCookie; + + // create a new thread + m_CoreConfiguration[coreId].Channel = m_PfRingDescriptors[rxChannel++]; + m_CoreConfiguration[coreId].RxThread = + std::thread(&pcpp::PfRingDevice::captureThreadMain, this, &cond, &mutex, + &startThread); + + // set affinity to cores + cpu_set_t cpuset; + CPU_ZERO(&cpuset); + CPU_SET(coreId, &cpuset); + int err = pthread_setaffinity_np( + m_CoreConfiguration[coreId].RxThread.native_handle(), sizeof(cpu_set_t), + &cpuset); + if (err != 0) { + PCPP_LOG_ERROR("Error while binding thread to core " + << coreId << ": errno=" << err); + startThread = 1; + clearCoreConfiguration(); + return false; + } + } + + startThread = 2; + cond.notify_all(); + + return true; +} + +bool PfRingDevice::startCaptureSingleThread( + OnPfRingPacketsArriveCallback onPacketsArrive, + void* onPacketsArriveUserCookie) { + if (!m_StopThread) { + PCPP_LOG_ERROR("Device already capturing. Cannot start 2 capture sessions " + "at the same time"); + return false; + } + + if (m_NumOfOpenedRxChannels != 1) { + PCPP_LOG_ERROR("Cannot start capturing on a single thread when more than 1 " + "RX channel is opened"); + return false; + } + + PCPP_LOG_DEBUG("Trying to start capturing on a single thread for device [" + << m_DeviceName << "]"); + + clearCoreConfiguration(); + + m_OnPacketsArriveCallback = onPacketsArrive; + m_OnPacketsArriveUserCookie = onPacketsArriveUserCookie; + + m_StopThread = false; + + m_ReentrantMode = false; + + std::mutex mutex; + std::condition_variable cond; + int startThread = 0; + + cpu_set_t cpuset; + CPU_ZERO(&cpuset); + CPU_SET(0, &cpuset); + m_CoreConfiguration[0].IsInUse = true; + m_CoreConfiguration[0].Channel = m_PfRingDescriptors[0]; + m_CoreConfiguration[0].RxThread = + std::thread(&pcpp::PfRingDevice::captureThreadMain, this, &cond, &mutex, + &startThread); + m_CoreConfiguration[0].IsAffinitySet = false; + int err = + pthread_setaffinity_np(m_CoreConfiguration[0].RxThread.native_handle(), + sizeof(cpu_set_t), &cpuset); + if (err != 0) { + startThread = 1; + PCPP_LOG_ERROR("Error while binding thread to core 0: errno=" << err); + clearCoreConfiguration(); + return false; + } + startThread = 2; + cond.notify_all(); + + PCPP_LOG_DEBUG("Capturing started for device [" << m_DeviceName << "]"); + return true; +} + +void PfRingDevice::stopCapture() { + PCPP_LOG_DEBUG("Trying to stop capturing on device [" << m_DeviceName << "]"); + m_StopThread = true; + for (int coreId = 0; coreId < MAX_NUM_OF_CORES; coreId++) { + if (!m_CoreConfiguration[coreId].IsInUse) + continue; + m_CoreConfiguration[coreId].RxThread.join(); + PCPP_LOG_DEBUG("Thread on core [" << coreId << "] stopped"); + } + + PCPP_LOG_DEBUG("All capturing threads stopped"); +} + +void PfRingDevice::captureThreadMain(std::condition_variable* startCond, + std::mutex* startMutex, + const int* startState) { + while (*startState == 0) { + std::unique_lock lock(*startMutex); + startCond->wait_for(lock, std::chrono::milliseconds(100)); + } + if (*startState == 1) { + return; + } + + int coreId = this->getCurrentCoreId().Id; + pfring* ring = NULL; + + PCPP_LOG_DEBUG("Starting capture thread " << coreId); + + ring = this->m_CoreConfiguration[coreId].Channel; + + if (ring == NULL) { + PCPP_LOG_ERROR("Couldn't find ring for core " + << coreId << ". Exiting capture thread"); + return; + } + + while (!this->m_StopThread) { + // if buffer is NULL PF_RING avoids copy of the data + uint8_t* buffer = NULL; + uint32_t bufferLen = 0; + + // in multi-threaded mode flag PF_RING_REENTRANT is set, and this flag + // doesn't work with zero copy so I need to allocate a buffer and set buffer + // to point to it + if (this->m_ReentrantMode) { + uint8_t tempBuffer[PCPP_MAX_PACKET_SIZE]; + buffer = tempBuffer; + bufferLen = PCPP_MAX_PACKET_SIZE; + } + + struct pfring_pkthdr pktHdr; + int recvRes = pfring_recv(ring, &buffer, bufferLen, &pktHdr, 0); + if (recvRes > 0) { + // if caplen < len it means we don't have the whole packet. Treat this + // case as packet drop + // TODO: add this packet to dropped packet stats + // if (pktHdr.caplen != pktHdr.len) + // { + // PCPP_LOG_ERROR("Packet dropped due to + //len != caplen"); continue; + // } + + RawPacket rawPacket(buffer, pktHdr.caplen, pktHdr.ts, false); + this->m_OnPacketsArriveCallback(&rawPacket, 1, coreId, this, + this->m_OnPacketsArriveUserCookie); + } else if (recvRes < 0) { + // cppcheck-suppress shiftNegative + PCPP_LOG_ERROR("pfring_recv returned an error: [Err=" << recvRes << "]"); + } + } + + PCPP_LOG_DEBUG("Exiting capture thread " << coreId); +} + +void PfRingDevice::getThreadStatistics(SystemCore core, + PfRingStats& stats) const { + pfring* ring = NULL; + uint8_t coreId = core.Id; + + ring = m_CoreConfiguration[coreId].Channel; + + if (ring != NULL) { + pfring_stat tempStats; + if (pfring_stats(ring, &tempStats) < 0) { + PCPP_LOG_ERROR("Can't retrieve statistics for core [" + << (int)coreId << "], pfring_stats failed"); + return; + } + stats.drop = (uint64_t)tempStats.drop; + stats.recv = (uint64_t)tempStats.recv; + } else { + PCPP_LOG_ERROR("Core [" << (int)coreId + << "] is not in use, can't retrieve statistics"); + } +} + +void PfRingDevice::getCurrentThreadStatistics(PfRingStats& stats) const { + getThreadStatistics(getCurrentCoreId(), stats); +} + +void PfRingDevice::getStatistics(PfRingStats& stats) const { + stats.drop = 0; + stats.recv = 0; + + for (int coreId = 0; coreId < MAX_NUM_OF_CORES; coreId++) { + if (!m_CoreConfiguration[coreId].IsInUse) + continue; + + PfRingStats tempStat = {}; + getThreadStatistics(SystemCores::IdToSystemCore[coreId], tempStat); + stats.drop += tempStat.drop; + stats.recv += tempStat.recv; + + if (!m_CoreConfiguration[coreId].IsAffinitySet) + break; + } +} + +void PfRingDevice::clearCoreConfiguration() { + for (int i = 0; i < MAX_NUM_OF_CORES; i++) + m_CoreConfiguration[i].clear(); +} + +int PfRingDevice::getCoresInUseCount() const { + int res = 0; + for (int i = 0; i < MAX_NUM_OF_CORES; i++) + if (m_CoreConfiguration[i].IsInUse) + res++; + + return res; +} + +void PfRingDevice::setPfRingDeviceAttributes() { + if (m_InterfaceIndex > -1) + return; + + pfring* ring = NULL; + bool closeRing = false; + if (m_NumOfOpenedRxChannels > 0) + ring = m_PfRingDescriptors[0]; + else { + uint32_t flags = PF_RING_PROMISC | PF_RING_DNA_SYMMETRIC_RSS; + ring = pfring_open(m_DeviceName.c_str(), DEFAULT_PF_RING_SNAPLEN, flags); + closeRing = true; + } + + if (ring == NULL) { + PCPP_LOG_ERROR("Could not open a pfring for setting device attributes: MAC " + "address, interface index and HW clock"); + return; + } + + // set device MAC address + + uint8_t macAddress[6]; + if (pfring_get_bound_device_address(ring, macAddress) < 0) + PCPP_LOG_ERROR("Unable to read the device MAC address for interface '" + << m_DeviceName << "'"); + else + m_MacAddress = MacAddress(macAddress); + + // set interface ID + if (pfring_get_bound_device_ifindex(ring, &m_InterfaceIndex) < 0) + PCPP_LOG_ERROR("Unable to read interface index of device"); + + // try to set hardware device clock + m_HwClockEnabled = setPfRingDeviceClock(ring); + + // set interface MTU + int mtu = pfring_get_mtu_size(ring); + if (mtu < 0) + // cppcheck-suppress shiftNegative + PCPP_LOG_ERROR( + "Could not get MTU. pfring_get_mtu_size returned an error: " << mtu); + else + m_DeviceMTU = mtu + sizeof(ether_header) + sizeof(vlan_header); + + if (Logger::getInstance().isDebugEnabled(PcapLogModulePfRingDevice)) { + std::string hwEnabled = (m_HwClockEnabled ? "enabled" : "disabled"); + PCPP_LOG_DEBUG("Capturing from " << m_DeviceName << " [" << m_MacAddress + << "][ifIndex: " << m_InterfaceIndex + << "][MTU: " << m_DeviceMTU + << "], HW clock " << hwEnabled); + } + + if (closeRing) + pfring_close(ring); +} + +bool PfRingDevice::sendData(const uint8_t* packetData, int packetDataLength, + bool flushTxQueues) { + if (!m_DeviceOpened) { + PCPP_LOG_ERROR("Device is not opened. Cannot send packets"); + return false; + } + + uint8_t flushTxAsUint = (flushTxQueues ? 1 : 0); + +#define MAX_TRIES 5 + + int tries = 0; + int res = 0; + while (tries < MAX_TRIES) { + // don't allow sending of data larger than the MTU, otherwise pfring_send + // will fail + if (packetDataLength > m_DeviceMTU) + packetDataLength = m_DeviceMTU; + + // if the device is opened, m_PfRingDescriptors[0] will always be set and + // enables + res = pfring_send(m_PfRingDescriptors[0], (char*)packetData, + packetDataLength, flushTxAsUint); + + // res == -1 means it's an error coming from "sendto" which is the Linux API + // PF_RING is using to send packets errno == ENOBUFS means write buffer is + // full. PF_RING driver expects the userspace to handle this case My + // implementation is to sleep for 10 usec and try again + if (res == -1 && errno == ENOBUFS) { + tries++; + PCPP_LOG_DEBUG("Try #" + << tries + << ": Got ENOBUFS (write buffer full) error while sending " + "packet. Sleeping 20 usec and trying again"); + usleep(2000); + } else + break; + } + + if (tries >= MAX_TRIES) { + PCPP_LOG_ERROR("Tried to send data " << MAX_TRIES + << " times but write buffer is full"); + return false; + } + + if (res < 0) { + // res == -1 means it's an error coming from "sendto" which is the Linux API + // PF_RING is using to send packets + if (res == -1) + PCPP_LOG_ERROR("Error sending packet: Linux errno: " + << strerror(errno) << " [" << errno << "]"); + else + PCPP_LOG_ERROR("Error sending packet: pfring_send returned an error: " + << res << " , errno: " << strerror(errno) << " [" << errno + << "]"); + return false; + } else if (res != packetDataLength) { + PCPP_LOG_ERROR("Couldn't send all bytes, only " << res << " bytes out of " + << packetDataLength + << " bytes were sent"); + return false; + } + + return true; +} + +bool PfRingDevice::sendPacket(const uint8_t* packetData, int packetDataLength) { + return sendData(packetData, packetDataLength, true); +} + +bool PfRingDevice::sendPacket(const RawPacket& rawPacket) { + return sendData(rawPacket.getRawData(), rawPacket.getRawDataLen(), true); +} + +bool PfRingDevice::sendPacket(const Packet& packet) { + return sendData(packet.getRawPacketReadOnly()->getRawData(), + packet.getRawPacketReadOnly()->getRawDataLen(), true); +} + +int PfRingDevice::sendPackets(const RawPacket* rawPacketsArr, int arrLength) { + int packetsSent = 0; + for (int i = 0; i < arrLength; i++) { + if (!sendData(rawPacketsArr[i].getRawData(), + rawPacketsArr[i].getRawDataLen(), false)) + break; + else + packetsSent++; + } + + // The following method isn't supported in PF_RING aware drivers, probably + // only in DNA and ZC + pfring_flush_tx_packets(m_PfRingDescriptors[0]); + + PCPP_LOG_DEBUG(packetsSent << " out of " << arrLength + << " raw packets were sent successfully"); + + return packetsSent; +} + +int PfRingDevice::sendPackets(const Packet** packetsArr, int arrLength) { + int packetsSent = 0; + for (int i = 0; i < arrLength; i++) { + if (!sendData(packetsArr[i]->getRawPacketReadOnly()->getRawData(), + packetsArr[i]->getRawPacketReadOnly()->getRawDataLen(), + false)) + break; + else + packetsSent++; + } + + // The following method isn't supported in PF_RING aware drivers, probably + // only in DNA and ZC + pfring_flush_tx_packets(m_PfRingDescriptors[0]); + + PCPP_LOG_DEBUG(packetsSent << " out of " << arrLength + << " packets were sent successfully"); + + return packetsSent; +} + +int PfRingDevice::sendPackets(const RawPacketVector& rawPackets) { + int packetsSent = 0; + for (RawPacketVector::ConstVectorIterator iter = rawPackets.begin(); + iter != rawPackets.end(); iter++) { + if (!sendData((*iter)->getRawData(), (*iter)->getRawDataLen(), false)) + break; + else + packetsSent++; + } + + // The following method isn't supported in PF_RING aware drivers, probably + // only in DNA and ZC + pfring_flush_tx_packets(m_PfRingDescriptors[0]); - PCPP_LOG_DEBUG(packetsSent << " out of " << arrLength << " raw packets were sent successfully"); + PCPP_LOG_DEBUG(packetsSent << " out of " << rawPackets.size() + << " raw packets were sent successfully"); - return packetsSent; -} - -int PfRingDevice::sendPackets(const Packet** packetsArr, int arrLength) -{ - int packetsSent = 0; - for (int i = 0; i < arrLength; i++) - { - if (!sendData(packetsArr[i]->getRawPacketReadOnly()->getRawData(), packetsArr[i]->getRawPacketReadOnly()->getRawDataLen(), false)) - break; - else - packetsSent++; - } - - // The following method isn't supported in PF_RING aware drivers, probably only in DNA and ZC - pfring_flush_tx_packets(m_PfRingDescriptors[0]); - - PCPP_LOG_DEBUG(packetsSent << " out of " << arrLength << " packets were sent successfully"); - - return packetsSent; -} - -int PfRingDevice::sendPackets(const RawPacketVector& rawPackets) -{ - int packetsSent = 0; - for (RawPacketVector::ConstVectorIterator iter = rawPackets.begin(); iter != rawPackets.end(); iter++) - { - if (!sendData((*iter)->getRawData(), (*iter)->getRawDataLen(), false)) - break; - else - packetsSent++; - } - - // The following method isn't supported in PF_RING aware drivers, probably only in DNA and ZC - pfring_flush_tx_packets(m_PfRingDescriptors[0]); - - PCPP_LOG_DEBUG(packetsSent << " out of " << rawPackets.size() << " raw packets were sent successfully"); - - return packetsSent; + return packetsSent; } PfRingDevice::CoreConfiguration::CoreConfiguration() - : Channel(NULL), IsInUse(false), IsAffinitySet(true) -{ -} + : Channel(NULL), IsInUse(false), IsAffinitySet(true) {} -void PfRingDevice::CoreConfiguration::clear() -{ - Channel = NULL; - IsInUse = false; - IsAffinitySet = true; +void PfRingDevice::CoreConfiguration::clear() { + Channel = NULL; + IsInUse = false; + IsAffinitySet = true; } } // namespace pcpp diff --git a/Pcap++/src/PfRingDeviceList.cpp b/Pcap++/src/PfRingDeviceList.cpp index 5fe1a575b7..7c92c5a36a 100644 --- a/Pcap++/src/PfRingDeviceList.cpp +++ b/Pcap++/src/PfRingDeviceList.cpp @@ -9,93 +9,88 @@ #include "pcap.h" #include "pfring.h" -namespace pcpp -{ - -PfRingDeviceList::PfRingDeviceList() -{ - m_PfRingVersion = ""; - - FILE *fd = popen("lsmod | grep pf_ring", "r"); - char buf[16]; - if (!fread(buf, 1, sizeof (buf), fd)) // if there is some result the module must be loaded - { - PCPP_LOG_ERROR("PF_RING kernel module isn't loaded. Please run: 'sudo insmod /kernel/pf_ring.ko'"); - return; - } - - PCPP_LOG_DEBUG("PF_RING kernel module is loaded"); - - pcap_if_t* interfaceList; - char errbuf[PCAP_ERRBUF_SIZE]; - PCPP_LOG_DEBUG("PfRingDeviceList init: searching all interfaces on machine"); - int err = pcap_findalldevs(&interfaceList, errbuf); - if (err < 0) - { - PCPP_LOG_ERROR("Error searching for PF_RING devices: " << errbuf); - } - - pcap_if_t* currInterface = interfaceList; - while (currInterface != NULL) - { - uint32_t flags = PF_RING_PROMISC | PF_RING_DNA_SYMMETRIC_RSS; - pfring* ring = pfring_open(currInterface->name, 128, flags); - if (ring != NULL) - { - if (m_PfRingVersion == "") - calcPfRingVersion(ring); - pfring_close(ring); - PfRingDevice* newDev = new PfRingDevice(currInterface->name); - m_PfRingDeviceList.push_back(newDev); - PCPP_LOG_DEBUG("Found interface: " << currInterface->name); - } - - currInterface = currInterface->next; - } - - PCPP_LOG_DEBUG("PfRingDeviceList init end"); - pcap_freealldevs(interfaceList); +namespace pcpp { + +PfRingDeviceList::PfRingDeviceList() { + m_PfRingVersion = ""; + + FILE* fd = popen("lsmod | grep pf_ring", "r"); + char buf[16]; + if (!fread(buf, 1, sizeof(buf), + fd)) // if there is some result the module must be loaded + { + PCPP_LOG_ERROR("PF_RING kernel module isn't loaded. Please run: 'sudo " + "insmod /kernel/pf_ring.ko'"); + return; + } + + PCPP_LOG_DEBUG("PF_RING kernel module is loaded"); + + pcap_if_t* interfaceList; + char errbuf[PCAP_ERRBUF_SIZE]; + PCPP_LOG_DEBUG("PfRingDeviceList init: searching all interfaces on machine"); + int err = pcap_findalldevs(&interfaceList, errbuf); + if (err < 0) { + PCPP_LOG_ERROR("Error searching for PF_RING devices: " << errbuf); + } + + pcap_if_t* currInterface = interfaceList; + while (currInterface != NULL) { + uint32_t flags = PF_RING_PROMISC | PF_RING_DNA_SYMMETRIC_RSS; + pfring* ring = pfring_open(currInterface->name, 128, flags); + if (ring != NULL) { + if (m_PfRingVersion == "") + calcPfRingVersion(ring); + pfring_close(ring); + PfRingDevice* newDev = new PfRingDevice(currInterface->name); + m_PfRingDeviceList.push_back(newDev); + PCPP_LOG_DEBUG("Found interface: " << currInterface->name); + } + + currInterface = currInterface->next; + } + + PCPP_LOG_DEBUG("PfRingDeviceList init end"); + pcap_freealldevs(interfaceList); } -PfRingDeviceList::~PfRingDeviceList() -{ - for(std::vector::iterator devIter = m_PfRingDeviceList.begin(); devIter != m_PfRingDeviceList.end(); devIter++) - { - delete (*devIter); - } +PfRingDeviceList::~PfRingDeviceList() { + for (std::vector::iterator devIter = + m_PfRingDeviceList.begin(); + devIter != m_PfRingDeviceList.end(); devIter++) { + delete (*devIter); + } } -PfRingDevice* PfRingDeviceList::getPfRingDeviceByName(const std::string &devName) const -{ - PCPP_LOG_DEBUG("Searching all live devices..."); - for(std::vector::const_iterator devIter = m_PfRingDeviceList.begin(); devIter != m_PfRingDeviceList.end(); devIter++) - { - if ((*devIter)->getDeviceName() == devName) - return (*devIter); - } - - PCPP_LOG_DEBUG("Found no PF_RING devices with name '" << devName << "'"); - return NULL; +PfRingDevice* +PfRingDeviceList::getPfRingDeviceByName(const std::string& devName) const { + PCPP_LOG_DEBUG("Searching all live devices..."); + for (std::vector::const_iterator devIter = + m_PfRingDeviceList.begin(); + devIter != m_PfRingDeviceList.end(); devIter++) { + if ((*devIter)->getDeviceName() == devName) + return (*devIter); + } + + PCPP_LOG_DEBUG("Found no PF_RING devices with name '" << devName << "'"); + return NULL; } -void PfRingDeviceList::calcPfRingVersion(void* ring) -{ - pfring* ringPtr = (pfring*)ring; - uint32_t version; - if (pfring_version(ringPtr, &version) < 0) - { - PCPP_LOG_ERROR("Couldn't retrieve PF_RING version, pfring_version returned an error"); - return; - } - - char versionAsString[25]; - sprintf(versionAsString, "PF_RING v.%u.%u.%u\n", - (version & 0xFFFF0000) >> 16, - (version & 0x0000FF00) >> 8, - version & 0x000000FF); - - PCPP_LOG_DEBUG("PF_RING version is: " << versionAsString); - m_PfRingVersion = std::string(versionAsString); +void PfRingDeviceList::calcPfRingVersion(void* ring) { + pfring* ringPtr = (pfring*)ring; + uint32_t version; + if (pfring_version(ringPtr, &version) < 0) { + PCPP_LOG_ERROR( + "Couldn't retrieve PF_RING version, pfring_version returned an error"); + return; + } + + char versionAsString[25]; + sprintf(versionAsString, "PF_RING v.%u.%u.%u\n", (version & 0xFFFF0000) >> 16, + (version & 0x0000FF00) >> 8, version & 0x000000FF); + + PCPP_LOG_DEBUG("PF_RING version is: " << versionAsString); + m_PfRingVersion = std::string(versionAsString); } } // namespace pcpp diff --git a/Pcap++/src/RawSocketDevice.cpp b/Pcap++/src/RawSocketDevice.cpp index 8db2fed05f..dd420706f0 100644 --- a/Pcap++/src/RawSocketDevice.cpp +++ b/Pcap++/src/RawSocketDevice.cpp @@ -1,23 +1,22 @@ #include "RawSocketDevice.h" #include "EndianPortable.h" #ifdef __linux__ -#include #include -#include -#include -#include +#include #include #include +#include +#include +#include #endif -#include -#include "Logger.h" +#include "EthLayer.h" #include "IpUtils.h" -#include "SystemUtils.h" +#include "Logger.h" #include "Packet.h" -#include "EthLayer.h" +#include "SystemUtils.h" +#include -namespace pcpp -{ +namespace pcpp { #define RAW_SOCKET_BUFFER_LEN 65536 @@ -26,547 +25,525 @@ namespace pcpp #ifndef SIO_RCVALL /* SIO_RCVALL defined on w2k and later. Not defined in Mingw32 */ /* 0x98000001 = _WSAIOW(IOC_VENDOR,1) */ -# define SIO_RCVALL 0x98000001 +#define SIO_RCVALL 0x98000001 #endif // SIO_RCVALL -class WinSockInitializer -{ -private: - static bool m_IsInitialized; - -public: - - static void initialize() - { - if (m_IsInitialized) - return; - - // Load Winsock - WSADATA wsaData; - int res = WSAStartup(MAKEWORD(2,2), &wsaData); - if (res != 0) - { - PCPP_LOG_ERROR("WSAStartup failed with error code: " << res); - m_IsInitialized = false; - } - - m_IsInitialized = true; - } +class WinSockInitializer { + private: + static bool m_IsInitialized; + + public: + static void initialize() { + if (m_IsInitialized) + return; + + // Load Winsock + WSADATA wsaData; + int res = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (res != 0) { + PCPP_LOG_ERROR("WSAStartup failed with error code: " << res); + m_IsInitialized = false; + } + + m_IsInitialized = true; + } }; bool WinSockInitializer::m_IsInitialized = false; #endif // defined(_WIN32) -struct SocketContainer -{ +struct SocketContainer { #if defined(_WIN32) - SOCKET fd; + SOCKET fd; #elif defined(__linux__) - int fd; - int interfaceIndex; - std::string interfaceName; + int fd; + int interfaceIndex; + std::string interfaceName; #endif }; -RawSocketDevice::RawSocketDevice(const IPAddress& interfaceIP) : IDevice(), m_Socket(nullptr) -{ +RawSocketDevice::RawSocketDevice(const IPAddress& interfaceIP) + : IDevice(), m_Socket(nullptr) { #if defined(_WIN32) - WinSockInitializer::initialize(); - m_InterfaceIP = interfaceIP; - m_SockFamily = (m_InterfaceIP.getType() == IPAddress::IPv4AddressType ? IPv4 : IPv6); + WinSockInitializer::initialize(); + m_InterfaceIP = interfaceIP; + m_SockFamily = + (m_InterfaceIP.getType() == IPAddress::IPv4AddressType ? IPv4 : IPv6); #elif defined(__linux__) - m_InterfaceIP = interfaceIP; - m_SockFamily = Ethernet; + m_InterfaceIP = interfaceIP; + m_SockFamily = Ethernet; #else - m_SockFamily = Ethernet; + m_SockFamily = Ethernet; #endif } +RawSocketDevice::~RawSocketDevice() { close(); } -RawSocketDevice::~RawSocketDevice() -{ - close(); -} - -RawSocketDevice::RecvPacketResult RawSocketDevice::receivePacket(RawPacket& rawPacket, bool blocking, int timeout) -{ +RawSocketDevice::RecvPacketResult +RawSocketDevice::receivePacket(RawPacket& rawPacket, bool blocking, + int timeout) { #if defined(_WIN32) - if (!isOpened()) - { - PCPP_LOG_ERROR("Device is not open"); - return RecvError; - } - - SOCKET fd = ((SocketContainer*)m_Socket)->fd; - char* buffer = new char[RAW_SOCKET_BUFFER_LEN]; - memset(buffer, 0, RAW_SOCKET_BUFFER_LEN); - - // value of 0 timeout means disabling timeout - if (timeout < 0) - timeout = 0; + if (!isOpened()) { + PCPP_LOG_ERROR("Device is not open"); + return RecvError; + } + + SOCKET fd = ((SocketContainer*)m_Socket)->fd; + char* buffer = new char[RAW_SOCKET_BUFFER_LEN]; + memset(buffer, 0, RAW_SOCKET_BUFFER_LEN); + + // value of 0 timeout means disabling timeout + if (timeout < 0) + timeout = 0; + + u_long blockingMode = (blocking ? 0 : 1); + ioctlsocket(fd, FIONBIO, &blockingMode); + + DWORD timeoutVal = timeout * 1000; + setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeoutVal, + sizeof(timeoutVal)); + + // recvfrom(fd, buffer, RAW_SOCKET_BUFFER_LEN, 0, (struct + // sockaddr*)&sockAddr,(socklen_t*)&sockAddrLen); + int bufferLen = recv(fd, buffer, RAW_SOCKET_BUFFER_LEN, 0); + if (bufferLen < 0) { + delete[] buffer; + int errorCode = 0; + RecvPacketResult error = getError(errorCode); + + if (error == RecvError) + PCPP_LOG_ERROR("Error reading from recvfrom. Error code is " + << errorCode); + + return error; + } + + if (bufferLen > 0) { + timeval time; + gettimeofday(&time, NULL); + rawPacket.setRawData((const uint8_t*)buffer, bufferLen, time, + LINKTYPE_DLT_RAW1); + return RecvSuccess; + } + + PCPP_LOG_ERROR("Buffer length is zero"); + delete[] buffer; + return RecvError; - u_long blockingMode = (blocking? 0 : 1); - ioctlsocket(fd, FIONBIO, &blockingMode); - - DWORD timeoutVal = timeout * 1000; - setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeoutVal, sizeof(timeoutVal)); +#elif defined(__linux__) - //recvfrom(fd, buffer, RAW_SOCKET_BUFFER_LEN, 0, (struct sockaddr*)&sockAddr,(socklen_t*)&sockAddrLen); - int bufferLen = recv(fd, buffer, RAW_SOCKET_BUFFER_LEN, 0); - if (bufferLen < 0) - { - delete [] buffer; - int errorCode = 0; - RecvPacketResult error = getError(errorCode); + if (!isOpened()) { + PCPP_LOG_ERROR("Device is not open"); + return RecvError; + } + + int fd = ((SocketContainer*)m_Socket)->fd; + char* buffer = new char[RAW_SOCKET_BUFFER_LEN]; + memset(buffer, 0, RAW_SOCKET_BUFFER_LEN); + + // value of 0 timeout means disabling timeout + if (timeout < 0) + timeout = 0; + + // set blocking or non-blocking flag + int flags = fcntl(fd, F_GETFL, 0); + if (flags == -1) { + delete[] buffer; + PCPP_LOG_ERROR("Cannot get socket flags"); + return RecvError; + } + flags = (blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK)); + if (fcntl(fd, F_SETFL, flags) != 0) { + delete[] buffer; + PCPP_LOG_ERROR("Cannot set socket non-blocking flag"); + return RecvError; + } + + // set timeout on socket + struct timeval timeoutVal; + timeoutVal.tv_sec = timeout; + timeoutVal.tv_usec = 0; + setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeoutVal, + sizeof(timeoutVal)); + + int bufferLen = recv(fd, buffer, RAW_SOCKET_BUFFER_LEN, 0); + if (bufferLen < 0) { + delete[] buffer; + int errorCode = errno; + RecvPacketResult error = getError(errorCode); + + if (error == RecvError) + PCPP_LOG_ERROR("Error reading from recvfrom. Error code is " + << errorCode); + + return error; + } + + if (bufferLen > 0) { + timeval time; + gettimeofday(&time, NULL); + rawPacket.setRawData((const uint8_t*)buffer, bufferLen, time, + LINKTYPE_ETHERNET); + return RecvSuccess; + } + + PCPP_LOG_ERROR("Buffer length is zero"); + delete[] buffer; + return RecvError; - if (error == RecvError) - PCPP_LOG_ERROR("Error reading from recvfrom. Error code is " << errorCode); +#else - return error; - } + PCPP_LOG_ERROR("Raw socket are not supported on this platform"); + return RecvError; - if (bufferLen > 0) - { - timeval time; - gettimeofday(&time, NULL); - rawPacket.setRawData((const uint8_t*)buffer, bufferLen, time, LINKTYPE_DLT_RAW1); - return RecvSuccess; - } +#endif +} - PCPP_LOG_ERROR("Buffer length is zero"); - delete [] buffer; - return RecvError; +int RawSocketDevice::receivePackets(RawPacketVector& packetVec, int timeout, + int& failedRecv) { + if (!isOpened()) { + PCPP_LOG_ERROR("Device is not open"); + return 0; + } -#elif defined(__linux__) + long curSec, curNsec; + clockGetTime(curSec, curNsec); - if (!isOpened()) - { - PCPP_LOG_ERROR("Device is not open"); - return RecvError; - } - - int fd = ((SocketContainer*)m_Socket)->fd; - char* buffer = new char[RAW_SOCKET_BUFFER_LEN]; - memset(buffer, 0, RAW_SOCKET_BUFFER_LEN); - - // value of 0 timeout means disabling timeout - if (timeout < 0) - timeout = 0; - - // set blocking or non-blocking flag - int flags = fcntl(fd, F_GETFL, 0); - if (flags == -1) - { - delete [] buffer; - PCPP_LOG_ERROR("Cannot get socket flags"); - return RecvError; - } - flags = (blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK)); - if (fcntl(fd, F_SETFL, flags) != 0) - { - delete [] buffer; - PCPP_LOG_ERROR("Cannot set socket non-blocking flag"); - return RecvError; - } - - // set timeout on socket - struct timeval timeoutVal; - timeoutVal.tv_sec = timeout; - timeoutVal.tv_usec = 0; - setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeoutVal, sizeof(timeoutVal)); - - int bufferLen = recv(fd, buffer, RAW_SOCKET_BUFFER_LEN, 0); - if (bufferLen < 0) - { - delete [] buffer; - int errorCode = errno; - RecvPacketResult error = getError(errorCode); - - if (error == RecvError) - PCPP_LOG_ERROR("Error reading from recvfrom. Error code is " << errorCode); - - return error; - } - - if (bufferLen > 0) - { - timeval time; - gettimeofday(&time, NULL); - rawPacket.setRawData((const uint8_t*)buffer, bufferLen, time, LINKTYPE_ETHERNET); - return RecvSuccess; - } - - PCPP_LOG_ERROR("Buffer length is zero"); - delete [] buffer; - return RecvError; + int packetCount = 0; + failedRecv = 0; -#else + long timeoutSec = curSec + timeout; - PCPP_LOG_ERROR("Raw socket are not supported on this platform"); - return RecvError; + while (curSec < timeoutSec) { + RawPacket* rawPacket = new RawPacket(); + if (receivePacket(*rawPacket, true, timeoutSec - curSec) == RecvSuccess) { + packetVec.pushBack(rawPacket); + packetCount++; + } else { + failedRecv++; + delete rawPacket; + } -#endif -} + clockGetTime(curSec, curNsec); + } -int RawSocketDevice::receivePackets(RawPacketVector& packetVec, int timeout, int& failedRecv) -{ - if (!isOpened()) - { - PCPP_LOG_ERROR("Device is not open"); - return 0; - } - - long curSec, curNsec; - clockGetTime(curSec, curNsec); - - int packetCount = 0; - failedRecv = 0; - - long timeoutSec = curSec + timeout; - - while (curSec < timeoutSec) - { - RawPacket* rawPacket = new RawPacket(); - if (receivePacket(*rawPacket, true, timeoutSec-curSec) == RecvSuccess) - { - packetVec.pushBack(rawPacket); - packetCount++; - } - else - { - failedRecv++; - delete rawPacket; - } - - clockGetTime(curSec, curNsec); - } - - return packetCount; + return packetCount; } -bool RawSocketDevice::sendPacket(const RawPacket* rawPacket) -{ +bool RawSocketDevice::sendPacket(const RawPacket* rawPacket) { #if defined(_WIN32) - PCPP_LOG_ERROR("Sending packets with raw socket are not supported on Windows"); - return 0; + PCPP_LOG_ERROR( + "Sending packets with raw socket are not supported on Windows"); + return 0; #elif defined(__linux__) - if (!isOpened()) - { - PCPP_LOG_ERROR("Device is not open"); - return false; - } + if (!isOpened()) { + PCPP_LOG_ERROR("Device is not open"); + return false; + } - Packet packet((RawPacket*)rawPacket, OsiModelDataLinkLayer); - if (!packet.isPacketOfType(pcpp::Ethernet)) - { - PCPP_LOG_ERROR("Can't send non-Ethernet packets"); - return false; - } + Packet packet((RawPacket*)rawPacket, OsiModelDataLinkLayer); + if (!packet.isPacketOfType(pcpp::Ethernet)) { + PCPP_LOG_ERROR("Can't send non-Ethernet packets"); + return false; + } - int fd = ((SocketContainer*)m_Socket)->fd; + int fd = ((SocketContainer*)m_Socket)->fd; - sockaddr_ll addr; - memset(&addr, 0, sizeof(struct sockaddr_ll)); - addr.sll_family = htobe16(PF_PACKET); - addr.sll_protocol = htobe16(ETH_P_ALL); - addr.sll_halen = 6; - addr.sll_ifindex = ((SocketContainer*)m_Socket)->interfaceIndex; + sockaddr_ll addr; + memset(&addr, 0, sizeof(struct sockaddr_ll)); + addr.sll_family = htobe16(PF_PACKET); + addr.sll_protocol = htobe16(ETH_P_ALL); + addr.sll_halen = 6; + addr.sll_ifindex = ((SocketContainer*)m_Socket)->interfaceIndex; - EthLayer* ethLayer = packet.getLayerOfType(); - MacAddress dstMac = ethLayer->getDestMac(); - dstMac.copyTo((uint8_t*)&(addr.sll_addr)); + EthLayer* ethLayer = packet.getLayerOfType(); + MacAddress dstMac = ethLayer->getDestMac(); + dstMac.copyTo((uint8_t*)&(addr.sll_addr)); - if (::sendto(fd, ((RawPacket*)rawPacket)->getRawData(), ((RawPacket*)rawPacket)->getRawDataLen(), 0, (struct sockaddr*)&addr, sizeof(addr)) == -1) - { - PCPP_LOG_ERROR("Failed to send packet. Error was: '" << strerror(errno) << "'"); - return false; - } + if (::sendto(fd, ((RawPacket*)rawPacket)->getRawData(), + ((RawPacket*)rawPacket)->getRawDataLen(), 0, + (struct sockaddr*)&addr, sizeof(addr)) == -1) { + PCPP_LOG_ERROR("Failed to send packet. Error was: '" << strerror(errno) + << "'"); + return false; + } - return true; + return true; #else - PCPP_LOG_ERROR("Raw socket are not supported on this platform"); - return 0; + PCPP_LOG_ERROR("Raw socket are not supported on this platform"); + return 0; #endif } -int RawSocketDevice::sendPackets(const RawPacketVector& packetVec) -{ +int RawSocketDevice::sendPackets(const RawPacketVector& packetVec) { #if defined(_WIN32) - PCPP_LOG_ERROR("Sending packets with raw socket are not supported on Windows"); - return false; + PCPP_LOG_ERROR( + "Sending packets with raw socket are not supported on Windows"); + return false; #elif defined(__linux__) - if (!isOpened()) - { - PCPP_LOG_ERROR("Device is not open"); - return 0; - } + if (!isOpened()) { + PCPP_LOG_ERROR("Device is not open"); + return 0; + } - int fd = ((SocketContainer*)m_Socket)->fd; + int fd = ((SocketContainer*)m_Socket)->fd; - sockaddr_ll addr; - memset(&addr, 0, sizeof(struct sockaddr_ll)); - addr.sll_family = htobe16(PF_PACKET); - addr.sll_protocol = htobe16(ETH_P_ALL); - addr.sll_halen = 6; - addr.sll_ifindex = ((SocketContainer*)m_Socket)->interfaceIndex; + sockaddr_ll addr; + memset(&addr, 0, sizeof(struct sockaddr_ll)); + addr.sll_family = htobe16(PF_PACKET); + addr.sll_protocol = htobe16(ETH_P_ALL); + addr.sll_halen = 6; + addr.sll_ifindex = ((SocketContainer*)m_Socket)->interfaceIndex; - int sendCount = 0; + int sendCount = 0; - for (RawPacketVector::ConstVectorIterator iter = packetVec.begin(); iter != packetVec.end(); iter++) - { - Packet packet(*iter, OsiModelDataLinkLayer); - if (!packet.isPacketOfType(pcpp::Ethernet)) - { - PCPP_LOG_DEBUG("Can't send non-Ethernet packets"); - continue; - } + for (RawPacketVector::ConstVectorIterator iter = packetVec.begin(); + iter != packetVec.end(); iter++) { + Packet packet(*iter, OsiModelDataLinkLayer); + if (!packet.isPacketOfType(pcpp::Ethernet)) { + PCPP_LOG_DEBUG("Can't send non-Ethernet packets"); + continue; + } - EthLayer* ethLayer = packet.getLayerOfType(); - MacAddress dstMac = ethLayer->getDestMac(); - dstMac.copyTo((uint8_t*)&(addr.sll_addr)); + EthLayer* ethLayer = packet.getLayerOfType(); + MacAddress dstMac = ethLayer->getDestMac(); + dstMac.copyTo((uint8_t*)&(addr.sll_addr)); - if (::sendto(fd, (*iter)->getRawData(), (*iter)->getRawDataLen(), 0, (struct sockaddr*)&addr, sizeof(addr)) == -1) - { - PCPP_LOG_DEBUG("Failed to send packet. Error was: '" << strerror(errno) << "'"); - continue; - } + if (::sendto(fd, (*iter)->getRawData(), (*iter)->getRawDataLen(), 0, + (struct sockaddr*)&addr, sizeof(addr)) == -1) { + PCPP_LOG_DEBUG("Failed to send packet. Error was: '" << strerror(errno) + << "'"); + continue; + } - sendCount++; - } + sendCount++; + } - return sendCount; + return sendCount; #else - PCPP_LOG_ERROR("Raw socket are not supported on this platform"); - return false; + PCPP_LOG_ERROR("Raw socket are not supported on this platform"); + return false; #endif } - -bool RawSocketDevice::open() -{ +bool RawSocketDevice::open() { #if defined(_WIN32) - if (!m_InterfaceIP.isValid()) - { - PCPP_LOG_ERROR("IP address is not valid"); - return false; - } - - int family = (m_SockFamily == IPv4 ? AF_INET : AF_INET6); - SOCKET fd = socket(family, SOCK_RAW, IPPROTO_IP); - if ((int)fd == SOCKET_ERROR) - { - int error = WSAGetLastError(); - std::string additionalMessage = ""; - if (error == WSAEACCES) - additionalMessage = ", you may not be running with administrative privileges which is required for opening raw sockets on Windows"; - PCPP_LOG_ERROR("Failed to create raw socket. Error code was " << error << " " << additionalMessage); - return false; - } - - void* localAddr = NULL; - struct sockaddr_in localAddrIPv4; - struct sockaddr_in6 localAddrIPv6; - size_t localAddrSize = 0; - - if (m_SockFamily == IPv4) - { - localAddrIPv4.sin_family = family; - int res = inet_pton(family, m_InterfaceIP.toString().c_str(), &localAddrIPv4.sin_addr.s_addr); - if (res <= 0) - { - PCPP_LOG_ERROR("inet_pton failed, probably IP address provided is in bad format"); - closesocket(fd); - return false; - } - localAddrIPv4.sin_port = 0; // Any local port will do - localAddr = &localAddrIPv4; - localAddrSize = sizeof(localAddrIPv4); - } - else - { - localAddrIPv6.sin6_family = family; - int res = inet_pton(AF_INET6, m_InterfaceIP.toString().c_str(), &localAddrIPv6.sin6_addr.s6_addr); - if (res <= 0) - { - PCPP_LOG_ERROR("inet_pton failed, probably IP address provided is in bad format"); - closesocket(fd); - return false; - } - localAddrIPv6.sin6_port = 0; // Any local port will do - localAddrIPv6.sin6_scope_id = 0; - localAddr = &localAddrIPv6; - localAddrSize = sizeof(localAddrIPv6); - } - - if (bind(fd, (struct sockaddr *)localAddr, localAddrSize) == SOCKET_ERROR) - { - PCPP_LOG_ERROR("Failed to bind to interface. Error code was '" << WSAGetLastError() << "'"); - closesocket(fd); - return false; - } - - int n = 1; - DWORD dwBytesRet; - if (WSAIoctl(fd, SIO_RCVALL, &n, sizeof(n), NULL, 0, &dwBytesRet, NULL, NULL) == SOCKET_ERROR) - { - PCPP_LOG_ERROR("Call to WSAIotcl(" << std::hex << SIO_RCVALL << ") failed with error code " << WSAGetLastError()); - closesocket(fd); - return false; - } - - m_Socket = new SocketContainer(); - ((SocketContainer*)m_Socket)->fd = fd; - - m_DeviceOpened = true; - - return true; + if (!m_InterfaceIP.isValid()) { + PCPP_LOG_ERROR("IP address is not valid"); + return false; + } + + int family = (m_SockFamily == IPv4 ? AF_INET : AF_INET6); + SOCKET fd = socket(family, SOCK_RAW, IPPROTO_IP); + if ((int)fd == SOCKET_ERROR) { + int error = WSAGetLastError(); + std::string additionalMessage = ""; + if (error == WSAEACCES) + additionalMessage = + ", you may not be running with administrative privileges which is " + "required for opening raw sockets on Windows"; + PCPP_LOG_ERROR("Failed to create raw socket. Error code was " + << error << " " << additionalMessage); + return false; + } + + void* localAddr = NULL; + struct sockaddr_in localAddrIPv4; + struct sockaddr_in6 localAddrIPv6; + size_t localAddrSize = 0; + + if (m_SockFamily == IPv4) { + localAddrIPv4.sin_family = family; + int res = inet_pton(family, m_InterfaceIP.toString().c_str(), + &localAddrIPv4.sin_addr.s_addr); + if (res <= 0) { + PCPP_LOG_ERROR( + "inet_pton failed, probably IP address provided is in bad format"); + closesocket(fd); + return false; + } + localAddrIPv4.sin_port = 0; // Any local port will do + localAddr = &localAddrIPv4; + localAddrSize = sizeof(localAddrIPv4); + } else { + localAddrIPv6.sin6_family = family; + int res = inet_pton(AF_INET6, m_InterfaceIP.toString().c_str(), + &localAddrIPv6.sin6_addr.s6_addr); + if (res <= 0) { + PCPP_LOG_ERROR( + "inet_pton failed, probably IP address provided is in bad format"); + closesocket(fd); + return false; + } + localAddrIPv6.sin6_port = 0; // Any local port will do + localAddrIPv6.sin6_scope_id = 0; + localAddr = &localAddrIPv6; + localAddrSize = sizeof(localAddrIPv6); + } + + if (bind(fd, (struct sockaddr*)localAddr, localAddrSize) == SOCKET_ERROR) { + PCPP_LOG_ERROR("Failed to bind to interface. Error code was '" + << WSAGetLastError() << "'"); + closesocket(fd); + return false; + } + + int n = 1; + DWORD dwBytesRet; + if (WSAIoctl(fd, SIO_RCVALL, &n, sizeof(n), NULL, 0, &dwBytesRet, NULL, + NULL) == SOCKET_ERROR) { + PCPP_LOG_ERROR("Call to WSAIotcl(" << std::hex << SIO_RCVALL + << ") failed with error code " + << WSAGetLastError()); + closesocket(fd); + return false; + } + + m_Socket = new SocketContainer(); + ((SocketContainer*)m_Socket)->fd = fd; + + m_DeviceOpened = true; + + return true; #elif defined(__linux__) #if defined(__ANDROID_API__) && __ANDROID_API__ < 24 - PCPP_LOG_ERROR("Raw sockets aren't supported in Android API < 24"); - return false; + PCPP_LOG_ERROR("Raw sockets aren't supported in Android API < 24"); + return false; #else - if (!m_InterfaceIP.isValid()) - { - PCPP_LOG_ERROR("IP address is not valid"); - return false; - } - - int fd = socket(AF_PACKET, SOCK_RAW, htobe16(ETH_P_ALL)); - if (fd < 0) - { - PCPP_LOG_ERROR("Failed to create raw socket. Error code was " << errno); - return false; - } - - // find interface name and index from IP address - struct ifaddrs* addrs; - getifaddrs(&addrs); - std::string ifaceName = ""; - int ifaceIndex = -1; - for (struct ifaddrs* curAddr = addrs; curAddr != NULL; curAddr = curAddr->ifa_next) - { - if (curAddr->ifa_addr && (curAddr->ifa_flags & IFF_UP)) - { - if (curAddr->ifa_addr->sa_family == AF_INET) - { - struct sockaddr_in* sockAddr = (struct sockaddr_in*)(curAddr->ifa_addr); - char addrAsCharArr[32]; - inet_ntop(curAddr->ifa_addr->sa_family, (void *)&(sockAddr->sin_addr), addrAsCharArr, sizeof(addrAsCharArr)); - if (!strcmp(m_InterfaceIP.toString().c_str(), addrAsCharArr)) - { - ifaceName = curAddr->ifa_name; - ifaceIndex = if_nametoindex(curAddr->ifa_name); - } - } - else if (curAddr->ifa_addr->sa_family == AF_INET6) - { - struct sockaddr_in6* sockAddr = (struct sockaddr_in6*)(curAddr->ifa_addr); - char addrAsCharArr[40]; - inet_ntop(curAddr->ifa_addr->sa_family, (void *)&(sockAddr->sin6_addr), addrAsCharArr, sizeof(addrAsCharArr)); - if (!strcmp(m_InterfaceIP.toString().c_str(), addrAsCharArr)) - { - ifaceName = curAddr->ifa_name; - ifaceIndex = if_nametoindex(curAddr->ifa_name); - } - } - - } - } - freeifaddrs(addrs); - - if (ifaceName == "" || ifaceIndex < 0) - { - PCPP_LOG_ERROR("Cannot detect interface name or index from IP address"); - ::close(fd); - return false; - } - - // bind raw socket to interface - struct ifreq ifr; - memset(&ifr, 0, sizeof(ifr)); - snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", ifaceName.c_str()); - if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) == -1) - { - PCPP_LOG_ERROR("Cannot bind raw socket to interface '" << ifaceName << "'"); - ::close(fd); - return false; - } - - m_Socket = new SocketContainer(); - ((SocketContainer*)m_Socket)->fd = fd; - ((SocketContainer*)m_Socket)->interfaceIndex = ifaceIndex; - ((SocketContainer*)m_Socket)->interfaceName = ifaceName; - - m_DeviceOpened = true; - - return true; + if (!m_InterfaceIP.isValid()) { + PCPP_LOG_ERROR("IP address is not valid"); + return false; + } + + int fd = socket(AF_PACKET, SOCK_RAW, htobe16(ETH_P_ALL)); + if (fd < 0) { + PCPP_LOG_ERROR("Failed to create raw socket. Error code was " << errno); + return false; + } + + // find interface name and index from IP address + struct ifaddrs* addrs; + getifaddrs(&addrs); + std::string ifaceName = ""; + int ifaceIndex = -1; + for (struct ifaddrs* curAddr = addrs; curAddr != NULL; + curAddr = curAddr->ifa_next) { + if (curAddr->ifa_addr && (curAddr->ifa_flags & IFF_UP)) { + if (curAddr->ifa_addr->sa_family == AF_INET) { + struct sockaddr_in* sockAddr = + (struct sockaddr_in*)(curAddr->ifa_addr); + char addrAsCharArr[32]; + inet_ntop(curAddr->ifa_addr->sa_family, (void*)&(sockAddr->sin_addr), + addrAsCharArr, sizeof(addrAsCharArr)); + if (!strcmp(m_InterfaceIP.toString().c_str(), addrAsCharArr)) { + ifaceName = curAddr->ifa_name; + ifaceIndex = if_nametoindex(curAddr->ifa_name); + } + } else if (curAddr->ifa_addr->sa_family == AF_INET6) { + struct sockaddr_in6* sockAddr = + (struct sockaddr_in6*)(curAddr->ifa_addr); + char addrAsCharArr[40]; + inet_ntop(curAddr->ifa_addr->sa_family, (void*)&(sockAddr->sin6_addr), + addrAsCharArr, sizeof(addrAsCharArr)); + if (!strcmp(m_InterfaceIP.toString().c_str(), addrAsCharArr)) { + ifaceName = curAddr->ifa_name; + ifaceIndex = if_nametoindex(curAddr->ifa_name); + } + } + } + } + freeifaddrs(addrs); + + if (ifaceName == "" || ifaceIndex < 0) { + PCPP_LOG_ERROR("Cannot detect interface name or index from IP address"); + ::close(fd); + return false; + } + + // bind raw socket to interface + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", ifaceName.c_str()); + if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, (void*)&ifr, sizeof(ifr)) == + -1) { + PCPP_LOG_ERROR("Cannot bind raw socket to interface '" << ifaceName << "'"); + ::close(fd); + return false; + } + + m_Socket = new SocketContainer(); + ((SocketContainer*)m_Socket)->fd = fd; + ((SocketContainer*)m_Socket)->interfaceIndex = ifaceIndex; + ((SocketContainer*)m_Socket)->interfaceName = ifaceName; + + m_DeviceOpened = true; + + return true; #endif // __ANDROID_API__ #else - PCPP_LOG_ERROR("Raw socket are not supported on this platform"); - return false; + PCPP_LOG_ERROR("Raw socket are not supported on this platform"); + return false; #endif } -void RawSocketDevice::close() -{ - if (m_Socket != nullptr && isOpened()) - { - SocketContainer* sockContainer = (SocketContainer*)m_Socket; +void RawSocketDevice::close() { + if (m_Socket != nullptr && isOpened()) { + SocketContainer* sockContainer = (SocketContainer*)m_Socket; #if defined(_WIN32) - closesocket(sockContainer->fd); + closesocket(sockContainer->fd); #elif defined(__linux__) - ::close(sockContainer->fd); + ::close(sockContainer->fd); #endif - delete sockContainer; - m_Socket = nullptr; - m_DeviceOpened = false; - } + delete sockContainer; + m_Socket = nullptr; + m_DeviceOpened = false; + } } -RawSocketDevice::RecvPacketResult RawSocketDevice::getError(int& errorCode) const -{ +RawSocketDevice::RecvPacketResult +RawSocketDevice::getError(int& errorCode) const { #if defined(_WIN32) - errorCode = WSAGetLastError(); - if (errorCode == WSAEWOULDBLOCK) - return RecvWouldBlock; - if (errorCode == WSAETIMEDOUT) - return RecvTimeout; + errorCode = WSAGetLastError(); + if (errorCode == WSAEWOULDBLOCK) + return RecvWouldBlock; + if (errorCode == WSAETIMEDOUT) + return RecvTimeout; - return RecvError; + return RecvError; #elif defined(__linux__) - if ((errorCode == EAGAIN) || (errorCode == EWOULDBLOCK)) - return RecvWouldBlock; + if ((errorCode == EAGAIN) || (errorCode == EWOULDBLOCK)) + return RecvWouldBlock; - return RecvError; + return RecvError; #else - return RecvError; + return RecvError; #endif } -} +} // namespace pcpp diff --git a/Pcap++/src/WinPcapLiveDevice.cpp b/Pcap++/src/WinPcapLiveDevice.cpp index c1e0429c7e..555c25f5da 100644 --- a/Pcap++/src/WinPcapLiveDevice.cpp +++ b/Pcap++/src/WinPcapLiveDevice.cpp @@ -7,123 +7,129 @@ #include "TimespecTimeval.h" #include "pcap.h" -namespace pcpp -{ - -WinPcapLiveDevice::WinPcapLiveDevice(pcap_if_t* iface, bool calculateMTU, bool calculateMacAddress, bool calculateDefaultGateway) : PcapLiveDevice(iface, calculateMTU, calculateMacAddress, calculateDefaultGateway) -{ - m_MinAmountOfDataToCopyFromKernelToApplication = 16000; +namespace pcpp { + +WinPcapLiveDevice::WinPcapLiveDevice(pcap_if_t* iface, bool calculateMTU, + bool calculateMacAddress, + bool calculateDefaultGateway) + : PcapLiveDevice(iface, calculateMTU, calculateMacAddress, + calculateDefaultGateway) { + m_MinAmountOfDataToCopyFromKernelToApplication = 16000; } -bool WinPcapLiveDevice::startCapture(OnPacketArrivesCallback onPacketArrives, void* onPacketArrivesUserCookie, int intervalInSecondsToUpdateStats, OnStatsUpdateCallback onStatsUpdate, void* onStatsUpdateUserCookie) -{ - if (!m_DeviceOpened || m_PcapDescriptor == NULL) - { - PCPP_LOG_ERROR("Device '" << m_Name << "' not opened"); - return false; - } - - //Put the interface in capture mode - if (pcap_setmode(m_PcapDescriptor, MODE_CAPT) < 0) - { - PCPP_LOG_ERROR("Error setting the capture mode for device '" << m_Name << "'"); - return false; - } - - return PcapLiveDevice::startCapture(onPacketArrives, onPacketArrivesUserCookie, intervalInSecondsToUpdateStats, onStatsUpdate, onStatsUpdateUserCookie); +bool WinPcapLiveDevice::startCapture(OnPacketArrivesCallback onPacketArrives, + void* onPacketArrivesUserCookie, + int intervalInSecondsToUpdateStats, + OnStatsUpdateCallback onStatsUpdate, + void* onStatsUpdateUserCookie) { + if (!m_DeviceOpened || m_PcapDescriptor == NULL) { + PCPP_LOG_ERROR("Device '" << m_Name << "' not opened"); + return false; + } + + // Put the interface in capture mode + if (pcap_setmode(m_PcapDescriptor, MODE_CAPT) < 0) { + PCPP_LOG_ERROR("Error setting the capture mode for device '" << m_Name + << "'"); + return false; + } + + return PcapLiveDevice::startCapture( + onPacketArrives, onPacketArrivesUserCookie, + intervalInSecondsToUpdateStats, onStatsUpdate, onStatsUpdateUserCookie); } -bool WinPcapLiveDevice::startCapture(int intervalInSecondsToUpdateStats, OnStatsUpdateCallback onStatsUpdate, void* onStatsUpdateUserCookie) -{ - if (!m_DeviceOpened || m_PcapDescriptor == NULL) - { - PCPP_LOG_ERROR("Device '" << m_Name << "' not opened"); - return false; - } - - //Put the interface in statistics mode - if (pcap_setmode(m_PcapDescriptor, MODE_STAT) < 0) - { - PCPP_LOG_ERROR("Error setting the statistics mode for device '" << m_Name << "'"); - return false; - } - - return PcapLiveDevice::startCapture(intervalInSecondsToUpdateStats, onStatsUpdate, onStatsUpdateUserCookie); +bool WinPcapLiveDevice::startCapture(int intervalInSecondsToUpdateStats, + OnStatsUpdateCallback onStatsUpdate, + void* onStatsUpdateUserCookie) { + if (!m_DeviceOpened || m_PcapDescriptor == NULL) { + PCPP_LOG_ERROR("Device '" << m_Name << "' not opened"); + return false; + } + + // Put the interface in statistics mode + if (pcap_setmode(m_PcapDescriptor, MODE_STAT) < 0) { + PCPP_LOG_ERROR("Error setting the statistics mode for device '" << m_Name + << "'"); + return false; + } + + return PcapLiveDevice::startCapture(intervalInSecondsToUpdateStats, + onStatsUpdate, onStatsUpdateUserCookie); } -int WinPcapLiveDevice::sendPackets(RawPacket* rawPacketsArr, int arrLength) -{ - if (!m_DeviceOpened || m_PcapDescriptor == NULL) - { - PCPP_LOG_ERROR("Device '" << m_Name << "' not opened"); - return 0; - } - - int dataSize = 0; - int packetsSent = 0; - for (int i = 0; i < arrLength; i++) - dataSize += rawPacketsArr[i].getRawDataLen(); - - pcap_send_queue* sendQueue = pcap_sendqueue_alloc(dataSize + arrLength*sizeof(pcap_pkthdr)); - PCPP_LOG_DEBUG("Allocated send queue of size " << (dataSize + arrLength*sizeof(pcap_pkthdr))); - struct pcap_pkthdr* packetHeader = new struct pcap_pkthdr[arrLength]; - for (int i = 0; i < arrLength; i++) - { - packetHeader[i].caplen = rawPacketsArr[i].getRawDataLen(); - packetHeader[i].len = rawPacketsArr[i].getRawDataLen(); - timespec packet_time = rawPacketsArr[i].getPacketTimeStamp(); - TIMESPEC_TO_TIMEVAL(&packetHeader[i].ts, &packet_time); - if (pcap_sendqueue_queue(sendQueue, &packetHeader[i], rawPacketsArr[i].getRawData()) == -1) - { - PCPP_LOG_ERROR("pcap_send_queue is too small for all packets. Sending only " << i << " packets"); - break; - } - packetsSent++; - } - - PCPP_LOG_DEBUG(packetsSent << " packets were queued successfully"); - - int res; - if ((res = pcap_sendqueue_transmit(m_PcapDescriptor, sendQueue, 0)) < (int)(sendQueue->len)) - { - PCPP_LOG_ERROR("An error occurred sending the packets: " << pcap_geterr(m_PcapDescriptor) << ". Only " << res << " bytes were sent"); - packetsSent = 0; - dataSize = 0; - for (int i = 0; i < arrLength; i++) - { - dataSize += rawPacketsArr[i].getRawDataLen(); - if (dataSize > res) - { - return packetsSent; - } - packetsSent++; - } - return packetsSent; - } - PCPP_LOG_DEBUG("Packets were sent successfully"); - - pcap_sendqueue_destroy(sendQueue); - PCPP_LOG_DEBUG("Send queue destroyed"); - - delete[] packetHeader; - return packetsSent; +int WinPcapLiveDevice::sendPackets(RawPacket* rawPacketsArr, int arrLength) { + if (!m_DeviceOpened || m_PcapDescriptor == NULL) { + PCPP_LOG_ERROR("Device '" << m_Name << "' not opened"); + return 0; + } + + int dataSize = 0; + int packetsSent = 0; + for (int i = 0; i < arrLength; i++) + dataSize += rawPacketsArr[i].getRawDataLen(); + + pcap_send_queue* sendQueue = + pcap_sendqueue_alloc(dataSize + arrLength * sizeof(pcap_pkthdr)); + PCPP_LOG_DEBUG("Allocated send queue of size " + << (dataSize + arrLength * sizeof(pcap_pkthdr))); + struct pcap_pkthdr* packetHeader = new struct pcap_pkthdr[arrLength]; + for (int i = 0; i < arrLength; i++) { + packetHeader[i].caplen = rawPacketsArr[i].getRawDataLen(); + packetHeader[i].len = rawPacketsArr[i].getRawDataLen(); + timespec packet_time = rawPacketsArr[i].getPacketTimeStamp(); + TIMESPEC_TO_TIMEVAL(&packetHeader[i].ts, &packet_time); + if (pcap_sendqueue_queue(sendQueue, &packetHeader[i], + rawPacketsArr[i].getRawData()) == -1) { + PCPP_LOG_ERROR( + "pcap_send_queue is too small for all packets. Sending only " + << i << " packets"); + break; + } + packetsSent++; + } + + PCPP_LOG_DEBUG(packetsSent << " packets were queued successfully"); + + int res; + if ((res = pcap_sendqueue_transmit(m_PcapDescriptor, sendQueue, 0)) < + (int)(sendQueue->len)) { + PCPP_LOG_ERROR("An error occurred sending the packets: " + << pcap_geterr(m_PcapDescriptor) << ". Only " << res + << " bytes were sent"); + packetsSent = 0; + dataSize = 0; + for (int i = 0; i < arrLength; i++) { + dataSize += rawPacketsArr[i].getRawDataLen(); + if (dataSize > res) { + return packetsSent; + } + packetsSent++; + } + return packetsSent; + } + PCPP_LOG_DEBUG("Packets were sent successfully"); + + pcap_sendqueue_destroy(sendQueue); + PCPP_LOG_DEBUG("Send queue destroyed"); + + delete[] packetHeader; + return packetsSent; } -bool WinPcapLiveDevice::setMinAmountOfDataToCopyFromKernelToApplication(int size) -{ - if (!m_DeviceOpened) - { - PCPP_LOG_ERROR("Device not opened"); - return false; - } - - if (pcap_setmintocopy(m_PcapDescriptor, size) != 0) - { - PCPP_LOG_ERROR("pcap_setmintocopy failed"); - return false; - } - m_MinAmountOfDataToCopyFromKernelToApplication = size; - return true; +bool WinPcapLiveDevice::setMinAmountOfDataToCopyFromKernelToApplication( + int size) { + if (!m_DeviceOpened) { + PCPP_LOG_ERROR("Device not opened"); + return false; + } + + if (pcap_setmintocopy(m_PcapDescriptor, size) != 0) { + PCPP_LOG_ERROR("pcap_setmintocopy failed"); + return false; + } + m_MinAmountOfDataToCopyFromKernelToApplication = size; + return true; } } // namespace pcpp diff --git a/Pcap++/src/XdpDevice.cpp b/Pcap++/src/XdpDevice.cpp index b585da3b31..01bb1aa359 100644 --- a/Pcap++/src/XdpDevice.cpp +++ b/Pcap++/src/XdpDevice.cpp @@ -3,612 +3,597 @@ #include "GeneralUtils.h" #include "Logger.h" #include "Packet.h" +#include #include #include +#include #include #include +#include #include #include -#include -#include -#include -namespace pcpp -{ +namespace pcpp { struct xsk_umem_info { - struct xsk_ring_prod fq; - struct xsk_ring_cons cq; - struct xsk_umem *umem; + struct xsk_ring_prod fq; + struct xsk_ring_cons cq; + struct xsk_umem* umem; }; -struct xsk_socket_info -{ - struct xsk_ring_cons rx; - struct xsk_ring_prod tx; - struct xsk_socket *xsk; +struct xsk_socket_info { + struct xsk_ring_cons rx; + struct xsk_ring_prod tx; + struct xsk_socket* xsk; }; -#define DEFAULT_UMEM_NUM_FRAMES (XSK_RING_PROD__DEFAULT_NUM_DESCS * 2) -#define DEFAULT_FILL_RING_SIZE (XSK_RING_PROD__DEFAULT_NUM_DESCS * 2) +#define DEFAULT_UMEM_NUM_FRAMES (XSK_RING_PROD__DEFAULT_NUM_DESCS * 2) +#define DEFAULT_FILL_RING_SIZE (XSK_RING_PROD__DEFAULT_NUM_DESCS * 2) #define DEFAULT_COMPLETION_RING_SIZE XSK_RING_PROD__DEFAULT_NUM_DESCS -#define DEFAULT_BATCH_SIZE 64 -#define IS_POWER_OF_TWO(num) (num && ((num & (num - 1)) == 0)) +#define DEFAULT_BATCH_SIZE 64 +#define IS_POWER_OF_TWO(num) (num && ((num & (num - 1)) == 0)) + +XdpDevice::XdpUmem::XdpUmem(uint16_t numFrames, uint16_t frameSize, + uint32_t fillRingSize, + uint32_t completionRingSize) { + size_t bufferSize = numFrames * frameSize; + + if (posix_memalign(&m_Buffer, getpagesize(), bufferSize)) { + throw std::runtime_error("Could not allocate buffer memory for UMEM"); + } + + struct xsk_umem_config cfg = {.fill_size = fillRingSize, + .comp_size = completionRingSize, + .frame_size = frameSize, + .frame_headroom = + XSK_UMEM__DEFAULT_FRAME_HEADROOM, + .flags = 0}; + + struct xsk_umem_info* umem = new xsk_umem_info; + memset(umem, 0, sizeof(xsk_umem_info)); + + int ret = xsk_umem__create(&umem->umem, m_Buffer, bufferSize, &umem->fq, + &umem->cq, &cfg); + if (ret) { + throw std::runtime_error( + "Could not allocate UMEM - xsk_umem__create() returned " + + std::to_string(ret)); + } + + m_UmemInfo = umem; + + for (uint16_t i = 0; i < numFrames; i++) { + m_FreeFrames.push_back(i * frameSize); + } + + m_FrameSize = frameSize; + m_FrameCount = numFrames; +} +XdpDevice::XdpUmem::~XdpUmem() { + xsk_umem__delete(static_cast(m_UmemInfo)->umem); + free(m_Buffer); +} -XdpDevice::XdpUmem::XdpUmem(uint16_t numFrames, uint16_t frameSize, uint32_t fillRingSize, uint32_t completionRingSize) -{ - size_t bufferSize = numFrames * frameSize; +const uint8_t* XdpDevice::XdpUmem::getDataPtr(uint64_t addr) const { + return static_cast(xsk_umem__get_data(m_Buffer, addr)); +} - if (posix_memalign(&m_Buffer, getpagesize(), bufferSize)) - { - throw std::runtime_error("Could not allocate buffer memory for UMEM"); - } +void XdpDevice::XdpUmem::setData(uint64_t addr, const uint8_t* data, + size_t dataLen) { + auto dataPtr = static_cast(xsk_umem__get_data(m_Buffer, addr)); + memcpy(dataPtr, data, dataLen); +} - struct xsk_umem_config cfg = { - .fill_size = fillRingSize, - .comp_size = completionRingSize, - .frame_size = frameSize, - .frame_headroom = XSK_UMEM__DEFAULT_FRAME_HEADROOM, - .flags = 0 - }; +std::pair> +XdpDevice::XdpUmem::allocateFrames(uint32_t count) { + if (m_FreeFrames.size() < count) { + PCPP_LOG_ERROR("Not enough frames to allocate. Requested: " + << count << ", available: " << m_FreeFrames.size()); + return {false, {}}; + } + + std::vector result; + for (uint32_t i = 0; i < count; i++) { + result.push_back(m_FreeFrames.back()); + m_FreeFrames.pop_back(); + } + + return {true, result}; +} - struct xsk_umem_info* umem = new xsk_umem_info; - memset(umem, 0, sizeof(xsk_umem_info)); +void XdpDevice::XdpUmem::freeFrame(uint64_t addr) { + auto frame = (uint64_t)((addr / m_FrameSize) * m_FrameSize); + m_FreeFrames.push_back(frame); +} - int ret = xsk_umem__create(&umem->umem, m_Buffer, bufferSize, &umem->fq, &umem->cq, &cfg); - if (ret) - { - throw std::runtime_error("Could not allocate UMEM - xsk_umem__create() returned " + std::to_string(ret)); - } +XdpDevice::XdpDevice(std::string interfaceName) + : m_InterfaceName(std::move(interfaceName)), m_Config(nullptr), + m_ReceivingPackets(false), m_Umem(nullptr), m_SocketInfo(nullptr) { + memset(&m_Stats, 0, sizeof(m_Stats)); + memset(&m_PrevStats, 0, sizeof(m_PrevStats)); +} - m_UmemInfo = umem; +XdpDevice::~XdpDevice() { close(); } - for (uint16_t i = 0; i < numFrames; i++) - { - m_FreeFrames.push_back(i * frameSize); - } +bool XdpDevice::receivePackets(OnPacketsArrive onPacketsArrive, + void* onPacketsArriveUserCookie, int timeoutMS) { + if (!m_DeviceOpened) { + PCPP_LOG_ERROR("Device is not open"); + return false; + } - m_FrameSize = frameSize; - m_FrameCount = numFrames; -} + auto socketInfo = static_cast(m_SocketInfo); -XdpDevice::XdpUmem::~XdpUmem() -{ - xsk_umem__delete(static_cast(m_UmemInfo)->umem); - free(m_Buffer); -} + m_ReceivingPackets = true; + uint32_t rxId = 0; -const uint8_t* XdpDevice::XdpUmem::getDataPtr(uint64_t addr) const -{ - return static_cast(xsk_umem__get_data(m_Buffer, addr)); -} + pollfd pollFds[1]; + pollFds[0] = {.fd = xsk_socket__fd(socketInfo->xsk), .events = POLLIN}; -void XdpDevice::XdpUmem::setData(uint64_t addr, const uint8_t* data, size_t dataLen) -{ - auto dataPtr = static_cast(xsk_umem__get_data(m_Buffer, addr)); - memcpy(dataPtr, data, dataLen); -} + while (m_ReceivingPackets) { + checkCompletionRing(); -std::pair> XdpDevice::XdpUmem::allocateFrames(uint32_t count) -{ - if (m_FreeFrames.size() < count) - { - PCPP_LOG_ERROR("Not enough frames to allocate. Requested: " << count << ", available: " << m_FreeFrames.size()); - return {false, {} }; - } - - std::vector result; - for (uint32_t i = 0; i < count; i++) - { - result.push_back(m_FreeFrames.back()); - m_FreeFrames.pop_back(); - } - - return {true, result}; -} + auto pollResult = poll(pollFds, 1, timeoutMS); + if (pollResult == 0 && timeoutMS != 0) { + m_Stats.rxPollTimeout++; + m_ReceivingPackets = false; + return true; + } + if (pollResult < 0) { + PCPP_LOG_ERROR("poll() returned an error: " << errno); + m_ReceivingPackets = false; + return false; + } -void XdpDevice::XdpUmem::freeFrame(uint64_t addr) -{ - auto frame = (uint64_t )((addr / m_FrameSize) * m_FrameSize); - m_FreeFrames.push_back(frame); -} + uint32_t receivedPacketsCount = + xsk_ring_cons__peek(&socketInfo->rx, m_Config->rxTxBatchSize, &rxId); -XdpDevice::XdpDevice(std::string interfaceName) : - m_InterfaceName(std::move(interfaceName)), m_Config(nullptr), m_ReceivingPackets(false), m_Umem(nullptr), m_SocketInfo(nullptr) -{ - memset(&m_Stats, 0, sizeof(m_Stats)); - memset(&m_PrevStats, 0, sizeof(m_PrevStats)); -} + if (!receivedPacketsCount) { + continue; + } -XdpDevice::~XdpDevice() -{ - close(); -} + m_Stats.rxPackets += receivedPacketsCount; -bool XdpDevice::receivePackets(OnPacketsArrive onPacketsArrive, void* onPacketsArriveUserCookie, int timeoutMS) -{ - if (!m_DeviceOpened) - { - PCPP_LOG_ERROR("Device is not open"); - return false; - } - - auto socketInfo = static_cast(m_SocketInfo); - - m_ReceivingPackets = true; - uint32_t rxId = 0; - - pollfd pollFds[1]; - pollFds[0] = { - .fd = xsk_socket__fd(socketInfo->xsk), - .events = POLLIN - }; - - while (m_ReceivingPackets) - { - checkCompletionRing(); - - auto pollResult = poll(pollFds, 1, timeoutMS); - if (pollResult == 0 && timeoutMS != 0) - { - m_Stats.rxPollTimeout++; - m_ReceivingPackets = false; - return true; - } - if (pollResult < 0) - { - PCPP_LOG_ERROR("poll() returned an error: " << errno); - m_ReceivingPackets = false; - return false; - } - - uint32_t receivedPacketsCount = xsk_ring_cons__peek(&socketInfo->rx, m_Config->rxTxBatchSize, &rxId); - - if (!receivedPacketsCount) - { - continue; - } - - m_Stats.rxPackets += receivedPacketsCount; - - RawPacket rawPacketsArr[receivedPacketsCount]; - - for (uint32_t i = 0; i < receivedPacketsCount; i++) - { - uint64_t addr = xsk_ring_cons__rx_desc(&socketInfo->rx, rxId + i)->addr; - uint32_t len = xsk_ring_cons__rx_desc(&socketInfo->rx, rxId + i)->len; - - auto data = m_Umem->getDataPtr(addr); - timespec ts; - clock_gettime(CLOCK_REALTIME, &ts); - rawPacketsArr[i].initWithRawData(data, static_cast(len), ts); - - m_Stats.rxBytes += len; - - m_Umem->freeFrame(addr); - } - - onPacketsArrive(rawPacketsArr, receivedPacketsCount, this, onPacketsArriveUserCookie); - - xsk_ring_cons__release(&socketInfo->rx, receivedPacketsCount); - m_Stats.rxRingId = rxId + receivedPacketsCount; - - if (!populateFillRing(receivedPacketsCount, rxId)) - { - m_ReceivingPackets = false; - } - } - - return true; -} + RawPacket rawPacketsArr[receivedPacketsCount]; + + for (uint32_t i = 0; i < receivedPacketsCount; i++) { + uint64_t addr = xsk_ring_cons__rx_desc(&socketInfo->rx, rxId + i)->addr; + uint32_t len = xsk_ring_cons__rx_desc(&socketInfo->rx, rxId + i)->len; + + auto data = m_Umem->getDataPtr(addr); + timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + rawPacketsArr[i].initWithRawData(data, static_cast(len), ts); + + m_Stats.rxBytes += len; + + m_Umem->freeFrame(addr); + } + + onPacketsArrive(rawPacketsArr, receivedPacketsCount, this, + onPacketsArriveUserCookie); + + xsk_ring_cons__release(&socketInfo->rx, receivedPacketsCount); + m_Stats.rxRingId = rxId + receivedPacketsCount; -void XdpDevice::stopReceivePackets() -{ - m_ReceivingPackets = false; + if (!populateFillRing(receivedPacketsCount, rxId)) { + m_ReceivingPackets = false; + } + } + + return true; } -bool XdpDevice::sendPackets(const std::function& getPacketAt, const std::function& getPacketCount, bool waitForTxCompletion, int waitForTxCompletionTimeoutMS) -{ - if (!m_DeviceOpened) - { - PCPP_LOG_ERROR("Device is not open"); - return false; - } - - auto socketInfo = static_cast(m_SocketInfo); - - checkCompletionRing(); - - uint32_t txId = 0; - uint32_t packetCount = getPacketCount(); - - auto frameResponse = m_Umem->allocateFrames(packetCount); - if (!frameResponse.first) - { - return false; - } - - if (xsk_ring_prod__reserve(&socketInfo->tx, packetCount, &txId) < packetCount) - { - for (auto frame : frameResponse.second) - { - m_Umem->freeFrame(frame); - } - PCPP_LOG_ERROR("Cannot reserve " << packetCount << " tx slots"); - return false; - } - - for (uint32_t i = 0; i < packetCount; i++) - { - if (getPacketAt(i).getRawDataLen() > m_Umem->getFrameSize()) - { - PCPP_LOG_ERROR("Cannot send packets with data length (" << getPacketAt(i).getRawDataLen() << ") greater than UMEM frame size (" << m_Umem->getFrameSize() << ")"); - return false; - } - } - - uint64_t sentBytes = 0; - for (uint32_t i = 0; i < packetCount; i++) - { - uint64_t frame = frameResponse.second[i]; - m_Umem->setData(frame, getPacketAt(i).getRawData(), getPacketAt(i).getRawDataLen()); - - struct xdp_desc* txDesc = xsk_ring_prod__tx_desc(&socketInfo->tx, txId + i); - txDesc->addr = frame; - txDesc->len = getPacketAt(i).getRawDataLen(); - - sentBytes += txDesc->len; - - } - - xsk_ring_prod__submit(&socketInfo->tx, packetCount); - m_Stats.txSentPackets += packetCount; - m_Stats.txSentBytes += sentBytes; - m_Stats.txRingId = txId + packetCount; - - if (waitForTxCompletion) - { - uint32_t completedPackets = checkCompletionRing(); - - pollfd pollFds[1]; - pollFds[0] = { - .fd = xsk_socket__fd(socketInfo->xsk), - .events = POLLOUT - }; - - while (completedPackets < packetCount) - { - auto pollResult = poll(pollFds, 1, waitForTxCompletionTimeoutMS); - if (pollResult == 0 && waitForTxCompletionTimeoutMS != 0) - { - PCPP_LOG_ERROR("Wait for TX completion timed out"); - return false; - } - if (pollResult < 0) - { - PCPP_LOG_ERROR("poll() returned an error: " << errno); - return false; - } - - completedPackets += checkCompletionRing(); - } - } - - return true; +void XdpDevice::stopReceivePackets() { m_ReceivingPackets = false; } + +bool XdpDevice::sendPackets( + const std::function& getPacketAt, + const std::function& getPacketCount, bool waitForTxCompletion, + int waitForTxCompletionTimeoutMS) { + if (!m_DeviceOpened) { + PCPP_LOG_ERROR("Device is not open"); + return false; + } + + auto socketInfo = static_cast(m_SocketInfo); + + checkCompletionRing(); + + uint32_t txId = 0; + uint32_t packetCount = getPacketCount(); + + auto frameResponse = m_Umem->allocateFrames(packetCount); + if (!frameResponse.first) { + return false; + } + + if (xsk_ring_prod__reserve(&socketInfo->tx, packetCount, &txId) < + packetCount) { + for (auto frame : frameResponse.second) { + m_Umem->freeFrame(frame); + } + PCPP_LOG_ERROR("Cannot reserve " << packetCount << " tx slots"); + return false; + } + + for (uint32_t i = 0; i < packetCount; i++) { + if (getPacketAt(i).getRawDataLen() > m_Umem->getFrameSize()) { + PCPP_LOG_ERROR("Cannot send packets with data length (" + << getPacketAt(i).getRawDataLen() + << ") greater than UMEM frame size (" + << m_Umem->getFrameSize() << ")"); + return false; + } + } + + uint64_t sentBytes = 0; + for (uint32_t i = 0; i < packetCount; i++) { + uint64_t frame = frameResponse.second[i]; + m_Umem->setData(frame, getPacketAt(i).getRawData(), + getPacketAt(i).getRawDataLen()); + + struct xdp_desc* txDesc = xsk_ring_prod__tx_desc(&socketInfo->tx, txId + i); + txDesc->addr = frame; + txDesc->len = getPacketAt(i).getRawDataLen(); + + sentBytes += txDesc->len; + } + + xsk_ring_prod__submit(&socketInfo->tx, packetCount); + m_Stats.txSentPackets += packetCount; + m_Stats.txSentBytes += sentBytes; + m_Stats.txRingId = txId + packetCount; + + if (waitForTxCompletion) { + uint32_t completedPackets = checkCompletionRing(); + + pollfd pollFds[1]; + pollFds[0] = {.fd = xsk_socket__fd(socketInfo->xsk), .events = POLLOUT}; + + while (completedPackets < packetCount) { + auto pollResult = poll(pollFds, 1, waitForTxCompletionTimeoutMS); + if (pollResult == 0 && waitForTxCompletionTimeoutMS != 0) { + PCPP_LOG_ERROR("Wait for TX completion timed out"); + return false; + } + if (pollResult < 0) { + PCPP_LOG_ERROR("poll() returned an error: " << errno); + return false; + } + + completedPackets += checkCompletionRing(); + } + } + + return true; } -bool XdpDevice::sendPackets(const RawPacketVector& packets, bool waitForTxCompletion, int waitForTxCompletionTimeoutMS) -{ - return sendPackets([&](uint32_t i) { return *packets.at(static_cast(i)); }, [&]() { return packets.size(); }, waitForTxCompletion, waitForTxCompletionTimeoutMS); +bool XdpDevice::sendPackets(const RawPacketVector& packets, + bool waitForTxCompletion, + int waitForTxCompletionTimeoutMS) { + return sendPackets( + [&](uint32_t i) { return *packets.at(static_cast(i)); }, + [&]() { return packets.size(); }, waitForTxCompletion, + waitForTxCompletionTimeoutMS); } -bool XdpDevice::sendPackets(RawPacket packets[], size_t packetCount, bool waitForTxCompletion, int waitForTxCompletionTimeoutMS) -{ - return sendPackets([&](uint32_t i) { return packets[i]; }, [&]() { return static_cast(packetCount); }, waitForTxCompletion, waitForTxCompletionTimeoutMS); +bool XdpDevice::sendPackets(RawPacket packets[], size_t packetCount, + bool waitForTxCompletion, + int waitForTxCompletionTimeoutMS) { + return sendPackets([&](uint32_t i) { return packets[i]; }, + [&]() { return static_cast(packetCount); }, + waitForTxCompletion, waitForTxCompletionTimeoutMS); } -bool XdpDevice::populateFillRing(uint32_t count, uint32_t rxId) -{ - auto frameResponse = m_Umem->allocateFrames(count); - if (!frameResponse.first) - { - return false; - } - - bool result = populateFillRing(frameResponse.second, rxId); - if (!result) - { - for (auto frame : frameResponse.second) - { - m_Umem->freeFrame(frame); - } - } - - return result; +bool XdpDevice::populateFillRing(uint32_t count, uint32_t rxId) { + auto frameResponse = m_Umem->allocateFrames(count); + if (!frameResponse.first) { + return false; + } + + bool result = populateFillRing(frameResponse.second, rxId); + if (!result) { + for (auto frame : frameResponse.second) { + m_Umem->freeFrame(frame); + } + } + + return result; } -bool XdpDevice::populateFillRing(const std::vector& addresses, uint32_t rxId) -{ - auto umem = static_cast(m_Umem->getInfo()); - auto count = static_cast(addresses.size()); +bool XdpDevice::populateFillRing(const std::vector& addresses, + uint32_t rxId) { + auto umem = static_cast(m_Umem->getInfo()); + auto count = static_cast(addresses.size()); - uint32_t ret = xsk_ring_prod__reserve(&umem->fq,count, &rxId); - if (ret != count) - { - PCPP_LOG_ERROR("xsk_ring_prod__reserve returned: " << ret << "; expected: " << count); - return false; - } + uint32_t ret = xsk_ring_prod__reserve(&umem->fq, count, &rxId); + if (ret != count) { + PCPP_LOG_ERROR( + "xsk_ring_prod__reserve returned: " << ret << "; expected: " << count); + return false; + } - for (uint32_t i = 0; i < count; i++) - { - *xsk_ring_prod__fill_addr(&umem->fq, rxId + i) = addresses[i]; - } + for (uint32_t i = 0; i < count; i++) { + *xsk_ring_prod__fill_addr(&umem->fq, rxId + i) = addresses[i]; + } - xsk_ring_prod__submit(&umem->fq, count); - m_Stats.fqRingId = rxId + count; + xsk_ring_prod__submit(&umem->fq, count); + m_Stats.fqRingId = rxId + count; - return true; + return true; } -uint32_t XdpDevice::checkCompletionRing() -{ - uint32_t cqId = 0; - auto umemInfo = static_cast(m_Umem->getInfo()); - - auto socketInfo = static_cast(m_SocketInfo); - if (xsk_ring_prod__needs_wakeup(&socketInfo->tx)) - { - sendto(xsk_socket__fd(socketInfo->xsk), NULL, 0, MSG_DONTWAIT, NULL, 0); - } - - uint32_t completedCount = xsk_ring_cons__peek(&umemInfo->cq, m_Config->rxTxBatchSize, &cqId); - - if (completedCount) - { - for (uint32_t i = 0; i < completedCount; i++) - { - uint64_t addr = *xsk_ring_cons__comp_addr(&umemInfo->cq, cqId + i); - m_Umem->freeFrame(addr); - } - - xsk_ring_cons__release(&umemInfo->cq, completedCount); - m_Stats.cqRingId = cqId + completedCount; - } - - m_Stats.txCompletedPackets += completedCount; - return completedCount; -} +uint32_t XdpDevice::checkCompletionRing() { + uint32_t cqId = 0; + auto umemInfo = static_cast(m_Umem->getInfo()); + + auto socketInfo = static_cast(m_SocketInfo); + if (xsk_ring_prod__needs_wakeup(&socketInfo->tx)) { + sendto(xsk_socket__fd(socketInfo->xsk), NULL, 0, MSG_DONTWAIT, NULL, 0); + } -bool XdpDevice::configureSocket() -{ - auto socketInfo = new xsk_socket_info(); - - auto umemInfo = static_cast(m_Umem->getInfo()); - - struct xsk_socket_config xskConfig; - xskConfig.rx_size = m_Config->txSize; - xskConfig.tx_size = m_Config->rxSize; - xskConfig.libbpf_flags = 0; - xskConfig.xdp_flags = 0; - xskConfig.bind_flags = 0; - if (m_Config->attachMode == XdpDeviceConfiguration::SkbMode) - { - xskConfig.xdp_flags = XDP_FLAGS_SKB_MODE; - xskConfig.bind_flags &= ~XDP_ZEROCOPY; - xskConfig.bind_flags |= XDP_COPY; - } - else if (m_Config->attachMode == XdpDeviceConfiguration::DriverMode) - { - xskConfig.xdp_flags = XDP_FLAGS_DRV_MODE; - } - - int ret = xsk_socket__create(&socketInfo->xsk, m_InterfaceName.c_str(), 0, umemInfo->umem, - &socketInfo->rx, &socketInfo->tx, &xskConfig); - if (ret) - { - PCPP_LOG_ERROR("xsk_socket__create returned an error: " << ret); - delete socketInfo; - return false; - } - - m_SocketInfo = socketInfo; - return true; + uint32_t completedCount = + xsk_ring_cons__peek(&umemInfo->cq, m_Config->rxTxBatchSize, &cqId); + + if (completedCount) { + for (uint32_t i = 0; i < completedCount; i++) { + uint64_t addr = *xsk_ring_cons__comp_addr(&umemInfo->cq, cqId + i); + m_Umem->freeFrame(addr); + } + + xsk_ring_cons__release(&umemInfo->cq, completedCount); + m_Stats.cqRingId = cqId + completedCount; + } + + m_Stats.txCompletedPackets += completedCount; + return completedCount; } -bool XdpDevice::initUmem() -{ - m_Umem = new XdpUmem(m_Config->umemNumFrames, m_Config->umemFrameSize, m_Config->fillRingSize, m_Config->completionRingSize); - return true; +bool XdpDevice::configureSocket() { + auto socketInfo = new xsk_socket_info(); + + auto umemInfo = static_cast(m_Umem->getInfo()); + + struct xsk_socket_config xskConfig; + xskConfig.rx_size = m_Config->txSize; + xskConfig.tx_size = m_Config->rxSize; + xskConfig.libbpf_flags = 0; + xskConfig.xdp_flags = 0; + xskConfig.bind_flags = 0; + if (m_Config->attachMode == XdpDeviceConfiguration::SkbMode) { + xskConfig.xdp_flags = XDP_FLAGS_SKB_MODE; + xskConfig.bind_flags &= ~XDP_ZEROCOPY; + xskConfig.bind_flags |= XDP_COPY; + } else if (m_Config->attachMode == XdpDeviceConfiguration::DriverMode) { + xskConfig.xdp_flags = XDP_FLAGS_DRV_MODE; + } + + int ret = xsk_socket__create(&socketInfo->xsk, m_InterfaceName.c_str(), 0, + umemInfo->umem, &socketInfo->rx, &socketInfo->tx, + &xskConfig); + if (ret) { + PCPP_LOG_ERROR("xsk_socket__create returned an error: " << ret); + delete socketInfo; + return false; + } + + m_SocketInfo = socketInfo; + return true; } -bool XdpDevice::initConfig() -{ - if (!m_Config) - { - m_Config = new XdpDeviceConfiguration(); - } - - uint16_t numFrames = m_Config->umemNumFrames ? m_Config->umemNumFrames : DEFAULT_UMEM_NUM_FRAMES; - uint16_t frameSize = m_Config->umemFrameSize ? m_Config->umemFrameSize : getpagesize(); - uint32_t fillRingSize = m_Config->fillRingSize ? m_Config->fillRingSize : DEFAULT_FILL_RING_SIZE; - uint32_t completionRingSize = m_Config->completionRingSize ? m_Config->completionRingSize : DEFAULT_COMPLETION_RING_SIZE; - uint32_t rxSize = m_Config->rxSize ? m_Config->rxSize : XSK_RING_CONS__DEFAULT_NUM_DESCS; - uint32_t txSize = m_Config->txSize ? m_Config->txSize : XSK_RING_PROD__DEFAULT_NUM_DESCS; - uint32_t batchSize = m_Config->rxTxBatchSize ? m_Config->rxTxBatchSize : DEFAULT_BATCH_SIZE; - - if (frameSize != getpagesize()) - { - PCPP_LOG_ERROR("UMEM frame size must match the memory page size (" << getpagesize() << ")"); - return false; - } - - if (!(IS_POWER_OF_TWO(fillRingSize) && IS_POWER_OF_TWO(completionRingSize) && IS_POWER_OF_TWO(rxSize) && IS_POWER_OF_TWO(txSize))) - { - PCPP_LOG_ERROR("All ring sizes (fill ring, completion ring, rx ring, tx ring) should be a power of two"); - return false; - } - - if (fillRingSize > numFrames) - { - PCPP_LOG_ERROR("Fill ring size (" << fillRingSize << ") must be lower or equal to the total number of UMEM frames (" << numFrames << ")"); - return false; - } - - if (completionRingSize > numFrames) - { - PCPP_LOG_ERROR("Completion ring size (" << completionRingSize << ") must be lower or equal to the total number of UMEM frames (" << numFrames << ")"); - return false; - } - - if (rxSize > numFrames) - { - PCPP_LOG_ERROR("RX size (" << rxSize << ") must be lower or equal to the total number of UMEM frames (" << numFrames << ")"); - return false; - } - - if (txSize > numFrames) - { - PCPP_LOG_ERROR("TX size (" << txSize << ") must be lower or equal to the total number of UMEM frames (" << numFrames << ")"); - return false; - } - - if (batchSize > rxSize || batchSize > txSize) - { - PCPP_LOG_ERROR("RX/TX batch size (" << batchSize << ") must be lower or equal to RX/TX ring size"); - return false; - } - - m_Config->umemNumFrames = numFrames; - m_Config->umemFrameSize = frameSize; - m_Config->fillRingSize = fillRingSize; - m_Config->completionRingSize = completionRingSize; - m_Config->rxSize = rxSize; - m_Config->txSize = txSize; - m_Config->rxTxBatchSize = batchSize; - - return true; +bool XdpDevice::initUmem() { + m_Umem = new XdpUmem(m_Config->umemNumFrames, m_Config->umemFrameSize, + m_Config->fillRingSize, m_Config->completionRingSize); + return true; } -bool XdpDevice::open() -{ - if (m_DeviceOpened) - { - PCPP_LOG_ERROR("Device already opened"); - return false; - } - - if (!(initConfig() && - initUmem() && - populateFillRing(std::min(m_Config->fillRingSize, static_cast(m_Config->umemNumFrames / 2))) && - configureSocket())) - { - if (m_Umem) - { - delete m_Umem; - m_Umem = nullptr; - } - if (m_Config) - { - delete m_Config; - m_Config = nullptr; - } - return false; - } - - memset(&m_Stats, 0, sizeof(m_Stats)); - memset(&m_PrevStats, 0 ,sizeof(m_PrevStats)); - - m_DeviceOpened = true; - return m_DeviceOpened; +bool XdpDevice::initConfig() { + if (!m_Config) { + m_Config = new XdpDeviceConfiguration(); + } + + uint16_t numFrames = m_Config->umemNumFrames ? m_Config->umemNumFrames + : DEFAULT_UMEM_NUM_FRAMES; + uint16_t frameSize = + m_Config->umemFrameSize ? m_Config->umemFrameSize : getpagesize(); + uint32_t fillRingSize = + m_Config->fillRingSize ? m_Config->fillRingSize : DEFAULT_FILL_RING_SIZE; + uint32_t completionRingSize = m_Config->completionRingSize + ? m_Config->completionRingSize + : DEFAULT_COMPLETION_RING_SIZE; + uint32_t rxSize = + m_Config->rxSize ? m_Config->rxSize : XSK_RING_CONS__DEFAULT_NUM_DESCS; + uint32_t txSize = + m_Config->txSize ? m_Config->txSize : XSK_RING_PROD__DEFAULT_NUM_DESCS; + uint32_t batchSize = + m_Config->rxTxBatchSize ? m_Config->rxTxBatchSize : DEFAULT_BATCH_SIZE; + + if (frameSize != getpagesize()) { + PCPP_LOG_ERROR("UMEM frame size must match the memory page size (" + << getpagesize() << ")"); + return false; + } + + if (!(IS_POWER_OF_TWO(fillRingSize) && IS_POWER_OF_TWO(completionRingSize) && + IS_POWER_OF_TWO(rxSize) && IS_POWER_OF_TWO(txSize))) { + PCPP_LOG_ERROR("All ring sizes (fill ring, completion ring, rx ring, tx " + "ring) should be a power of two"); + return false; + } + + if (fillRingSize > numFrames) { + PCPP_LOG_ERROR( + "Fill ring size (" + << fillRingSize + << ") must be lower or equal to the total number of UMEM frames (" + << numFrames << ")"); + return false; + } + + if (completionRingSize > numFrames) { + PCPP_LOG_ERROR( + "Completion ring size (" + << completionRingSize + << ") must be lower or equal to the total number of UMEM frames (" + << numFrames << ")"); + return false; + } + + if (rxSize > numFrames) { + PCPP_LOG_ERROR( + "RX size (" + << rxSize + << ") must be lower or equal to the total number of UMEM frames (" + << numFrames << ")"); + return false; + } + + if (txSize > numFrames) { + PCPP_LOG_ERROR( + "TX size (" + << txSize + << ") must be lower or equal to the total number of UMEM frames (" + << numFrames << ")"); + return false; + } + + if (batchSize > rxSize || batchSize > txSize) { + PCPP_LOG_ERROR("RX/TX batch size (" + << batchSize + << ") must be lower or equal to RX/TX ring size"); + return false; + } + + m_Config->umemNumFrames = numFrames; + m_Config->umemFrameSize = frameSize; + m_Config->fillRingSize = fillRingSize; + m_Config->completionRingSize = completionRingSize; + m_Config->rxSize = rxSize; + m_Config->txSize = txSize; + m_Config->rxTxBatchSize = batchSize; + + return true; } -bool XdpDevice::open(const XdpDeviceConfiguration& config) -{ - m_Config = new XdpDeviceConfiguration(config); - return open(); +bool XdpDevice::open() { + if (m_DeviceOpened) { + PCPP_LOG_ERROR("Device already opened"); + return false; + } + + if (!(initConfig() && initUmem() && + populateFillRing( + std::min(m_Config->fillRingSize, + static_cast(m_Config->umemNumFrames / 2))) && + configureSocket())) { + if (m_Umem) { + delete m_Umem; + m_Umem = nullptr; + } + if (m_Config) { + delete m_Config; + m_Config = nullptr; + } + return false; + } + + memset(&m_Stats, 0, sizeof(m_Stats)); + memset(&m_PrevStats, 0, sizeof(m_PrevStats)); + + m_DeviceOpened = true; + return m_DeviceOpened; } -void XdpDevice::close() -{ - if (m_DeviceOpened) - { - auto socketInfo = static_cast(m_SocketInfo); - xsk_socket__delete(socketInfo->xsk); - m_DeviceOpened = false; - delete m_Umem; - delete m_Config; - m_Config = nullptr; - m_Umem = nullptr; - } +bool XdpDevice::open(const XdpDeviceConfiguration& config) { + m_Config = new XdpDeviceConfiguration(config); + return open(); } -bool XdpDevice::getSocketStats() -{ - auto socketInfo = static_cast(m_SocketInfo); - int fd = xsk_socket__fd(socketInfo->xsk); - - struct xdp_statistics socketStats; - socklen_t optlen = sizeof(socketStats); - - int err = getsockopt(fd, SOL_XDP, XDP_STATISTICS, &socketStats, &optlen); - if (err) - { - PCPP_LOG_ERROR("Error getting stats from socket, return error: " << err); - return false; - } - - if (optlen != sizeof(struct xdp_statistics)) - { - PCPP_LOG_ERROR("Error getting stats from socket: optlen (" << optlen << ") != expected size (" << sizeof(struct xdp_statistics) << ")"); - return false; - } - - m_Stats.rxDroppedInvalidPackets = socketStats.rx_invalid_descs; - m_Stats.rxDroppedRxRingFullPackets = socketStats.rx_ring_full; - m_Stats.rxDroppedFillRingPackets = socketStats.rx_fill_ring_empty_descs; - m_Stats.rxDroppedTotalPackets = m_Stats.rxDroppedFillRingPackets + m_Stats.rxDroppedRxRingFullPackets + m_Stats.rxDroppedInvalidPackets + socketStats.rx_dropped; - m_Stats.txDroppedInvalidPackets = socketStats.tx_invalid_descs; - - return true; +void XdpDevice::close() { + if (m_DeviceOpened) { + auto socketInfo = static_cast(m_SocketInfo); + xsk_socket__delete(socketInfo->xsk); + m_DeviceOpened = false; + delete m_Umem; + delete m_Config; + m_Config = nullptr; + m_Umem = nullptr; + } } -#define nanosec_gap(begin, end) ((end.tv_sec - begin.tv_sec) * 1000000000.0 + (end.tv_nsec - begin.tv_nsec)) - -XdpDevice::XdpDeviceStats XdpDevice::getStatistics() -{ - timespec timestamp; - clock_gettime(CLOCK_MONOTONIC, ×tamp); - - m_Stats.timestamp = timestamp; - - if (m_DeviceOpened) - { - getSocketStats(); - m_Stats.umemFreeFrames = m_Umem->getFreeFrameCount(); - m_Stats.umemAllocatedFrames = m_Umem->getFrameCount() - m_Stats.umemFreeFrames; - } - else - { - m_Stats.umemFreeFrames = 0; - m_Stats.umemAllocatedFrames = 0; - } - - double secsElapsed = (double)nanosec_gap(m_PrevStats.timestamp, timestamp) / 1000000000.0; - m_Stats.rxPacketsPerSec = static_cast((m_Stats.rxPackets - m_PrevStats.rxPackets) / secsElapsed); - m_Stats.rxBytesPerSec = static_cast((m_Stats.rxBytes - m_PrevStats.rxBytes) / secsElapsed); - m_Stats.txSentPacketsPerSec = static_cast((m_Stats.txSentPackets - m_PrevStats.txSentPackets) / secsElapsed); - m_Stats.txSentBytesPerSec = static_cast((m_Stats.txSentBytes - m_PrevStats.txSentBytes) / secsElapsed); - m_Stats.txCompletedPacketsPerSec = static_cast((m_Stats.txCompletedPackets - m_PrevStats.txCompletedPackets) / secsElapsed); - - m_PrevStats.timestamp = timestamp; - m_PrevStats.rxPackets = m_Stats.rxPackets; - m_PrevStats.rxBytes = m_Stats.rxBytes; - m_PrevStats.txSentPackets = m_Stats.txSentPackets; - m_PrevStats.txSentBytes = m_Stats.txSentBytes; - m_PrevStats.txCompletedPackets = m_Stats.txCompletedPackets; - - return m_Stats; +bool XdpDevice::getSocketStats() { + auto socketInfo = static_cast(m_SocketInfo); + int fd = xsk_socket__fd(socketInfo->xsk); + + struct xdp_statistics socketStats; + socklen_t optlen = sizeof(socketStats); + + int err = getsockopt(fd, SOL_XDP, XDP_STATISTICS, &socketStats, &optlen); + if (err) { + PCPP_LOG_ERROR("Error getting stats from socket, return error: " << err); + return false; + } + + if (optlen != sizeof(struct xdp_statistics)) { + PCPP_LOG_ERROR("Error getting stats from socket: optlen (" + << optlen << ") != expected size (" + << sizeof(struct xdp_statistics) << ")"); + return false; + } + + m_Stats.rxDroppedInvalidPackets = socketStats.rx_invalid_descs; + m_Stats.rxDroppedRxRingFullPackets = socketStats.rx_ring_full; + m_Stats.rxDroppedFillRingPackets = socketStats.rx_fill_ring_empty_descs; + m_Stats.rxDroppedTotalPackets = + m_Stats.rxDroppedFillRingPackets + m_Stats.rxDroppedRxRingFullPackets + + m_Stats.rxDroppedInvalidPackets + socketStats.rx_dropped; + m_Stats.txDroppedInvalidPackets = socketStats.tx_invalid_descs; + + return true; } +#define nanosec_gap(begin, end) \ + ((end.tv_sec - begin.tv_sec) * 1000000000.0 + (end.tv_nsec - begin.tv_nsec)) + +XdpDevice::XdpDeviceStats XdpDevice::getStatistics() { + timespec timestamp; + clock_gettime(CLOCK_MONOTONIC, ×tamp); + + m_Stats.timestamp = timestamp; + + if (m_DeviceOpened) { + getSocketStats(); + m_Stats.umemFreeFrames = m_Umem->getFreeFrameCount(); + m_Stats.umemAllocatedFrames = + m_Umem->getFrameCount() - m_Stats.umemFreeFrames; + } else { + m_Stats.umemFreeFrames = 0; + m_Stats.umemAllocatedFrames = 0; + } + + double secsElapsed = + (double)nanosec_gap(m_PrevStats.timestamp, timestamp) / 1000000000.0; + m_Stats.rxPacketsPerSec = static_cast( + (m_Stats.rxPackets - m_PrevStats.rxPackets) / secsElapsed); + m_Stats.rxBytesPerSec = static_cast( + (m_Stats.rxBytes - m_PrevStats.rxBytes) / secsElapsed); + m_Stats.txSentPacketsPerSec = static_cast( + (m_Stats.txSentPackets - m_PrevStats.txSentPackets) / secsElapsed); + m_Stats.txSentBytesPerSec = static_cast( + (m_Stats.txSentBytes - m_PrevStats.txSentBytes) / secsElapsed); + m_Stats.txCompletedPacketsPerSec = static_cast( + (m_Stats.txCompletedPackets - m_PrevStats.txCompletedPackets) / + secsElapsed); + + m_PrevStats.timestamp = timestamp; + m_PrevStats.rxPackets = m_Stats.rxPackets; + m_PrevStats.rxBytes = m_Stats.rxBytes; + m_PrevStats.txSentPackets = m_Stats.txSentPackets; + m_PrevStats.txSentBytes = m_Stats.txSentBytes; + m_PrevStats.txCompletedPackets = m_Stats.txCompletedPackets; + + return m_Stats; } + +} // namespace pcpp diff --git a/run_format.bash b/run_format.bash new file mode 100755 index 0000000000..92357437e8 --- /dev/null +++ b/run_format.bash @@ -0,0 +1,6 @@ +#!/bin/bash + +list=('Common++' 'Packet++' 'Pcap++' 'Examples') +for folder in "${list[@]}"; do + find "$folder" \( -name "*.cpp" -o -name "*.h" \) -exec clang-format -i {} + +done