From 09f479f890b8b4e2023ca5e71d3068f9fa469c84 Mon Sep 17 00:00:00 2001 From: seladb Date: Mon, 29 Jul 2024 20:07:05 -0700 Subject: [PATCH] Run `clang-format` for Examples and Tutorials (#1464) --- .pre-commit-config.yaml | 2 +- Examples/ArpSpoofing/main.cpp | 156 +- Examples/Arping/main.cpp | 232 ++- Examples/DNSResolver/main.cpp | 216 +-- Examples/DnsSpoofing/main.cpp | 269 ++-- Examples/DpdkBridge/AppWorkerThread.h | 19 +- Examples/DpdkBridge/Common.h | 32 +- Examples/DpdkBridge/main.cpp | 264 ++-- .../AppWorkerThread.h | 31 +- Examples/DpdkExample-FilterTraffic/Common.h | 43 +- .../PacketMatchingEngine.h | 15 +- Examples/DpdkExample-FilterTraffic/main.cpp | 508 +++--- Examples/ExampleApp/main.cpp | 6 +- Examples/HttpAnalyzer/HttpStatsCollector.h | 186 ++- Examples/HttpAnalyzer/main.cpp | 343 ++-- Examples/IPDefragUtil/main.cpp | 260 +-- Examples/IPFragUtil/main.cpp | 289 ++-- Examples/IcmpFileTransfer/Common.cpp | 228 ++- Examples/IcmpFileTransfer/Common.h | 62 +- .../IcmpFileTransfer-catcher.cpp | 191 +-- .../IcmpFileTransfer-pitcher.cpp | 229 ++- Examples/KniPong/main.cpp | 1189 +++++++------- Examples/PcapPlusPlus-benchmark/benchmark.cpp | 34 +- Examples/PcapPrinter/main.cpp | 140 +- .../dirent-for-Visual-Studio/include/dirent.h | 1406 +++++++++-------- Examples/PcapSearch/main.cpp | 277 ++-- Examples/PcapSplitter/ConnectionSplitters.h | 43 +- Examples/PcapSplitter/IPPortSplitters.h | 184 ++- Examples/PcapSplitter/SimpleSplitters.h | 24 +- Examples/PcapSplitter/Splitters.h | 40 +- Examples/PcapSplitter/main.cpp | 284 ++-- Examples/PfRingExample-FilterTraffic/Common.h | 52 +- .../PacketMatchingEngine.h | 15 +- Examples/PfRingExample-FilterTraffic/main.cpp | 425 ++--- Examples/SSLAnalyzer/SSLStatsCollector.h | 140 +- Examples/SSLAnalyzer/main.cpp | 265 ++-- Examples/TLSFingerprinting/main.cpp | 320 ++-- Examples/TcpReassembly/main.cpp | 337 ++-- .../Tutorial-DpdkL2Fwd/WorkerThread.cpp | 8 +- .../Tutorial-DpdkL2Fwd/WorkerThread.h | 7 +- .../Tutorials/Tutorial-DpdkL2Fwd/main.cpp | 35 +- .../Tutorials/Tutorial-HelloWorld/main.cpp | 6 +- .../Tutorials/Tutorial-LiveTraffic/main.cpp | 63 +- .../Tutorial-PacketCraftAndEdit/main.cpp | 4 +- .../Tutorials/Tutorial-PacketParsing/main.cpp | 79 +- .../Tutorials/Tutorial-PcapFiles/main.cpp | 7 +- .../PacketMatchingEngine.h | 15 +- Examples/XdpExample-FilterTraffic/main.cpp | 337 ++-- Tests/Pcap++Test/Tests/LiveDeviceTests.cpp | 3 +- 49 files changed, 4752 insertions(+), 4568 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cdbd8c581d..722288935f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,7 +24,7 @@ repos: hooks: - id: clang-format args: ["--style=file"] # Use the .clang-format file for configuration - files: ^Common\+\+/.*\.(cpp|h)$ + files: ^(Common\+\+|Tests|Examples)/.*\.(cpp|h)$ - id: cppcheck args: ["--std=c++11", "--language=c++", "--suppressions-list=cppcheckSuppressions.txt", "--inline-suppr", "--force"] - repo: https://github.com/codespell-project/codespell diff --git a/Examples/ArpSpoofing/main.cpp b/Examples/ArpSpoofing/main.cpp index 2865cd5e73..d25e1a6eb3 100644 --- a/Examples/ArpSpoofing/main.cpp +++ b/Examples/ArpSpoofing/main.cpp @@ -14,59 +14,54 @@ #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' }, + { "version", no_argument, nullptr, 'v' }, + { "help", no_argument, nullptr, 'h' }, + { 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; + << "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; + 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 @@ -75,18 +70,14 @@ pcpp::MacAddress getMacAddress(const pcpp::IPv4Address& ipAddr, pcpp::PcapLiveDe 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); - + 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 + // setup arp reply filter pcpp::ArpFilter arpFilter(pcpp::ARP_REPLY); if (!pDevice->setFilter(arpFilter)) { @@ -94,7 +85,7 @@ pcpp::MacAddress getMacAddress(const pcpp::IPv4Address& ipAddr, pcpp::PcapLiveDe return pcpp::MacAddress::Zero; } - //send the arp request and wait for arp reply + // send the arp request and wait for arp reply pDevice->sendPacket(&arpRequest); pcpp::RawPacketVector capturedPackets; pDevice->startCapture(capturedPackets); @@ -107,7 +98,7 @@ pcpp::MacAddress getMacAddress(const pcpp::IPv4Address& ipAddr, pcpp::PcapLiveDe return pcpp::MacAddress::Zero; } - //parse arp reply and extract the MAC address + // parse arp reply and extract the MAC address pcpp::Packet arpReply(capturedPackets.front()); if (arpReply.isPacketOfType(pcpp::ARP)) { @@ -117,8 +108,8 @@ pcpp::MacAddress getMacAddress(const pcpp::IPv4Address& ipAddr, pcpp::PcapLiveDe return pcpp::MacAddress::Zero; } - -void doArpSpoofing(pcpp::PcapLiveDevice* pDevice, const pcpp::IPv4Address& gatewayAddr, const pcpp::IPv4Address& victimAddr) +void doArpSpoofing(pcpp::PcapLiveDevice* pDevice, const pcpp::IPv4Address& gatewayAddr, + const pcpp::IPv4Address& victimAddr) { pcpp::MacAddress gatewayMacAddr; @@ -150,11 +141,7 @@ void doArpSpoofing(pcpp::PcapLiveDevice* pDevice, const pcpp::IPv4Address& gatew // 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); + pcpp::ArpLayer gwArpLayer(pcpp::ARP_REPLY, pDevice->getMacAddress(), gatewayMacAddr, victimAddr, gatewayAddr); gwArpReply.addLayer(&gwEthLayer); gwArpReply.addLayer(&gwArpLayer); gwArpReply.computeCalculateFields(); @@ -162,72 +149,69 @@ void doArpSpoofing(pcpp::PcapLiveDevice* pDevice, const pcpp::IPv4Address& gatew // 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); + 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) + while (1) { pDevice->sendPacket(&gwArpReply); - std::cout << "Sent ARP reply: " << gatewayAddr << " [gateway] is at MAC address " << deviceMacAddress << " [me]" << std::endl; + 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; + 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 + // 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) + 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); + 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 == "") + // 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 + // Currently supports only IPv4 addresses pcpp::IPv4Address ifaceAddr; pcpp::IPv4Address victimAddr; pcpp::IPv4Address gatewayAddr; diff --git a/Examples/Arping/main.cpp b/Examples/Arping/main.cpp index 9b252de361..919e3d42f0 100644 --- a/Examples/Arping/main.cpp +++ b/Examples/Arping/main.cpp @@ -1,8 +1,8 @@ /** * 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 @@ -17,86 +17,86 @@ #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 + +// clang-format off +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' }, + { "version", no_argument, nullptr, 'v' }, + { "list", optional_argument, nullptr, 'l' }, + { "timeout", optional_argument, nullptr, 'w' }, + { "help", optional_argument, nullptr, 'h' }, + { nullptr, 0, nullptr, 0 } }; - +// clang-format on /** * 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; + 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; + 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(); + const std::vector& devList = + pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDevicesList(); std::cout << std::endl << "Network interfaces:" << std::endl; - for (const auto &dev : devList) + for (const auto& dev : devList) { - std::cout << " -> Name: '" << dev->getName() << "' IP address: " << dev->getIPv4Address().toString() << std::endl; + std::cout << " -> Name: '" << dev->getName() << "' IP address: " << dev->getIPv4Address().toString() + << std::endl; } exit(0); } - /** * The callback to be called when application is terminated by ctrl-c. Stops the endless while loop */ @@ -106,7 +106,6 @@ void onApplicationInterrupted(void* cookie) *shouldStop = true; } - /** * main method of the application */ @@ -125,64 +124,65 @@ int main(int argc, char* argv[]) int optionIndex = 0; int opt = 0; - while((opt = getopt_long(argc, argv, "i:s:S:T:c:hvlw:", ArpingOptions, &optionIndex)) != -1) + 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': - try - { - sourceMac = pcpp::MacAddress(optarg); - } - catch (std::exception&) { - EXIT_WITH_ERROR("Source MAC address is not valid"); - } - break; - case 'S': - try - { - sourceIP = pcpp::IPv4Address(static_cast(optarg)); - } - catch(const std::exception&) - { - EXIT_WITH_ERROR("Source IP address is not valid"); - } - break; - case 'T': - try - { - targetIP = pcpp::IPv4Address(static_cast(optarg)); - } - catch(const std::exception&) - { - EXIT_WITH_ERROR("Target IP is not valid"); - } - 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); + case 0: + break; + case 'i': + ifaceNameOrIP = optarg; + ifaceNameOrIpProvided = true; + break; + case 's': + try + { + sourceMac = pcpp::MacAddress(optarg); + } + catch (std::exception&) + { + EXIT_WITH_ERROR("Source MAC address is not valid"); + } + break; + case 'S': + try + { + sourceIP = pcpp::IPv4Address(static_cast(optarg)); + } + catch (const std::exception&) + { + EXIT_WITH_ERROR("Source IP address is not valid"); + } + break; + case 'T': + try + { + targetIP = pcpp::IPv4Address(static_cast(optarg)); + } + catch (const std::exception&) + { + EXIT_WITH_ERROR("Target IP is not valid"); + } + 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); } } @@ -238,7 +238,8 @@ int main(int argc, char* argv[]) 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); + pcpp::MacAddress result = pcpp::NetworkUtils::getInstance().getMacAddress(targetIP, dev, arpResponseTimeMS, + sourceMac, sourceIP, timeoutSec); // failed fetching MAC address if (result == pcpp::MacAddress::Zero) @@ -246,16 +247,13 @@ int main(int argc, char* argv[]) // PcapPlusPlus logger saves the last internal error message std::cout << "Arping index=" << i << " : " << pcpp::Logger::getInstance().getLastError() << std::endl; } - else // Succeeded fetching MAC address + 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; + std::cout << "Reply from " << targetIP << " " + << "[" << result << "] " << std::fixed << arpResponseTimeMS << "ms " + << "index=" << i << std::endl; } i++; diff --git a/Examples/DNSResolver/main.cpp b/Examples/DNSResolver/main.cpp index 10ce187135..f29f1e894d 100644 --- a/Examples/DNSResolver/main.cpp +++ b/Examples/DNSResolver/main.cpp @@ -8,82 +8,86 @@ #include "Logger.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 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; + << "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; + 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(); + const std::vector& devList = + pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDevicesList(); std::cout << std::endl << "Network interfaces:" << std::endl; - for (const auto &dev : devList) + for (const auto& dev : devList) { - std::cout << " -> Name: '" << dev->getName() << "' IP address: " << dev->getIPv4Address().toString() << std::endl; + std::cout << " -> Name: '" << dev->getName() << "' IP address: " << dev->getIPv4Address().toString() + << std::endl; } exit(0); } - /** * The callback to be called when application is terminated by ctrl-c. Stops the endless while loop */ @@ -93,7 +97,6 @@ void onApplicationInterrupted(void* cookie) device->close(); } - /** * main method of the application */ @@ -112,61 +115,61 @@ int main(int argc, char* argv[]) int optionIndex = 0; int opt = 0; - while((opt = getopt_long(argc, argv, "i:d:g:s:t:hvl", DNSResolverOptions, &optionIndex)) != -1) + 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); - } + 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); + } } } @@ -187,10 +190,12 @@ int main(int argc, char* argv[]) // 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(); + const std::vector& devList = + pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDevicesList(); - auto iter = std::find_if(devList.begin(), devList.end(), - [](pcpp::PcapLiveDevice *dev) { return dev->getDefaultGateway() != pcpp::IPv4Address::Zero; }); + auto iter = std::find_if(devList.begin(), devList.end(), [](pcpp::PcapLiveDevice* dev) { + return dev->getDefaultGateway() != pcpp::IPv4Address::Zero; + }); if (iter != devList.end()) { dev = *iter; @@ -213,14 +218,17 @@ int main(int argc, char* argv[]) // 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); + pcpp::IPv4Address resultIP = pcpp::NetworkUtils::getInstance().getIPv4Address( + hostname, dev, responseTime, dnsTTL, timeoutSec, dnsServerIP, gatewayIP); if (resultIP == pcpp::IPv4Address::Zero) { 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; + std::cout << std::endl + << "IP address of [" << hostname << "] is: " << resultIP << " DNS-TTL=" << dnsTTL + << " time=" << (int)responseTime << "ms" << std::endl; } } catch (const std::exception&) diff --git a/Examples/DnsSpoofing/main.cpp b/Examples/DnsSpoofing/main.cpp index d19d014b24..eda9912819 100644 --- a/Examples/DnsSpoofing/main.cpp +++ b/Examples/DnsSpoofing/main.cpp @@ -1,9 +1,9 @@ /** * 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 @@ -13,7 +13,7 @@ #include #include #if !defined(_WIN32) -#include +# include #endif #include "IpAddress.h" #include "RawPacket.h" @@ -32,27 +32,25 @@ #include "PcapPlusPlusVersion.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 */ @@ -61,10 +59,10 @@ struct DnsSpoofStats int numOfSpoofedDnsRequests; std::unordered_map spoofedHosts; - DnsSpoofStats() : numOfSpoofedDnsRequests(0) {} + DnsSpoofStats() : numOfSpoofedDnsRequests(0) + {} }; - /** * A struct that holds all arguments passed to handleDnsRequest() */ @@ -75,70 +73,74 @@ struct DnsSpoofingArgs DnsSpoofStats stats; bool shouldStop; - DnsSpoofingArgs() : shouldStop(false) {} + 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; + 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; + 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(); + const std::vector& devList = + pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDevicesList(); std::cout << std::endl << "Network interfaces:" << std::endl; - for (const auto &dev : devList) + for (const auto& dev : devList) { - std::cout << " -> Name: '" << dev->getName() << "' IP address: " << dev->getIPv4Address().toString() << std::endl; + std::cout << " -> Name: '" << dev->getName() << "' IP address: " << dev->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) { @@ -147,7 +149,8 @@ void handleDnsRequest(pcpp::RawPacket* packet, pcpp::PcapLiveDevice* dev, void* // 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)) + if (!dnsRequest.isPacketOfType(pcpp::DNS) || !dnsRequest.isPacketOfType(pcpp::IP) || + !dnsRequest.isPacketOfType(pcpp::UDP) || !dnsRequest.isPacketOfType(pcpp::Ethernet)) return; // extract all packet layers @@ -161,7 +164,7 @@ void handleDnsRequest(pcpp::RawPacket* packet, pcpp::PcapLiveDevice* dev, void* 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::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; @@ -172,7 +175,7 @@ void handleDnsRequest(pcpp::RawPacket* packet, pcpp::PcapLiveDevice* dev, void* bool hostMatch = false; // go over all hosts in dnsHostsToSpoof list and see if current query matches one of them - for (const auto &host : args->dnsHostsToSpoof) + for (const auto& host : args->dnsHostsToSpoof) { if (dnsLayer->getQuery(host, false) != nullptr) { @@ -185,7 +188,6 @@ void handleDnsRequest(pcpp::RawPacket* packet, pcpp::PcapLiveDevice* dev, void* return; } - // create a response out of the request packet // reverse src and dst MAC addresses @@ -239,7 +241,6 @@ void handleDnsRequest(pcpp::RawPacket* packet, pcpp::PcapLiveDevice* dev, void* args->stats.spoofedHosts[dnsQuery->getName()]++; } - /** * An auxiliary method for sorting the string count map. Used for printing the summary of spoofed hosts */ @@ -252,7 +253,6 @@ bool stringCountComparer(const std::pair& first, const std::pa return first.second > second.second; } - /** * A callback for application interrupted event (ctrl+c): print DNS spoofing summary */ @@ -266,12 +266,12 @@ void onApplicationInterrupted(void* cookie) 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; + << "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; @@ -284,11 +284,12 @@ void onApplicationInterrupted(void* cookie) // sort the spoofed hosts map so the most spoofed hosts will be first // since it's not possible to sort a std::unordered_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); + 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(const auto &iter : map2vec) + for (const auto& iter : map2vec) { std::stringstream values; values << iter.first << "|" << iter.second; @@ -299,11 +300,11 @@ void onApplicationInterrupted(void* cookie) 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) +void doDnsSpoofing(pcpp::PcapLiveDevice* dev, const pcpp::IPAddress& dnsServer, const pcpp::IPAddress& clientIP, + const std::vector& dnsHostsToSpoof) { // open device if (!dev->open()) @@ -316,7 +317,7 @@ void doDnsSpoofing(pcpp::PcapLiveDevice* dev, const pcpp::IPAddress& dnsServer, filterForAnd.push_back(&dnsPortFilter); pcpp::IPFilter clientIpFilter(clientIP.toString(), pcpp::SRC); - if(!clientIP.isZero()) + if (!clientIP.isZero()) { filterForAnd.push_back(&clientIpFilter); } @@ -335,7 +336,6 @@ void doDnsSpoofing(pcpp::PcapLiveDevice* dev, const pcpp::IPAddress& dnsServer, 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); @@ -347,7 +347,6 @@ void doDnsSpoofing(pcpp::PcapLiveDevice* dev, const pcpp::IPAddress& dnsServer, } } - /** * main method of the application */ @@ -367,74 +366,74 @@ int main(int argc, char* argv[]) std::vector hostList; - while((opt = getopt_long(argc, argv, "i:d:c:o:hvl", DnsSpoofingOptions, &optionIndex)) != -1) + 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': + case 0: + { + break; + } + case 'h': + { + printUsage(); + exit(0); + } + case 'v': + { + printAppVersion(); + break; + } + case 'l': + { + listInterfaces(); + exit(0); + } + case 'i': + { + interfaceNameOrIP = optarg; + break; + } + case 'd': + { + try { - listInterfaces(); - exit(0); + dnsServer = pcpp::IPAddress(static_cast(optarg)); } - case 'i': + catch (const std::exception&) { - interfaceNameOrIP = optarg; - break; + EXIT_WITH_ERROR("Spoof DNS server IP provided is empty or not a valid IP address"); } - case 'd': + break; + } + case 'c': + { + try { - try - { - dnsServer = pcpp::IPAddress(static_cast(optarg)); - } - catch(const std::exception&) - { - EXIT_WITH_ERROR("Spoof DNS server IP provided is empty or not a valid IP address"); - } - break; + clientIP = pcpp::IPAddress(static_cast(optarg)); } - case 'c': + catch (const std::exception&) { - try - { - clientIP = pcpp::IPAddress(static_cast(optarg)); - } - catch(const std::exception&) - { - EXIT_WITH_ERROR("Client IP to spoof is invalid"); - } - clientIpSet = true; - break; + EXIT_WITH_ERROR("Client IP to spoof is invalid"); } - case 'o': - { - std::string input = optarg; - std::istringstream stream(input); - std::string token; + 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); - } + while (std::getline(stream, token, ',')) + hostList.push_back(token); + break; + } + default: + { + printUsage(); + exit(1); + } } } diff --git a/Examples/DpdkBridge/AppWorkerThread.h b/Examples/DpdkBridge/AppWorkerThread.h index 1b8713fcd8..84a3a83e12 100644 --- a/Examples/DpdkBridge/AppWorkerThread.h +++ b/Examples/DpdkBridge/AppWorkerThread.h @@ -7,11 +7,10 @@ #include "DpdkDeviceList.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 { @@ -21,10 +20,9 @@ class AppWorkerThread : public pcpp::DpdkWorkerThread uint32_t m_CoreId; public: - explicit AppWorkerThread(AppWorkerConfig& workerConfig) : - m_WorkerConfig(workerConfig), m_Stop(true), m_CoreId(MAX_NUM_OF_CORES+1) - { - } + explicit AppWorkerThread(AppWorkerConfig& workerConfig) + : m_WorkerConfig(workerConfig), m_Stop(true), m_CoreId(MAX_NUM_OF_CORES + 1) + {} virtual ~AppWorkerThread() { @@ -46,13 +44,13 @@ class AppWorkerThread : public pcpp::DpdkWorkerThread return true; } - #define MAX_RECEIVE_BURST 64 +#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++) + 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); @@ -85,5 +83,4 @@ class AppWorkerThread : public pcpp::DpdkWorkerThread { return m_CoreId; } - }; diff --git a/Examples/DpdkBridge/Common.h b/Examples/DpdkBridge/Common.h index 77cc4a44ea..c93016b1cf 100644 --- a/Examples/DpdkBridge/Common.h +++ b/Examples/DpdkBridge/Common.h @@ -13,23 +13,24 @@ #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: @@ -43,7 +44,6 @@ struct AppWorkerConfig uint16_t RxQueues; pcpp::DpdkDevice* TxDevice; - AppWorkerConfig() : CoreId(MAX_NUM_OF_CORES+1), RxDevice(NULL), RxQueues(1), TxDevice(NULL) - { - } + 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 02dec1aada..49b76652a1 100644 --- a/Examples/DpdkBridge/main.cpp +++ b/Examples/DpdkBridge/main.cpp @@ -2,20 +2,23 @@ * 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. + * 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 (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" @@ -38,24 +41,22 @@ #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} +// clang-format off +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 } }; - +// clang-format on /** * Print application usage @@ -63,38 +64,46 @@ static struct option DpdkBridgeOptions[] = 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; + << "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; + 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 */ @@ -112,28 +121,26 @@ void listDpdkPorts() // go over all available DPDK devices and print info for each one std::vector deviceList = pcpp::DpdkDeviceList::getInstance().getDpdkDeviceList(); - for (const auto &iter : deviceList) + for (const auto& iter : deviceList) { pcpp::DpdkDevice* dev = iter; std::cout << " " - << " Port #" << dev->getDeviceId() << ":" - << " MAC address='" << dev->getMacAddress() << "';" - << " PCI address='" << dev->getPciAddress() << "';" - << " PMD='" << dev->getPMDName() << "'" - << std::endl; + << " Port #" << dev->getDeviceId() << ":" + << " MAC address='" << dev->getMacAddress() << "';" + << " PCI address='" << dev->getPciAddress() << "';" + << " PMD='" << dev->getPMDName() << "'" << std::endl; } } - 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 */ @@ -141,10 +148,7 @@ 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(); @@ -152,7 +156,6 @@ void onApplicationInterrupted(void* cookie) args->shouldStop = true; } - /** * Extract and print traffic stats from a device */ @@ -161,7 +164,7 @@ void printStats(pcpp::DpdkDevice* device) pcpp::DpdkDevice::DpdkDeviceStats stats; device->getStatistics(stats); - std::cout << std::endl <<"Statistics for port " << device->getDeviceId() << ":" << std::endl; + std::cout << std::endl << "Statistics for port " << device->getDeviceId() << ":" << std::endl; std::vector columnNames; columnNames.push_back(" "); @@ -180,18 +183,22 @@ void printStats(pcpp::DpdkDevice* device) pcpp::TablePrinter printer(columnNames, columnLengths); std::stringstream totalRx; - totalRx << "rx" << "|" << stats.aggregatedRxStats.packets << "|" << stats.aggregatedRxStats.packetsPerSec << "|" << stats.aggregatedRxStats.bytes << "|" << stats.aggregatedRxStats.bytesPerSec; + 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; + 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[]) { @@ -208,75 +215,75 @@ int main(int argc, char* argv[]) 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) + 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': + 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, ',')) { - 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)) { - 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); + // not an integer + EXIT_WITH_ERROR_AND_PRINT_USAGE("DPDK ports list is invalid"); } - // verify list contains two ports - if(dpdkPortVec.size()!=2) - { - EXIT_WITH_ERROR_AND_PRINT_USAGE("DPDK list must contain two values"); - } - break; + dpdkPortVec.push_back(port); } - case 'm': + // verify list contains two ports + if (dpdkPortVec.size() != 2) { - 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); + 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); + } } } @@ -311,7 +318,7 @@ int main(int argc, char* argv[]) // collect the list of DPDK devices std::vector dpdkDevicesToUse; - for (const auto &port : dpdkPortVec) + for (const auto& port : dpdkPortVec) { pcpp::DpdkDevice* dev = pcpp::DpdkDeviceList::getInstance().getDeviceByPort(port); if (dev == NULL) @@ -322,11 +329,12 @@ int main(int argc, char* argv[]) } // go over all devices and open them - for (const auto &dev : dpdkDevicesToUse) + for (const auto& dev : dpdkDevicesToUse) { if (!dev->openMultiQueues(queueQuantity, 1)) { - EXIT_WITH_ERROR("Couldn't open DPDK device #" << dev->getDeviceId() << ", PMD '" << dev->getPMDName() << "'"); + EXIT_WITH_ERROR("Couldn't open DPDK device #" << dev->getDeviceId() << ", PMD '" << dev->getPMDName() + << "'"); } } @@ -375,9 +383,7 @@ int main(int argc, char* argv[]) std::cout << "\033[2J\033[1;1H"; // Print devices traffic stats - std::cout - << "Stats #" << statsCounter++ << std::endl - << "==========" << std::endl; + std::cout << "Stats #" << statsCounter++ << std::endl << "==========" << std::endl; printStats(dpdkDevicesToUse.at(0)); printStats(dpdkDevicesToUse.at(1)); } diff --git a/Examples/DpdkExample-FilterTraffic/AppWorkerThread.h b/Examples/DpdkExample-FilterTraffic/AppWorkerThread.h index ec9056df18..ff7317c40e 100644 --- a/Examples/DpdkExample-FilterTraffic/AppWorkerThread.h +++ b/Examples/DpdkExample-FilterTraffic/AppWorkerThread.h @@ -9,9 +9,10 @@ #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 { @@ -24,11 +25,10 @@ class AppWorkerThread : public pcpp::DpdkWorkerThread std::unordered_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) - { - } + AppWorkerThread(AppWorkerConfig& workerConfig, PacketMatchingEngine& matchingEngine) + : m_WorkerConfig(workerConfig), m_Stop(true), m_CoreId(MAX_NUM_OF_CORES + 1), + m_PacketMatchingEngine(matchingEngine) + {} virtual ~AppWorkerThread() { @@ -66,7 +66,7 @@ class AppWorkerThread : public pcpp::DpdkWorkerThread return true; } - #define MAX_RECEIVE_BURST 64 +#define MAX_RECEIVE_BURST 64 pcpp::MBufRawPacket* packetArr[MAX_RECEIVE_BURST] = {}; // main loop, runs until be told to stop @@ -74,10 +74,10 @@ class AppWorkerThread : public pcpp::DpdkWorkerThread while (!m_Stop) { // go over all DPDK devices configured for this worker/core - for (const auto &iter : m_WorkerConfig.inDataCfg) + for (const auto& iter : m_WorkerConfig.inDataCfg) { // for each DPDK device go over all RX queues configured for this worker/core - for (const auto &iter2 : iter.second) + for (const auto& iter2 : iter.second) { pcpp::DpdkDevice* dev = iter.first; @@ -94,7 +94,8 @@ class AppWorkerThread : public pcpp::DpdkWorkerThread bool packetMatched; - // hash the packet by 5-tuple and look in the flow table to see whether this packet belongs to an existing or new flow + // 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); auto iter3 = m_FlowTable.find(hash); @@ -103,7 +104,7 @@ class AppWorkerThread : public pcpp::DpdkWorkerThread { packetMatched = true; } - else // packet belongs to a new flow + else // packet belongs to a new flow { packetMatched = m_PacketMatchingEngine.isMatched(parsedPacket); if (packetMatched) @@ -111,7 +112,7 @@ class AppWorkerThread : public pcpp::DpdkWorkerThread // put new flow in flow table m_FlowTable[hash] = true; - //collect stats + // collect stats if (parsedPacket.isPacketOfType(pcpp::TCP)) { m_Stats.matchedTcpFlows++; @@ -120,7 +121,6 @@ class AppWorkerThread : public pcpp::DpdkWorkerThread { m_Stats.matchedUdpFlows++; } - } } @@ -171,5 +171,4 @@ class AppWorkerThread : public pcpp::DpdkWorkerThread { return m_CoreId; } - }; diff --git a/Examples/DpdkExample-FilterTraffic/Common.h b/Examples/DpdkExample-FilterTraffic/Common.h index 15c7d6c1aa..7f032de0c6 100644 --- a/Examples/DpdkExample-FilterTraffic/Common.h +++ b/Examples/DpdkExample-FilterTraffic/Common.h @@ -13,25 +13,26 @@ #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(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::unordered_map > InputDataConfig; +#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::unordered_map> InputDataConfig; /** * Contains all the configuration needed for the worker thread including: @@ -46,11 +47,11 @@ struct AppWorkerConfig bool writeMatchedPacketsToFile; std::string pathToWritePackets; - AppWorkerConfig() : coreId(MAX_NUM_OF_CORES+1), sendPacketsTo(nullptr), - writeMatchedPacketsToFile(false), pathToWritePackets("") {} + AppWorkerConfig() + : coreId(MAX_NUM_OF_CORES + 1), sendPacketsTo(nullptr), writeMatchedPacketsToFile(false), pathToWritePackets("") + {} }; - /** * Collect and analyze packet and flow statistics */ @@ -74,9 +75,11 @@ struct PacketStats int matchedUdpFlows; int matchedPackets; - PacketStats() : workerId(MAX_NUM_OF_CORES+1), packetCount(0), ethCount(0), arpCount(0), ipv4Count(0), ipv6Count(0), - tcpCount(0), udpCount(0), httpCount(0), dnsCount(0), tlsCount(0), - matchedTcpFlows(0), matchedUdpFlows(0), matchedPackets(0) {} + PacketStats() + : workerId(MAX_NUM_OF_CORES + 1), packetCount(0), ethCount(0), arpCount(0), ipv4Count(0), ipv6Count(0), + tcpCount(0), udpCount(0), httpCount(0), dnsCount(0), tlsCount(0), matchedTcpFlows(0), matchedUdpFlows(0), + matchedPackets(0) + {} void collectStats(pcpp::Packet& packet) { @@ -121,7 +124,7 @@ struct PacketStats void clear() { - workerId = MAX_NUM_OF_CORES+1; + workerId = MAX_NUM_OF_CORES + 1; packetCount = 0; ethCount = 0; arpCount = 0; diff --git a/Examples/DpdkExample-FilterTraffic/PacketMatchingEngine.h b/Examples/DpdkExample-FilterTraffic/PacketMatchingEngine.h index ef249f114a..e5354898ed 100644 --- a/Examples/DpdkExample-FilterTraffic/PacketMatchingEngine.h +++ b/Examples/DpdkExample-FilterTraffic/PacketMatchingEngine.h @@ -7,8 +7,9 @@ #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 { @@ -19,11 +20,13 @@ class PacketMatchingEngine 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) + 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; diff --git a/Examples/DpdkExample-FilterTraffic/main.cpp b/Examples/DpdkExample-FilterTraffic/main.cpp index 2674856a0a..3d6839c006 100644 --- a/Examples/DpdkExample-FilterTraffic/main.cpp +++ b/Examples/DpdkExample-FilterTraffic/main.cpp @@ -2,18 +2,20 @@ * 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 - * of matched packets. + * 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' */ @@ -37,84 +39,96 @@ #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} +// clang-format off +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 } }; - +// clang-format on /** * 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; + 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; + 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 */ @@ -132,30 +146,29 @@ void listDpdkPorts() // go over all available DPDK devices and print info for each one std::vector deviceList = pcpp::DpdkDeviceList::getInstance().getDpdkDeviceList(); - for (const auto &dev : deviceList) + for (const auto& dev : deviceList) { std::cout << " " - << " Port #" << dev->getDeviceId() << ":" - << " MAC address='" << dev->getMacAddress() << "';" - << " PCI address='" << dev->getPciAddress() << "';" - << " PMD='" << dev->getPMDName() << "'" - << std::endl; + << " 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) +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 (const auto &iter : dpdkDevicesToUse) + std::vector> deviceAndRxQVec; + for (const auto& iter : dpdkDevicesToUse) { for (int rxQueueIndex = 0; rxQueueIndex < rxQueues; rxQueueIndex++) { @@ -165,14 +178,15 @@ void prepareCoreConfiguration(std::vector& dpdkDevicesToUse, 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 + // 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 (const auto &core : coresToUse) + std::vector>::iterator pairVecIter = deviceAndRxQVec.begin(); + for (const auto& core : coresToUse) { std::cout << "Using core " << (int)core.Id << std::endl; workerConfigArr[i].coreId = core.Id; @@ -199,10 +213,10 @@ void prepareCoreConfiguration(std::vector& dpdkDevicesToUse, // print configuration for core std::cout << " Core configuration:" << std::endl; - for (const auto &iter2 : workerConfigArr[i].inDataCfg) + for (const auto& iter2 : workerConfigArr[i].inDataCfg) { std::cout << " DPDK device#" << iter2.first->getDeviceId() << ": "; - for (const auto &iter3 : iter2.second) + for (const auto& iter3 : iter2.second) { std::cout << "RX-Queue#" << iter3 << "; "; } @@ -216,23 +230,22 @@ void prepareCoreConfiguration(std::vector& dpdkDevicesToUse, } } - struct FilterTrafficArgs { bool shouldStop; std::vector* workerThreadsVector; - FilterTrafficArgs() : shouldStop(false), workerThreadsVector(NULL) {} + FilterTrafficArgs() : shouldStop(false), workerThreadsVector(NULL) + {} }; - /** * Print thread stats in a table */ void printStats(const PacketStats& threadStats, const std::string& columnName) { - std::vector columnNames = {columnName, "Count"}; - std::vector columnsWidths = {21, 10}; + std::vector columnNames = { columnName, "Count" }; + std::vector columnsWidths = { 21, 10 }; pcpp::TablePrinter printer(columnNames, columnsWidths); printer.printRow("Eth count|" + std::to_string(threadStats.ethCount), '|'); @@ -252,7 +265,6 @@ void printStats(const PacketStats& threadStats, const std::string& columnName) printer.printRow("Total packet count|" + std::to_string(threadStats.packetCount), '|'); } - /** * The callback to be called when application is terminated by ctrl-c. Do cleanup and print summary stats */ @@ -268,7 +280,7 @@ void onApplicationInterrupted(void* cookie) // print final stats for every worker thread plus sum of all threads and free worker threads memory PacketStats aggregatedStats; std::vector threadStatsVec; - for (const auto &iter : *(args->workerThreadsVector)) + for (const auto& iter : *(args->workerThreadsVector)) { AppWorkerThread* thread = (AppWorkerThread*)(iter); PacketStats threadStats = thread->getStats(); @@ -300,10 +312,10 @@ void onApplicationInterrupted(void* cookie) 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[]) { @@ -324,178 +336,178 @@ int main(int argc, char* argv[]) 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; + 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) + 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': + 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, ',')) { - 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()) + char c; + std::stringstream stream2(portAsString); + stream2 >> port; + if (stream2.fail() || stream2.get(c)) { - EXIT_WITH_ERROR_AND_PRINT_USAGE("DPDK ports list is empty"); + // not an integer + EXIT_WITH_ERROR_AND_PRINT_USAGE("DPDK ports list is invalid"); } - break; - } - case 's': - { - sendPacketsToPort = atoi(optarg); - break; + dpdkPortVec.push_back(port); } - case 'c': + + // verify list is not empty + if (dpdkPortVec.empty()) { - coreMaskToUse = atoi(optarg); - break; + EXIT_WITH_ERROR_AND_PRINT_USAGE("DPDK ports list is empty"); } - case 'f': + break; + } + case 's': + { + sendPacketsToPort = atoi(optarg); + break; + } + case 'c': + { + coreMaskToUse = atoi(optarg); + break; + } + case 'f': + { + packetFilePath = std::string(optarg); + writePacketsToDisk = true; + if (packetFilePath.empty()) { - packetFilePath = std::string(optarg); - writePacketsToDisk = true; - if (packetFilePath.empty()) - { - EXIT_WITH_ERROR_AND_PRINT_USAGE("Filename to write packets is empty"); - } - break; + EXIT_WITH_ERROR_AND_PRINT_USAGE("Filename to write packets is empty"); } - case 'm': + break; + } + case 'm': + { + mBufPoolSize = atoi(optarg); + break; + } + case 'i': + { + try { - mBufPoolSize = atoi(optarg); - break; + srcIPToMatch = pcpp::IPv4Address(optarg); } - case 'i': + catch (const std::exception&) { - try - { - srcIPToMatch = pcpp::IPv4Address(optarg); - } - catch(const std::exception&) - { - EXIT_WITH_ERROR_AND_PRINT_USAGE("Source IP to match isn't a valid IP address"); - } - break; + EXIT_WITH_ERROR_AND_PRINT_USAGE("Source IP to match isn't a valid IP address"); } - case 'I': - { - try - { - dstIPToMatch = pcpp::IPv4Address(optarg); - } - catch(const std::exception&) - { - EXIT_WITH_ERROR_AND_PRINT_USAGE("Destination IP to match isn't a valid IP address"); - } - break; - } - case 'p': + break; + } + case 'I': + { + try { - 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; + dstIPToMatch = pcpp::IPv4Address(optarg); } - case 'P': + catch (const std::exception&) { - 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; + EXIT_WITH_ERROR_AND_PRINT_USAGE("Destination IP to match isn't a valid IP address"); } - case 'o': + break; + } + case 'p': + { + int ret = atoi(optarg); + if (ret <= 0 || ret > 65535) { - 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; + EXIT_WITH_ERROR_AND_PRINT_USAGE("Source port to match isn't a valid TCP/UDP port"); } - case 'r': + srcPortToMatch = ret; + break; + } + case 'P': + { + int ret = atoi(optarg); + if (ret <= 0 || ret > 65535) { - 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; + EXIT_WITH_ERROR_AND_PRINT_USAGE("Destination port to match isn't a valid TCP/UDP port"); } - case 't': + 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 { - 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; + EXIT_WITH_ERROR_AND_PRINT_USAGE("Protocol to match isn't TCP or UDP"); } - case 'h': + break; + } + case 'r': + { + rxQueues = atoi(optarg); + if (rxQueues == 0) { - printUsage(); - exit(0); + EXIT_WITH_ERROR("Cannot open the device with 0 RX queues"); } - case 'v': + if (rxQueues > MAX_QUEUES) { - printAppVersion(); - break; + EXIT_WITH_ERROR("The number of RX queues cannot exceed " << MAX_QUEUES); } - case 'l': + break; + } + case 't': + { + txQueues = atoi(optarg); + if (txQueues == 0) { - listDpdkPorts(); - exit(0); + EXIT_WITH_ERROR("Cannot open the device with 0 TX queues"); } - default: + if (txQueues > MAX_QUEUES) { - printUsage(); - exit(0); + 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); + } } } @@ -530,7 +542,7 @@ int main(int argc, char* argv[]) // collect the list of DPDK devices std::vector dpdkDevicesToUse; - for (const auto &port : dpdkPortVec) + for (const auto& port : dpdkPortVec) { pcpp::DpdkDevice* dev = pcpp::DpdkDeviceList::getInstance().getDeviceByPort(port); if (dev == NULL) @@ -541,26 +553,29 @@ int main(int argc, char* argv[]) } // go over all devices and open them - for (const auto &dev : dpdkDevicesToUse) + for (const auto& dev : dpdkDevicesToUse) { if (rxQueues > dev->getTotalNumOfRxQueues()) { - EXIT_WITH_ERROR("Number of RX errors cannot exceed the max allowed by the device which is " << dev->getTotalNumOfRxQueues()); + EXIT_WITH_ERROR("Number of RX errors cannot exceed the max allowed by the device which is " + << dev->getTotalNumOfRxQueues()); } if (txQueues > dev->getTotalNumOfTxQueues()) { - EXIT_WITH_ERROR("Number of TX errors cannot exceed the max allowed by the device which is " << dev->getTotalNumOfTxQueues()); + EXIT_WITH_ERROR("Number of TX errors cannot exceed the max allowed by the device which is " + << dev->getTotalNumOfTxQueues()); } if (!dev->openMultiQueues(rxQueues, txQueues)) { - EXIT_WITH_ERROR("Couldn't open DPDK device #" << dev->getDeviceId() << ", PMD '" << dev->getPMDName() << "'"); + EXIT_WITH_ERROR("Couldn't open DPDK device #" << dev->getDeviceId() << ", PMD '" << dev->getPMDName() + << "'"); } - std::cout - << "Opened device #" << dev->getDeviceId() - << " with " << rxQueues << " RX queues and " << txQueues << " TX queues." - << " RSS hash functions:" << std::endl; - std::vector rssHashFunctions = dev->rssHashFunctionMaskToString(dev->getConfiguredRssHashFunction()); - for(const auto &hashFunc : rssHashFunctions) + std::cout << "Opened device #" << dev->getDeviceId() << " with " << rxQueues << " RX queues and " << txQueues + << " TX queues." + << " RSS hash functions:" << std::endl; + std::vector rssHashFunctions = + dev->rssHashFunctionMaskToString(dev->getConfiguredRssHashFunction()); + for (const auto& hashFunc : rssHashFunctions) { std::cout << " " << hashFunc << std::endl; } @@ -568,14 +583,15 @@ int main(int argc, char* argv[]) // 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()) + 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); + prepareCoreConfiguration(dpdkDevicesToUse, coresToUse, writePacketsToDisk, packetFilePath, sendPacketsTo, + workerConfigArr, coresToUse.size(), rxQueues); PacketMatchingEngine matchingEngine(srcIPToMatch, dstIPToMatch, srcPortToMatch, dstPortToMatch, protocolToMatch); diff --git a/Examples/ExampleApp/main.cpp b/Examples/ExampleApp/main.cpp index fcad0e09fb..89f3bdf6d2 100644 --- a/Examples/ExampleApp/main.cpp +++ b/Examples/ExampleApp/main.cpp @@ -32,10 +32,8 @@ int main(int argc, char* argv[]) pcpp::IPv4Address destIP = parsedPacket.getLayerOfType()->getDstIPv4Address(); // print source and dest IPs - std::cout - << "Source IP is '" << srcIP << "'; " - << "Dest IP is '" << destIP << "'" - << std::endl; + std::cout << "Source IP is '" << srcIP << "'; " + << "Dest IP is '" << destIP << "'" << std::endl; } // close the file diff --git a/Examples/HttpAnalyzer/HttpStatsCollector.h b/Examples/HttpAnalyzer/HttpStatsCollector.h index cf45af7629..445fa166cf 100644 --- a/Examples/HttpAnalyzer/HttpStatsCollector.h +++ b/Examples/HttpAnalyzer/HttpStatsCollector.h @@ -16,8 +16,8 @@ */ struct Rate { - double currentRate; // periodic rate - double totalRate; // overlal rate + double currentRate; // periodic rate + double totalRate; // overlal rate void clear() { @@ -31,19 +31,32 @@ struct Rate */ 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 + // total number of HTTP flows + int numOfHttpFlows; + // rate of HTTP flows + Rate httpFlowRate; + // total number of HTTP flows that contains at least on HTTP pipelining transaction + int numOfHttpPipeliningFlows; + // total number of HTTP transactions + int numOfHttpTransactions; + // rate of HTTP transactions + Rate httpTransactionsRate; + // average number of HTTP transactions per flow + double averageNumOfHttpTransactionsPerFlow; + // total number of HTTP packets + int numOfHttpPackets; + // rate of HTTP packets + Rate httpPacketRate; + // average number of HTTP packets per flow + double averageNumOfPacketsPerFlow; + // total HTTP traffic in bytes + int amountOfHttpTraffic; + // average number of HTTP traffic per flow + double averageAmountOfDataPerFlow; + // rate of HTTP traffic + Rate httpTrafficRate; + // total stats collection time + double sampleTime; void clear() { @@ -63,18 +76,22 @@ struct HttpGeneralStats } }; - /** * 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() {} + // total number of HTTP messages of that type (request/response) + int numOfMessages; + // rate of HTTP messages of that type + Rate messageRate; + // total size (in bytes) of data in headers + int totalMessageHeaderSize; + // average header size + double averageMessageHeaderSize; + + virtual ~HttpMessageStats() + {} virtual void clear() { @@ -85,14 +102,15 @@ struct HttpMessageStats } }; - /** * 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 + // a map for counting the different HTTP methods seen in traffic + std::unordered_map> methodCount; + // a map for counting the hostnames seen in traffic + std::unordered_map hostnameCount; void clear() override { @@ -102,17 +120,21 @@ struct HttpRequestStats : HttpMessageStats } }; - /** * 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 + // a map for counting the different status codes seen in traffic + std::unordered_map statusCodeCount; + // a map for counting the content-types seen in traffic + std::unordered_map contentTypeCount; + // total number of responses containing the "content-length" field + int numOfMessagesWithContentLength; + // total body size extracted by responses containing "content-length" field + int totalContentLengthSize; + // average body size + double averageContentLengthSize; void clear() override { @@ -125,14 +147,12 @@ struct HttpResponseStats : HttpMessageStats } }; - /** * 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 */ @@ -191,23 +211,29 @@ class HttpStatsCollector // 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 + // 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; + 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 + // 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; @@ -245,30 +271,44 @@ class HttpStatsCollector /** * Get HTTP general stats */ - HttpGeneralStats& getGeneralStats() { return m_GeneralStats; } + HttpGeneralStats& getGeneralStats() + { + return m_GeneralStats; + } /** * Get HTTP request stats */ - HttpRequestStats& getRequestStats() { return m_RequestStats; } + HttpRequestStats& getRequestStats() + { + return m_RequestStats; + } /** * Get HTTP response stats */ - HttpResponseStats& getResponseStats() { return m_ResponseStats; } + 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 + // number of transactions that were started (request has arrived) but weren't closed yet (response hasn't + // arrived yet) + int numOfOpenTransactions; + // the last HTTP message seen on this flow (request, response or neither). Used to identify HTTP pipelining + pcpp::ProtocolType lastSeenMessage; + // was HTTP pipelining identified on this flow + bool httpPipeliningFlow; + // the current TCP sequence number from client to server. Used to identify TCP re-transmission + uint32_t curSeqNumberRequests; + // the current TCP sequence number from server to client. Used to identify TCP re-transmission + uint32_t curSeqNumberResponses; void clear() { @@ -278,7 +318,6 @@ class HttpStatsCollector } }; - /** * Collect stats relevant for every HTTP packet (request, response or any other) * This method calculates and returns the flow key for this packet @@ -307,14 +346,15 @@ class HttpStatsCollector // 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(); + 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) */ @@ -328,14 +368,15 @@ class HttpStatsCollector { // 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)) + 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 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; @@ -352,14 +393,15 @@ class HttpStatsCollector { // 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)) + 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 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; @@ -376,7 +418,8 @@ class HttpStatsCollector // calc average transactions per flow if (m_FlowTable.size() != 0) - m_GeneralStats.averageNumOfHttpTransactionsPerFlow = (double)m_GeneralStats.numOfHttpTransactions / (double)m_FlowTable.size(); + m_GeneralStats.averageNumOfHttpTransactionsPerFlow = + (double)m_GeneralStats.numOfHttpTransactions / (double)m_FlowTable.size(); } // set last seen sequence number @@ -384,7 +427,6 @@ class HttpStatsCollector } } - /** * Collect stats relevant for HTTP request messages */ @@ -393,7 +435,8 @@ class HttpStatsCollector m_RequestStats.numOfMessages++; m_RequestStats.totalMessageHeaderSize += req->getHeaderLen(); if (m_RequestStats.numOfMessages != 0) - m_RequestStats.averageMessageHeaderSize = (double)m_RequestStats.totalMessageHeaderSize / (double)m_RequestStats.numOfMessages; + 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); @@ -403,7 +446,6 @@ class HttpStatsCollector m_RequestStats.methodCount[req->getFirstLine()->getMethod()]++; } - /** * Collect stats relevant for HTTP response messages */ @@ -412,7 +454,8 @@ class HttpStatsCollector m_ResponseStats.numOfMessages++; m_ResponseStats.totalMessageHeaderSize += res->getHeaderLen(); if (m_ResponseStats.numOfMessages != 0) - m_ResponseStats.averageMessageHeaderSize = (double)m_ResponseStats.totalMessageHeaderSize / (double)m_ResponseStats.numOfMessages; + 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); @@ -421,7 +464,8 @@ class HttpStatsCollector 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; + m_ResponseStats.averageContentLengthSize = (double)m_ResponseStats.totalContentLengthSize / + (double)m_ResponseStats.numOfMessagesWithContentLength; } // extract content-type and add to content-type map @@ -449,11 +493,11 @@ class HttpStatsCollector double getCurTime(void) { - struct timeval tv; + struct timeval tv; - gettimeofday(&tv, NULL); + gettimeofday(&tv, NULL); - return (((double) tv.tv_sec) + (double) (tv.tv_usec / 1000000.0)); + return (((double)tv.tv_sec) + (double)(tv.tv_usec / 1000000.0)); } HttpGeneralStats m_GeneralStats; diff --git a/Examples/HttpAnalyzer/main.cpp b/Examples/HttpAnalyzer/main.cpp index 35a7bffaf8..b1f6803177 100644 --- a/Examples/HttpAnalyzer/main.cpp +++ b/Examples/HttpAnalyzer/main.cpp @@ -1,13 +1,14 @@ /** * 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 + * 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 @@ -31,37 +32,35 @@ #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} +// clang-format off +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 } }; - +// clang-format on struct HttpPacketArrivedData { @@ -69,75 +68,82 @@ struct HttpPacketArrivedData 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; + << "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; + 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(); + 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; + 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) +void printStatsHeadline(const std::string& description) { - std::cout << std::endl << description << std::endl << std::string(description.length(),'-') << std::endl << std::endl; + std::cout << std::endl + << description << std::endl + << std::string(description.length(), '-') << std::endl + << std::endl; } - /** * packet capture callback - called whenever a packet arrives */ @@ -146,7 +152,7 @@ void httpPacketArrive(pcpp::RawPacket* packet, pcpp::PcapLiveDevice* dev, void* // 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); @@ -164,15 +170,17 @@ void httpPacketArrive(pcpp::RawPacket* packet, pcpp::PcapLiveDevice* dev, void* void printMethods(const HttpRequestStats& reqStatscollector) { // create the table - std::vector columnNames = {"Method", "Count"}; - std::vector columnsWidths = {9, 5}; + 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; }); + 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) @@ -182,37 +190,46 @@ void printMethods(const HttpRequestStats& reqStatscollector) switch (iter.first) { case pcpp::HttpRequestLayer::HttpGET: - values << "GET" << "|" << reqStatscollector.methodCount.at(pcpp::HttpRequestLayer::HttpGET); + values << "GET" + << "|" << reqStatscollector.methodCount.at(pcpp::HttpRequestLayer::HttpGET); break; case pcpp::HttpRequestLayer::HttpPOST: - values << "POST" << "|" << reqStatscollector.methodCount.at(pcpp::HttpRequestLayer::HttpPOST); + values << "POST" + << "|" << reqStatscollector.methodCount.at(pcpp::HttpRequestLayer::HttpPOST); break; case pcpp::HttpRequestLayer::HttpCONNECT: - values << "CONNECT" << "|" << reqStatscollector.methodCount.at(pcpp::HttpRequestLayer::HttpCONNECT); + values << "CONNECT" + << "|" << reqStatscollector.methodCount.at(pcpp::HttpRequestLayer::HttpCONNECT); break; case pcpp::HttpRequestLayer::HttpDELETE: - values << "DELETE" << "|" << reqStatscollector.methodCount.at(pcpp::HttpRequestLayer::HttpDELETE); + values << "DELETE" + << "|" << reqStatscollector.methodCount.at(pcpp::HttpRequestLayer::HttpDELETE); break; case pcpp::HttpRequestLayer::HttpHEAD: - values << "HEAD" << "|" << reqStatscollector.methodCount.at(pcpp::HttpRequestLayer::HttpHEAD); + values << "HEAD" + << "|" << reqStatscollector.methodCount.at(pcpp::HttpRequestLayer::HttpHEAD); break; case pcpp::HttpRequestLayer::HttpOPTIONS: - values << "OPTIONS" << "|" << reqStatscollector.methodCount.at(pcpp::HttpRequestLayer::HttpOPTIONS); + values << "OPTIONS" + << "|" << reqStatscollector.methodCount.at(pcpp::HttpRequestLayer::HttpOPTIONS); break; case pcpp::HttpRequestLayer::HttpPATCH: - values << "PATCH" << "|" << reqStatscollector.methodCount.at(pcpp::HttpRequestLayer::HttpPATCH); + values << "PATCH" + << "|" << reqStatscollector.methodCount.at(pcpp::HttpRequestLayer::HttpPATCH); break; case pcpp::HttpRequestLayer::HttpPUT: - values << "PUT" << "|" << reqStatscollector.methodCount.at(pcpp::HttpRequestLayer::HttpPUT); + values << "PUT" + << "|" << reqStatscollector.methodCount.at(pcpp::HttpRequestLayer::HttpPUT); break; case pcpp::HttpRequestLayer::HttpTRACE: - values << "TRACE" << "|" << reqStatscollector.methodCount.at(pcpp::HttpRequestLayer::HttpTRACE); + values << "TRACE" + << "|" << reqStatscollector.methodCount.at(pcpp::HttpRequestLayer::HttpTRACE); break; default: break; } - if(iter.first != pcpp::HttpRequestLayer::HttpMethod::HttpMethodUnknown) + if (iter.first != pcpp::HttpRequestLayer::HttpMethod::HttpMethodUnknown) { printer.printRow(values.str(), '|'); } @@ -224,7 +241,8 @@ void printMethods(const HttpRequestStats& reqStatscollector) */ bool hostnameComparer(const std::pair& leftHost, const std::pair& rightHost) { - return leftHost.second > rightHost.second || (leftHost.second == rightHost.second && leftHost.first > rightHost.first); + return leftHost.second > rightHost.second || + (leftHost.second == rightHost.second && leftHost.first > rightHost.first); } /** @@ -233,18 +251,19 @@ bool hostnameComparer(const std::pair& leftHost, const std::pa void printHostnames(HttpRequestStats& reqStatscollector) { // create the table - std::vector columnNames = {"Hostname", "Count"}; - std::vector columnsWidths = {40, 5}; + 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::unordered_map you must copy it to a std::vector and sort it then - std::vector > map2vec(reqStatscollector.hostnameCount.begin(), reqStatscollector.hostnameCount.end()); + 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) + for (const auto& hostname : map2vec) { std::stringstream values; values << hostname.first << "|" << hostname.second; @@ -252,21 +271,24 @@ void printHostnames(HttpRequestStats& reqStatscollector) } } - /** * 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}; + 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::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; @@ -274,21 +296,24 @@ void printStatusCodes(const HttpResponseStats& resStatscollector) } } - /** * 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}; + 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::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; @@ -296,9 +321,9 @@ void printContentTypes(const HttpResponseStats& resStatscollector) } } - /** - * 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) { @@ -310,11 +335,13 @@ void printStatsSummary(HttpStatsCollector& collector) 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("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 transactions per flow", collector.getGeneralStats().averageNumOfHttpTransactionsPerFlow, + "Transactions"); PRINT_STAT_LINE("Average data per flow", collector.getGeneralStats().averageAmountOfDataPerFlow, "Bytes"); printStatsHeadline("HTTP request stats"); @@ -328,8 +355,10 @@ void printStatsSummary(HttpStatsCollector& collector) 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("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"); @@ -345,7 +374,6 @@ void printStatsSummary(HttpStatsCollector& collector) printContentTypes(collector.getResponseStats()); } - /** * Print the current rates. Should be called periodically during traffic capture */ @@ -354,13 +382,13 @@ 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 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 */ @@ -370,7 +398,6 @@ void onApplicationInterrupted(void* cookie) *shouldStop = true; } - /** * activate HTTP analysis from pcap file */ @@ -390,16 +417,14 @@ void analyzeHttpFromPcapFile(const std::string& pcapFileName, uint16_t dstPort) // 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)) + 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; + std::cout << std::endl << std::endl << "STATS SUMMARY" << std::endl << "=============" << std::endl; printStatsSummary(collector); // close input file @@ -409,11 +434,11 @@ void analyzeHttpFromPcapFile(const std::string& pcapFileName, uint16_t dstPort) delete reader; } - /** * activate HTTP analysis from live traffic */ -void analyzeHttpFromLiveTraffic(pcpp::PcapLiveDevice* dev, bool printRatesPeriodically, int printRatePeriod, const std::string& savePacketsToFileName, uint16_t dstPort) +void analyzeHttpFromLiveTraffic(pcpp::PcapLiveDevice* dev, bool printRatesPeriodically, int printRatePeriod, + const std::string& savePacketsToFileName, uint16_t dstPort) { // open the device if (!dev->open()) @@ -441,12 +466,11 @@ void analyzeHttpFromLiveTraffic(pcpp::PcapLiveDevice* dev, bool printRatesPeriod 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) + while (!shouldStop) { pcpp::multiPlatformSleep(printRatePeriod); @@ -466,9 +490,7 @@ void analyzeHttpFromLiveTraffic(pcpp::PcapLiveDevice* dev, bool printRatesPeriod collector.calcRates(); // print stats summary - std::cout << std::endl << std::endl - << "STATS SUMMARY" << std::endl - << "=============" << std::endl; + std::cout << std::endl << std::endl << "STATS SUMMARY" << std::endl << "=============" << std::endl; printStatsSummary(collector); // close and free the writer device @@ -494,47 +516,45 @@ int main(int argc, char* argv[]) std::string readPacketsFromPcapFileName = ""; - int optionIndex = 0; int opt = 0; - while((opt = getopt_long(argc, argv, "i:p:f:o:r:hvld", HttpAnalyzerOptions, &optionIndex)) != -1) + 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); + 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); + case 'v': + printAppVersion(); + break; + case 'l': + listInterfaces(); + break; + default: + printUsage(); + exit(-1); } } @@ -552,9 +572,10 @@ int main(int argc, char* argv[]) { analyzeHttpFromPcapFile(readPacketsFromPcapFileName, nPort); } - else // analyze in live traffic mode + else // analyze in live traffic mode { - pcpp::PcapLiveDevice* dev = pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDeviceByIpOrName(interfaceNameOrIP); + pcpp::PcapLiveDevice* dev = + pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDeviceByIpOrName(interfaceNameOrIP); if (dev == nullptr) EXIT_WITH_ERROR("Couldn't find interface by provided IP address or name"); diff --git a/Examples/IPDefragUtil/main.cpp b/Examples/IPDefragUtil/main.cpp index 76d5ff532a..394e87ddaf 100644 --- a/Examples/IPDefragUtil/main.cpp +++ b/Examples/IPDefragUtil/main.cpp @@ -11,23 +11,22 @@ #include "SystemUtils.h" #include "getopt.h" - -#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 } }; /** @@ -47,58 +46,66 @@ struct DefragStats int ipv6PacketsDefragmented; int totalPacketsWritten; - void clear() { memset(this, 0, sizeof(DefragStats)); } - DefragStats() { clear(); } + 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; + 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; + 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::unordered_map fragIDs, - bool copyAllPacketsToOutputFile, - DefragStats& stats) +void processPackets(pcpp::IFileReaderDevice* reader, pcpp::IFileWriterDevice* writer, bool filterByBpf, + const std::string& bpfFilter, bool filterByIpID, std::unordered_map fragIDs, + bool copyAllPacketsToOutputFile, DefragStats& stats) { pcpp::RawPacket rawPacket; pcpp::BPFStringFilter filter(bpfFilter); @@ -123,7 +130,7 @@ void processPackets(pcpp::IFileReaderDevice* reader, pcpp::IFileWriterDevice* wr { stats.ipPacketsMatchBpfFilter++; } - else // if not - set the packet as not marked for de-fragmentation + else // if not - set the packet as not marked for de-fragmentation { defragPacket = false; } @@ -144,7 +151,7 @@ void processPackets(pcpp::IFileReaderDevice* reader, pcpp::IFileWriterDevice* wr stats.ipv6Packets++; isIPv6Packet = true; } - else // if not - set the packet as not marked for de-fragmentation + else // if not - set the packet as not marked for de-fragmentation { defragPacket = false; } @@ -161,7 +168,7 @@ void processPackets(pcpp::IFileReaderDevice* reader, pcpp::IFileWriterDevice* wr { stats.ipv4PacketsMatchIpIDs++; } - else // if not - set the packet as not marked for de-fragmentation + else // if not - set the packet as not marked for de-fragmentation { defragPacket = false; } @@ -179,7 +186,7 @@ void processPackets(pcpp::IFileReaderDevice* reader, pcpp::IFileWriterDevice* wr { stats.ipv6PacketsMatchFragIDs++; } - else // if not - set the packet as not marked for de-fragmentation + else // if not - set the packet as not marked for de-fragmentation { defragPacket = false; } @@ -196,7 +203,8 @@ void processPackets(pcpp::IFileReaderDevice* reader, pcpp::IFileWriterDevice* wr // - 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)) + ((status == pcpp::IPReassembly::NON_IP_PACKET || status == pcpp::IPReassembly::NON_FRAGMENT) && + copyAllPacketsToOutputFile)) { writer->writePacket(*result->getRawPacket()); stats.totalPacketsWritten++; @@ -215,11 +223,9 @@ void processPackets(pcpp::IFileReaderDevice* reader, pcpp::IFileWriterDevice* wr } // 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 (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++; @@ -233,11 +239,9 @@ void processPackets(pcpp::IFileReaderDevice* reader, pcpp::IFileWriterDevice* wr writer->writePacket(rawPacket); stats.totalPacketsWritten++; } - } } - /** * A method for printing fragmentation process stats */ @@ -256,10 +260,12 @@ void printStats(const DefragStats& stats, bool filterByIpID, bool filterByBpf) } if (filterByBpf) stream << "IP packets match BPF filter: " << stats.ipPacketsMatchBpfFilter << std::endl; - stream << "Total fragments matched: " << (stats.ipv4FragmentsMatched + stats.ipv6FragmentsMatched) << 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 << "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; @@ -267,7 +273,6 @@ void printStats(const DefragStats& stats, bool filterByIpID, bool filterByBpf) std::cout << stream.str(); } - /** * main method of the application */ @@ -285,68 +290,67 @@ int main(int argc, char* argv[]) std::unordered_map fragIDMap; bool copyAllPacketsToOutputFile = false; - - while((opt = getopt_long(argc, argv, "o:d:f:ahv", DefragUtilOptions, &optionIndex)) != -1) + while ((opt = getopt_long(argc, argv, "o:d:f:ahv", DefragUtilOptions, &optionIndex)) != -1) { switch (opt) { - case 0: - { - break; - } - case 'o': + 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, ',')) { - outputFile = optarg; - break; + // 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); } - 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': + // verify list is not empty + if (fragIDMap.empty()) { - 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; + 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; + } } } @@ -363,14 +367,14 @@ int main(int argc, char* argv[]) switch (paramIndex) { - case 0: - { - inputFile = argv[i]; - break; - } + case 0: + { + inputFile = argv[i]; + break; + } - default: - EXIT_WITH_ERROR("Unexpected parameter: " << argv[i]); + default: + EXIT_WITH_ERROR("Unexpected parameter: " << argv[i]); } } @@ -392,7 +396,6 @@ int main(int argc, char* argv[]) 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; @@ -416,7 +419,8 @@ int main(int argc, char* argv[]) // run the de-fragmentation process DefragStats stats; - processPackets(reader, writer, filterByBpfFilter, bpfFilter, filterByFragID, fragIDMap, copyAllPacketsToOutputFile, stats); + processPackets(reader, writer, filterByBpfFilter, bpfFilter, filterByFragID, fragIDMap, copyAllPacketsToOutputFile, + stats); // close files reader->close(); diff --git a/Examples/IPFragUtil/main.cpp b/Examples/IPFragUtil/main.cpp index 27ad037f97..c6151ecd1c 100644 --- a/Examples/IPFragUtil/main.cpp +++ b/Examples/IPFragUtil/main.cpp @@ -13,24 +13,23 @@ #include "SystemUtils.h" #include "getopt.h" - -#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 } }; /** @@ -48,56 +47,66 @@ struct FragStats int ipv6PacketsFragmented; int totalPacketsWritten; - void clear() { memset(this, 0, sizeof(FragStats)); } - FragStats() { clear(); } + 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; + 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; + 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)); + uint16_t fragOffsetValue = pcpp::hostToNet16((uint16_t)(fragOffset / 8)); // set the fragment flags bits to zero fragOffsetValue &= (uint16_t)0xff1f; @@ -110,7 +119,6 @@ void setIPv4FragmentParams(pcpp::IPv4Layer* fragIpLayer, size_t fragOffset, bool fragIpLayer->getIPv4Header()->fragmentOffset = fragOffsetValue; } - /** * Add IPv6 fragmentation extension to an IPv6 fragment packet and set fragmentation parameters */ @@ -120,7 +128,6 @@ void setIPv6FragmentParams(pcpp::IPv6Layer* fragIpLayer, size_t fragOffset, bool fragIpLayer->addExtension(fragHeader); } - /** * Generate a 4-byte positive random number. Used for generating IPv6 fragment ID */ @@ -141,10 +148,11 @@ uint32_t generateRandomNumber() * 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 + * 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) +void splitIPPacketToFragmentsBySize(pcpp::RawPacket* rawPacket, size_t fragmentSize, + pcpp::RawPacketVector& resultFragments) { // parse raw packet pcpp::Packet packet(rawPacket); @@ -161,7 +169,7 @@ void splitIPPacketToFragmentsBySize(pcpp::RawPacket* rawPacket, size_t fragmentS pcpp::Layer* ipLayer = nullptr; if (ipProto == pcpp::IPv4) ipLayer = packet.getLayerOfType(); - else // ipProto == IPv6 + else // ipProto == IPv6 ipLayer = packet.getLayerOfType(); // if packet payload size is less than the requested fragment size, don't fragment and return @@ -182,7 +190,8 @@ void splitIPPacketToFragmentsBySize(pcpp::RawPacket* rawPacket, size_t fragmentS 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 + // 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; @@ -198,7 +207,7 @@ void splitIPPacketToFragmentsBySize(pcpp::RawPacket* rawPacket, size_t fragmentS pcpp::Layer* fragIpLayer = nullptr; if (ipProto == pcpp::IPv4) fragIpLayer = newFrag.getLayerOfType(); - else // ipProto == IPv6 + else // ipProto == IPv6 fragIpLayer = newFrag.getLayerOfType(); // delete all layers above IP layer @@ -211,7 +220,7 @@ void splitIPPacketToFragmentsBySize(pcpp::RawPacket* rawPacket, size_t fragmentS // set fragment parameters in IPv4/6 layer if (ipProto == pcpp::IPv4) setIPv4FragmentParams((pcpp::IPv4Layer*)fragIpLayer, curOffset, lastFrag); - else // ipProto == IPv6 + else // ipProto == IPv6 setIPv6FragmentParams((pcpp::IPv6Layer*)fragIpLayer, curOffset, lastFrag, randomNum); // compute all calculated fields of the new fragment @@ -223,20 +232,15 @@ void splitIPPacketToFragmentsBySize(pcpp::RawPacket* rawPacket, size_t fragmentS // 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::unordered_map ipIDs, - bool copyAllPacketsToOutputFile, - FragStats& stats) +void processPackets(pcpp::IFileReaderDevice* reader, pcpp::IFileWriterDevice* writer, int fragSize, bool filterByBpf, + const std::string& bpfFilter, bool filterByIpID, std::unordered_map ipIDs, + bool copyAllPacketsToOutputFile, FragStats& stats) { stats.clear(); @@ -259,7 +263,7 @@ void processPackets(pcpp::IFileReaderDevice* reader, pcpp::IFileWriterDevice* wr { stats.ipPacketsMatchBpfFilter++; } - else // if not - set the packet as not marked for fragmentation + else // if not - set the packet as not marked for fragmentation { fragPacket = false; } @@ -274,12 +278,12 @@ void processPackets(pcpp::IFileReaderDevice* reader, pcpp::IFileWriterDevice* wr ipProto = pcpp::IPv4; stats.ipv4Packets++; } - else if (parsedPacket.isPacketOfType(pcpp::IPv6)) // check if packet is of type IPv6 + 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 + else // if not - set the packet as not marked for fragmentation { fragPacket = false; } @@ -296,7 +300,7 @@ void processPackets(pcpp::IFileReaderDevice* reader, pcpp::IFileWriterDevice* wr { stats.ipv4PacketsMatchIpIDs++; } - else // if not - set the packet as not marked for fragmentation + else // if not - set the packet as not marked for fragmentation { fragPacket = false; } @@ -315,11 +319,11 @@ void processPackets(pcpp::IFileReaderDevice* reader, pcpp::IFileWriterDevice* wr { stats.ipPacketsUnderSize++; } - else if (resultFrags.size() > 1) // packet was fragmented + else if (resultFrags.size() > 1) // packet was fragmented { if (ipProto == pcpp::IPv4) stats.ipv4PacketsFragmented++; - else // ipProto == IPv6 + else // ipProto == IPv6 stats.ipv6PacketsFragmented++; } @@ -340,7 +344,6 @@ void processPackets(pcpp::IFileReaderDevice* reader, pcpp::IFileWriterDevice* wr } } - /** * A method for printing fragmentation process stats */ @@ -364,7 +367,6 @@ void printStats(const FragStats& stats, bool filterByIpID, bool filterByBpf) std::cout << stream.str(); } - /** * main method of the application */ @@ -383,76 +385,76 @@ int main(int argc, char* argv[]) std::unordered_map ipIDMap; bool copyAllPacketsToOutputFile = false; - while((opt = getopt_long(argc, argv, "o:s:d:f:ahv", FragUtilOptions, &optionIndex)) != -1) + 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': + 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, ',')) { - 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; + // 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); } - 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': + // verify list is not empty + if (ipIDMap.empty()) { - 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; + 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; + } } } @@ -470,18 +472,17 @@ int main(int argc, char* argv[]) switch (paramIndex) { - case 0: - { - inputFile = argv[i]; - break; - } + case 0: + { + inputFile = argv[i]; + break; + } - default: - EXIT_WITH_ERROR("Unexpected parameter: " << argv[i]); + default: + EXIT_WITH_ERROR("Unexpected parameter: " << argv[i]); } } - if (inputFile == "") { EXIT_WITH_ERROR("Input file name was not given"); @@ -505,7 +506,6 @@ int main(int argc, char* argv[]) 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; @@ -529,7 +529,8 @@ int main(int argc, char* argv[]) // run the fragmentation process FragStats stats; - processPackets(reader, writer, fragSize, filterByBpfFilter, bpfFilter, filterByIpID, ipIDMap, copyAllPacketsToOutputFile, stats); + processPackets(reader, writer, fragSize, filterByBpfFilter, bpfFilter, filterByIpID, ipIDMap, + copyAllPacketsToOutputFile, stats); // close files reader->close(); diff --git a/Examples/IcmpFileTransfer/Common.cpp b/Examples/IcmpFileTransfer/Common.cpp index 4e3037e7d3..80f080ce1d 100644 --- a/Examples/IcmpFileTransfer/Common.cpp +++ b/Examples/IcmpFileTransfer/Common.cpp @@ -10,101 +10,100 @@ #include "SystemUtils.h" #include "PcapPlusPlusVersion.h" - #if defined(_WIN32) -#define SEPARATOR '\\' +# define SEPARATOR '\\' #else -#define SEPARATOR '/' +# define SEPARATOR '/' #endif #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} +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' }, + { "version", no_argument, nullptr, 'v' }, + { "help", no_argument, nullptr, 'h' }, + { 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) -#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) +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 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; + 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; + 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(); + const std::vector& devList = + pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDevicesList(); std::cout << std::endl << "Network interfaces:" << std::endl; - for (const auto &dev : devList) + for (const auto& dev : devList) { - std::cout << " -> Name: '" << dev->getName() << "' IP address: " << dev->getIPv4Address().toString() << std::endl; + std::cout << " -> Name: '" << dev->getName() << "' IP address: " << dev->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) +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; @@ -119,48 +118,48 @@ void readCommandLineArguments(int argc, char* argv[], int optionIndex = 0; int opt = 0; - while((opt = getopt_long(argc, argv, "i:d:s:rp:b:hvl", IcmpFTOptions, &optionIndex)) != -1) + 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); + 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); } } @@ -174,7 +173,7 @@ void readCommandLineArguments(int argc, char* argv[], interfaceIP = pcpp::IPv4Address(interfaceNameOrIP); myIP = interfaceIP; } - catch(const std::exception&) + catch (const std::exception&) { pcpp::PcapLiveDevice* dev = pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDeviceByName(interfaceNameOrIP); if (dev == nullptr) @@ -192,7 +191,7 @@ void readCommandLineArguments(int argc, char* argv[], { tempIP = pcpp::IPv4Address(otherSideIPAsString); } - catch(const std::exception&) + catch (const std::exception&) { EXIT_WITH_ERROR_PRINT_USAGE("Invalid " << otherSide << " IP address"); } @@ -211,20 +210,17 @@ void readCommandLineArguments(int argc, char* argv[], // 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)"); + 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) +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; @@ -262,22 +258,16 @@ bool sendIcmpMessage(pcpp::PcapLiveDevice* dev, 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) +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) +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); } diff --git a/Examples/IcmpFileTransfer/Common.h b/Examples/IcmpFileTransfer/Common.h index 2deae9f696..baf7c0d3e8 100644 --- a/Examples/IcmpFileTransfer/Common.h +++ b/Examples/IcmpFileTransfer/Common.h @@ -4,7 +4,6 @@ #include "IpAddress.h" #include "PcapLiveDevice.h" - #define ICMP_FT_WAITING_FT_START 0x345a56c8e7f3cd67ULL #define ICMP_FT_START 0xd45ae6c2e7a3cd67ULL #define ICMP_FT_WAITING_DATA 0x6d5f86c817fb5d7eULL @@ -15,17 +14,20 @@ #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 @@ -33,36 +35,28 @@ 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); +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); /** - * 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, diff --git a/Examples/IcmpFileTransfer/IcmpFileTransfer-catcher.cpp b/Examples/IcmpFileTransfer/IcmpFileTransfer-catcher.cpp index 673bbfa6cb..a1ecd4703d 100644 --- a/Examples/IcmpFileTransfer/IcmpFileTransfer-catcher.cpp +++ b/Examples/IcmpFileTransfer/IcmpFileTransfer-catcher.cpp @@ -18,7 +18,6 @@ #include "Common.h" #include "SystemUtils.h" - /** * A struct used for starting a file transfer, mainly sending or getting the file name */ @@ -58,10 +57,9 @@ struct IcmpFileContentDataSend 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) { @@ -84,7 +82,8 @@ static bool waitForFileTransferStart(pcpp::RawPacket* rawPacket, pcpp::PcapLiveD // 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) + 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 @@ -101,26 +100,23 @@ static bool waitForFileTransferStart(pcpp::RawPacket* rawPacket, pcpp::PcapLiveD 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)) + // 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 + // 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) { @@ -143,7 +139,8 @@ static bool getFileContent(pcpp::RawPacket* rawPacket, pcpp::PcapLiveDevice* dev // 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) + 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 @@ -157,22 +154,27 @@ static bool getFileContent(pcpp::RawPacket* rawPacket, pcpp::PcapLiveDevice* dev 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 + // 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 + // 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 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())); + 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 @@ -196,7 +198,6 @@ static bool getFileContent(pcpp::RawPacket* rawPacket, pcpp::PcapLiveDevice* dev return false; } - /** * Receive a file from the pitcher */ @@ -218,12 +219,7 @@ void receiveFile(pcpp::IPv4Address pitcherIP, pcpp::IPv4Address catcherIP) std::cout << "Waiting for pitcher to send a file..." << std::endl; - IcmpFileTransferStart icmpFTStart = { - pitcherIP, - catcherIP, - "", - 0 - }; + 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); @@ -231,20 +227,14 @@ void receiveFile(pcpp::IPv4Address pitcherIP, pcpp::IPv4Address catcherIP) 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); + 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 << "' "; + std::cout << "Getting file from pitcher: '" << icmpFTStart.fileName << "' "; IcmpFileContentDataRecv icmpFileContentData = { - pitcherIP, - catcherIP, - &file, - icmpFTStart.fileName, - (uint16_t)(icmpFTStart.icmpId+1), - 0, - 0 + 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 @@ -252,13 +242,14 @@ void receiveFile(pcpp::IPv4Address pitcherIP, pcpp::IPv4Address catcherIP) if (!res) { file.close(); - EXIT_WITH_ERROR_AND_RUN_COMMAND("Cannot start capturing packets", std::remove(icmpFTStart.fileName.c_str())); + 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; + std::cout << std::endl + << std::endl + << "Finished getting file '" << icmpFTStart.fileName << "' " + << "[received " << icmpFileContentData.fileSize << " bytes]" << std::endl; } else EXIT_WITH_ERROR("Cannot create file"); @@ -268,10 +259,10 @@ void receiveFile(pcpp::IPv4Address pitcherIP, pcpp::IPv4Address catcherIP) 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) { @@ -294,11 +285,13 @@ static bool startFileTransfer(pcpp::RawPacket* rawPacket, pcpp::PcapLiveDevice* // 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) + 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 + // 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; @@ -308,20 +301,17 @@ static bool startFileTransfer(pcpp::RawPacket* rawPacket, pcpp::PcapLiveDevice* 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)) + 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) { @@ -344,7 +334,8 @@ static bool sendContent(pcpp::RawPacket* rawPacket, pcpp::PcapLiveDevice* dev, v // 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) + 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 @@ -365,12 +356,10 @@ static bool sendContent(pcpp::RawPacket* rawPacket, pcpp::PcapLiveDevice* dev, v // 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)) + // 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 @@ -380,12 +369,11 @@ static bool sendContent(pcpp::RawPacket* rawPacket, pcpp::PcapLiveDevice* dev, v // 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)) + // 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 @@ -399,12 +387,11 @@ static bool sendContent(pcpp::RawPacket* rawPacket, pcpp::PcapLiveDevice* dev, v // 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())) + // 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 @@ -424,11 +411,10 @@ static bool sendContent(pcpp::RawPacket* rawPacket, pcpp::PcapLiveDevice* dev, v return false; } - /** * Send a file to the pitcher */ -void sendFile(const std::string &filePath, pcpp::IPv4Address pitcherIP, pcpp::IPv4Address catcherIP, size_t blockSize) +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); @@ -445,15 +431,15 @@ void sendFile(const std::string &filePath, pcpp::IPv4Address pitcherIP, pcpp::IP 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); + 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(); + uint32_t fileSize = file.tellg(); - // go back to the beginning of the file + // 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 @@ -461,32 +447,19 @@ void sendFile(const std::string &filePath, pcpp::IPv4Address pitcherIP, pcpp::IP std::cout << "Waiting for pitcher to send a keep-alive signal..." << std::endl; - IcmpFileTransferStart icmpFTStart = { - pitcherIP, - catcherIP, - fileName, - 0 - }; + 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); + // 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 - }; + 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]; @@ -496,19 +469,19 @@ void sendFile(const std::string &filePath, pcpp::IPv4Address pitcherIP, pcpp::IP res = dev->startCaptureBlockingMode(sendContent, &icmpFileContentData, -1); // free the memory block data and close the file - delete [] icmpFileContentData.memblock; + 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; + std::cout << std::endl + << std::endl + << "Finished sending '" << fileName << "' " + << "[sent " << fileSize << " bytes]" << std::endl; } - else // if file couldn't be opened + else // if file couldn't be opened EXIT_WITH_ERROR("Cannot open file '" << filePath << "'"); // close the device @@ -532,8 +505,10 @@ int main(int argc, char* argv[]) // 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); + // 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) diff --git a/Examples/IcmpFileTransfer/IcmpFileTransfer-pitcher.cpp b/Examples/IcmpFileTransfer/IcmpFileTransfer-pitcher.cpp index 82d1b5f73a..2702f3b0c0 100644 --- a/Examples/IcmpFileTransfer/IcmpFileTransfer-pitcher.cpp +++ b/Examples/IcmpFileTransfer/IcmpFileTransfer-pitcher.cpp @@ -10,7 +10,7 @@ #include #include #ifndef _MSC_VER -#include "unistd.h" +# include "unistd.h" #endif #include "EthLayer.h" #include "IPv4Layer.h" @@ -21,21 +21,20 @@ #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 +# include 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); @@ -80,10 +79,9 @@ struct IcmpFileContentData 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) { @@ -106,7 +104,8 @@ static void waitForFileTransferStart(pcpp::RawPacket* rawPacket, pcpp::PcapLiveD // 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) + 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 @@ -125,10 +124,9 @@ static void waitForFileTransferStart(pcpp::RawPacket* rawPacket, pcpp::PcapLiveD 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) { @@ -151,14 +149,16 @@ static void getFileContent(pcpp::RawPacket* rawPacket, pcpp::PcapLiveDevice* dev // 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) + 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 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; @@ -172,15 +172,16 @@ static void getFileContent(pcpp::RawPacket* rawPacket, pcpp::PcapLiveDevice* dev // 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 + // 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; + std::cout << std::endl + << std::endl + << "Didn't get expected ICMP message #" << icmpFileContentData->expectedIcmpId << ", got #" + << pcpp::netToHost16(icmpLayer->getEchoReplyData()->header->id) << std::endl; return; } @@ -192,7 +193,8 @@ static void getFileContent(pcpp::RawPacket* rawPacket, pcpp::PcapLiveDevice* dev 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); + icmpFileContentData->file->write((char*)icmpLayer->getEchoReplyData()->data, + icmpLayer->getEchoReplyData()->dataLength); // count the bytes received icmpFileContentData->fileSize += icmpLayer->getEchoReplyData()->dataLength; @@ -206,7 +208,6 @@ static void getFileContent(pcpp::RawPacket* rawPacket, pcpp::PcapLiveDevice* dev } } - /** * Receive a file from the catcher */ @@ -228,18 +229,14 @@ void receiveFile(pcpp::IPv4Address pitcherIP, pcpp::IPv4Address catcherIP, int p // 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); + 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, - "" - }; + IcmpFileTransferStartRecv icmpFTStart = { pitcherIP, catcherIP, false, "" }; std::cout << "Waiting for catcher to start sending a file..." << std::endl; @@ -250,18 +247,19 @@ void receiveFile(pcpp::IPv4Address pitcherIP, pcpp::IPv4Address catcherIP, int p // 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 + // 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 + // 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); + 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); @@ -270,51 +268,44 @@ void receiveFile(pcpp::IPv4Address pitcherIP, pcpp::IPv4Address catcherIP, int p // 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); + 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 + 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 + // 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())); + 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 + // 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); + 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) @@ -328,25 +319,27 @@ void receiveFile(pcpp::IPv4Address pitcherIP, pcpp::IPv4Address catcherIP, int p // 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 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); + 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())); + 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; + std::cout << std::endl + << std::endl + << "Finished getting file '" << icmpFTStart.fileName << "' " + << "[received " << icmpFileContentData.fileSize << " bytes]" << std::endl; } else EXIT_WITH_ERROR("Cannot create file"); @@ -355,10 +348,9 @@ void receiveFile(pcpp::IPv4Address pitcherIP, pcpp::IPv4Address catcherIP, int p 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) { @@ -385,7 +377,8 @@ static bool waitForFileTransferStartAck(pcpp::RawPacket* rawPacket, pcpp::PcapLi // 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) + if (ip4Layer->getSrcIPv4Address() != icmpData->catcherIPAddr || + ip4Layer->getDstIPv4Address() != icmpData->pitcherIPAddr) return false; // verify the message type is ICMP_FT_ACK @@ -397,11 +390,11 @@ static bool waitForFileTransferStartAck(pcpp::RawPacket* rawPacket, pcpp::PcapLi 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) +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); @@ -419,7 +412,8 @@ void sendFile(const std::string &filePath, pcpp::IPv4Address pitcherIP, pcpp::IP // 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); + 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"); @@ -428,7 +422,7 @@ void sendFile(const std::string &filePath, pcpp::IPv4Address pitcherIP, pcpp::IP memset(memblock, 0, blockSize); // try the open the file for reading - std::ifstream file(filePath.c_str(), std::ios::in|std::ios::binary); + std::ifstream file(filePath.c_str(), std::ios::in | std::ios::binary); if (file.is_open()) { @@ -443,30 +437,25 @@ void sendFile(const std::string &filePath, pcpp::IPv4Address pitcherIP, pcpp::IP // copy the file name to the buffer strcpy((char*)memblock, fileName.c_str()); - IcmpFileTransferStartSend ftStartData = { - icmpId, - pitcherIP, - catcherIP - }; + 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 + // 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)) + // 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); + // 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"); @@ -486,21 +475,22 @@ void sendFile(const std::string &filePath, pcpp::IPv4Address pitcherIP, pcpp::IP 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 + // 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 + // 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)) + // 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) @@ -522,25 +512,28 @@ void sendFile(const std::string &filePath, pcpp::IPv4Address pitcherIP, pcpp::IP 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 + // 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())) + 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)) + // 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; + std::cout << std::endl + << std::endl + << "Finished sending '" << fileName << "' " + << "[sent " << bytesSentSoFar << " bytes]" << std::endl; } else EXIT_WITH_ERROR("Cannot open file '" << filePath << "'"); @@ -548,7 +541,7 @@ void sendFile(const std::string &filePath, pcpp::IPv4Address pitcherIP, pcpp::IP // close the file and the device. Free the memory for memblock file.close(); dev->close(); - delete [] memblock; + delete[] memblock; } /** @@ -568,8 +561,10 @@ int main(int argc, char* argv[]) // 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); + // 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) diff --git a/Examples/KniPong/main.cpp b/Examples/KniPong/main.cpp index c03bb187d0..919af9988e 100644 --- a/Examples/KniPong/main.cpp +++ b/Examples/KniPong/main.cpp @@ -30,12 +30,12 @@ #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,96 +43,93 @@ #define DEFAULT_KNI_NAME "pcppkni0" #define DEFAULT_PORT 62604 - namespace { -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 PacketStats -{ - unsigned long totalPackets; - unsigned long udpPacketsIn; - unsigned long udpPacketsOutFail; - unsigned long arpPacketsIn; - unsigned long arpPacketsOutFail; -}; - - -static bool doContinue = true; + struct KniPongArgs + { + std::string kniIp; + std::string outIp; + std::string kniName; + uint16_t kniPort; + }; + typedef int linuxFd; -/** - * 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); -} + 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; + }; -/** - * 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; -} + 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); + } -inline void parseArgs(int argc, char* argv[], KniPongArgs& args) -{ - struct option KniPongOptions[] = + /** + * Print application usage + */ + inline void printUsage() { - {"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) + 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) { - switch (opt) + 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': @@ -154,575 +151,578 @@ inline void parseArgs(int argc, char* argv[], KniPongArgs& args) { printUsage(); std::exit(0); - } break; + } + break; default: { printUsage(); exit(1); - } break; + } + 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"); } - } - // 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; - pcpp::IPv4Address outIp; - try - { - kniIp = pcpp::IPv4Address(args.kniIp); - } - catch (const std::exception&) - { - EXIT_WITH_ERROR("Cannot assign an invalid IPv4 address to the KNI device"); - } - try - { - outIp = pcpp::IPv4Address(args.outIp); - } - catch (const std::exception&) - { - EXIT_WITH_ERROR("Cannot assign an invalid IPv4 address as the virtual address"); - } + pcpp::IPv4Address kniIp; + pcpp::IPv4Address outIp; + try + { + kniIp = pcpp::IPv4Address(args.kniIp); + } + catch (const std::exception&) + { + EXIT_WITH_ERROR("Cannot assign an invalid IPv4 address to the KNI device"); + } + try + { + outIp = pcpp::IPv4Address(args.outIp); + } + catch (const std::exception&) + { + EXIT_WITH_ERROR("Cannot assign an invalid IPv4 address as the virtual address"); + } - 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 << "'"); + 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; + } -/** - * 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 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; - static void setCallbacks() + /** + * Setup IP of net device by calling the ip unix utility + */ + inline bool setKniIp(const pcpp::IPv4Address& ip, const std::string& kniName) { - 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; + std::ostringstream command; + command << "ip a add " << ip << "/24 dev " << kniName; + pcpp::executeShellCommand(command.str()); + command.str(""); + command << "ip a | grep " << ip; + try + { + std::string result = pcpp::executeShellCommand(command.str()); + return result != ""; + } + catch (const std::runtime_error&) + { + return false; + } } -}; -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; - try - { - std::string result = pcpp::executeShellCommand(command.str()); - return result != ""; - } - catch (const std::runtime_error&) + /** + * KNI device setup routine + */ + inline pcpp::KniDevice* setupKniDevice(const KniPongArgs& args) { - return false; + { + // 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)); + } -/** - * 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"); + return sock; } - 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) + /** + * Handle all ARP requests on KNI interface: map all IPs to same MAC + */ + inline void processArp(pcpp::Packet& packet, pcpp::ArpLayer* arpLayer) { - int old_errno = errno; - EXIT_WITH_ERROR("Could not open socket" << std::endl << "Errno: " << std::strerror(old_errno)); + 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)); } - // 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) + + /** + * 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) { - int old_errno = errno; - close(sock); - EXIT_WITH_ERROR("Could not bind socket" << std::endl << "Errno: " << std::strerror(old_errno)); + 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; } - 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)); -} + /** + * 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; + } -/** - * 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; -} + 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 + } -/** - * 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; + return true; + } - packetStats->totalPackets += numOfPackets; - for (uint32_t i = 0; i < numOfPackets; ++i) + /** + * Connect UDP socket to other IP:port pair derived from our args + */ + void connectUDPSocket(const LinuxSocket& sock, const KniPongArgs& args) { - packet.setRawPacket(packets + i, false); - if ((arpLayer = packet.getLayerOfType()) != NULL) + 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) { - ++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; + int old_errno = errno; + close(sock); + EXIT_WITH_ERROR("Could not connect socket" << std::endl << "Errno: " << std::strerror(old_errno)); } - - // 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) + /** + * Reworked fillbuf from netcat. See description in pingPongProcess + */ + ssize_t fillbuf(linuxFd fd, unsigned char buff[], size_t& buffPos) { - 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) + 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; - 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) + /** + * 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; - /* adjust buffer */ - adjust = buffPos - n; - if (adjust > 0) - std::memmove(buff, buff + n, adjust); - buffPos -= n; - return n; -} - - -/** - * Reworked readwrite from netcat. - * - * Note (echo-Mike): This function and fillbuf/drainbuf - * are analogous to code of NETCAT utility (OpenBSD version) - * Authors of original codebase: - * - Eric Jackson - * - Bob Beck - * - *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) + /** + * Reworked readwrite from netcat. + * + * Note (echo-Mike): This function and fillbuf/drainbuf + * are analogous to code of NETCAT utility (OpenBSD version) + * Authors of original codebase: + * - Eric Jackson + * - Bob Beck + * - *Hobbit* + * See: http://man7.org/linux/man-pages/man1/ncat.1.html + */ + void pingPongProcess(const LinuxSocket& sock) { - /* 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) + 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) { - int old_errno = errno; - if (old_errno != EINTR) + /* 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) { - close(sock); - EXIT_WITH_ERROR("poll returned an error" << std::endl << "Errno: " << std::strerror(old_errno)); + return; } - continue; - } + /* both outputs are gone, we can't continue */ + if (pfd[POLL_NETOUT].fd == -1 && pfd[POLL_STDOUT].fd == -1) + return; - if (num_fds == 0) - { - continue; - } + /* poll */ + int num_fds = poll(pfd, 4, DEFAULT_POLL_TIMEOUT); - /* treat socket error conditions */ - for (n = 0; n < 4; ++n) - { - if (pfd[n].revents & (POLLERR|POLLNVAL)) + /* treat poll errors */ + if (num_fds == -1) { - pfd[n].fd = -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; } - } - /* 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; - } + if (num_fds == 0) + { + continue; + } - /* 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) + /* 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; - /* 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) + } + + if (pfd[POLL_NETIN].events & POLLIN && pfd[POLL_NETIN].revents & POLLHUP && + !(pfd[POLL_NETIN].revents & POLLIN)) { - 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) + + 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; - /* 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; - } + /* 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; + } - /* 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; + /* 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 +} // namespace extern "C" void signal_handler(int) { doContinue = false; } - /** * main method of the application */ @@ -750,13 +750,14 @@ int main(int argc, char* argv[]) 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; + 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..96c77ca63f 100644 --- a/Examples/PcapPlusPlus-benchmark/benchmark.cpp +++ b/Examples/PcapPlusPlus-benchmark/benchmark.cpp @@ -1,15 +1,16 @@ /** * 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` */ @@ -56,9 +57,9 @@ bool handle_packet(Packet& packet) return true; } -int main(int argc, char *argv[]) +int main(int argc, char* argv[]) { - if(argc != 4) + if (argc != 4) { std::cout << "Usage: " << *argv << " \n"; return 1; @@ -67,13 +68,13 @@ int main(int argc, char *argv[]) int total_runs = std::stoi(argv[3]); size_t total_packets = 0; std::vector durations; - for(int i = 0; i < total_runs; ++i) + 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") + if (input_type == "dns") { start = std::chrono::high_resolution_clock::now(); RawPacket rawPacket; @@ -98,11 +99,8 @@ int main(int argc, char *argv[]) total_packets += count; reader.close(); } - auto total_time = std::accumulate( - durations.begin(), - durations.end(), - std::chrono::high_resolution_clock::duration(0) - ); + 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; diff --git a/Examples/PcapPrinter/main.cpp b/Examples/PcapPrinter/main.cpp index d263228474..76add37598 100644 --- a/Examples/PcapPrinter/main.cpp +++ b/Examples/PcapPrinter/main.cpp @@ -21,25 +21,23 @@ #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} +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) - +#define EXIT_WITH_ERROR(reason) \ + do \ + { \ + printUsage(); \ + std::cout << std::endl << "ERROR: " << reason << std::endl << std::endl; \ + exit(1); \ + } while (0) /** * Print application usage @@ -47,36 +45,34 @@ static struct option PcapPrinterOptions[] = 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; + << "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; + 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) { @@ -90,7 +86,8 @@ std::string linkLayerToString(pcpp::LinkLayerType linkLayer) 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) + else if (linkLayer == pcpp::LINKTYPE_RAW || linkLayer == pcpp::LINKTYPE_DLT_RAW1 || + linkLayer == pcpp::LINKTYPE_DLT_RAW2) { std::ostringstream stream; stream << "Raw IP (" << linkLayer << ")"; @@ -102,10 +99,9 @@ std::string linkLayerToString(pcpp::LinkLayerType linkLayer) return stream.str(); } - /** -* print file summary based on the reader type -*/ + * print file summary based on the reader type + */ std::string printFileSummary(pcpp::IFileReaderDevice* reader) { std::ostringstream stream; @@ -147,10 +143,9 @@ std::string printFileSummary(pcpp::IFileReaderDevice* reader) return stream.str(); } - /** -* print all requested packets in a pcap/snoop file -*/ + * 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 @@ -171,10 +166,9 @@ int printPcapPackets(pcpp::IFileReaderDevice* reader, std::ostream* out, int pac return packetCountSoFar; } - /** -* print all requested packets in a pcap-ng file -*/ + * 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 @@ -201,7 +195,6 @@ int printPcapNgPackets(pcpp::PcapNgFileReaderDevice* reader, std::ostream* out, return packetCountSoFar; } - /** * main method of this utility */ @@ -221,34 +214,34 @@ int main(int argc, char* argv[]) int optionIndex = 0; int opt = 0; - while((opt = getopt_long(argc, argv, "o:c:i:svh", PcapPrinterOptions, &optionIndex)) != -1) + 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); + 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); } } @@ -290,7 +283,6 @@ int main(int argc, char* argv[]) delete reader; EXIT_WITH_ERROR("Couldn't set filter '" << filter << "'"); } - } // print file summary diff --git a/Examples/PcapSearch/dirent-for-Visual-Studio/include/dirent.h b/Examples/PcapSearch/dirent-for-Visual-Studio/include/dirent.h index 833351b1da..6adbbfdfa5 100644 --- a/Examples/PcapSearch/dirent-for-Visual-Studio/include/dirent.h +++ b/Examples/PcapSearch/dirent-for-Visual-Studio/include/dirent.h @@ -14,7 +14,7 @@ * Windows Sockets 2.0. */ #ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN #endif #include @@ -36,123 +36,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 */ @@ -176,25 +176,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 */ @@ -203,54 +203,54 @@ /* Return number of bytes needed to store d_namlen */ #define _D_ALLOC_NAMLEN(p) (PATH_MAX) - #ifdef __cplusplus -extern "C" { +extern "C" +{ #endif + /* Wide-character version */ + struct _wdirent + { + /* Always zero */ + long d_ino; -/* Wide-character version */ -struct _wdirent { - /* Always zero */ - long d_ino; - - /* Structure size */ - unsigned short d_reclen; - - /* Length of name without \0 */ - size_t d_namlen; + /* Structure size */ + unsigned short d_reclen; - /* File type */ - int d_type; + /* Length of name without \0 */ + size_t d_namlen; - /* File name */ - wchar_t d_name[PATH_MAX]; -}; -typedef struct _wdirent _wdirent; + /* File type */ + int d_type; -struct _WDIR { - /* Current directory entry */ - struct _wdirent ent; + /* File name */ + wchar_t d_name[PATH_MAX]; + }; + typedef struct _wdirent _wdirent; - /* Private file data */ - WIN32_FIND_DATAW data; + struct _WDIR + { + /* Current directory entry */ + struct _wdirent ent; - /* True if data is valid */ - int cached; + /* Private file data */ + WIN32_FIND_DATAW data; - /* Win32 search handle */ - HANDLE handle; + /* True if data is valid */ + int cached; - /* Initial directory name */ - wchar_t *patt; -}; -typedef struct _WDIR _WDIR; + /* Win32 search handle */ + HANDLE handle; -static _WDIR *_wopendir (const wchar_t *dirname); -static struct _wdirent *_wreaddir (_WDIR *dirp); -static int _wclosedir (_WDIR *dirp); -static void _wrewinddir (_WDIR* dirp); + /* Initial directory name */ + 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); /* For compatibility with Symbian */ #define wdirent _wdirent @@ -260,666 +260,680 @@ static void _wrewinddir (_WDIR* dirp); #define wclosedir _wclosedir #define wrewinddir _wrewinddir - -/* Multi-byte character versions */ -struct dirent { - /* Always zero */ - long d_ino; - - /* Structure size */ - unsigned short d_reclen; - - /* Length of name without \0 */ - size_t d_namlen; - - /* File type */ - int d_type; - - /* File name */ - char d_name[PATH_MAX]; -}; -typedef struct dirent dirent; - -struct DIR { - struct dirent ent; - 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); - - -/* Internal utility functions */ -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_wcstombs_s( - size_t *pReturnValue, - char *mbstr, - size_t sizeInBytes, - const wchar_t *wcstr, - size_t count); - -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; - int error; - - /* Must have directory name */ - if (dirname == NULL || dirname[0] == '\0') { - dirent_set_errno (ENOENT); - return NULL; - } - - /* Allocate new _WDIR structure */ - dirp = (_WDIR*) malloc (sizeof (struct _WDIR)); - if (dirp != NULL) { - DWORD n; - - /* Reset _WDIR structure */ - dirp->handle = INVALID_HANDLE_VALUE; - dirp->patt = NULL; - 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 - - /* Allocate room for absolute directory name and search pattern */ - 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 - if (n > 0) { - wchar_t *p; - - /* Append search pattern \* to the directory name */ - p = dirp->patt + n; - if (dirp->patt < p) { - switch (p[-1]) { - case '\\': - case '/': - case ':': - /* Directory ends in path separator, e.g. c:\temp\ */ - /*NOP*/; - break; - - default: - /* Directory name doesn't end in path separator */ - *p++ = '\\'; - } - } - *p++ = '*'; - *p = '\0'; - - /* Open directory stream and retrieve the first entry */ - if (dirent_first (dirp)) { - /* Directory stream opened successfully */ - error = 0; - } else { - /* Cannot retrieve first entry */ - error = 1; - dirent_set_errno (ENOENT); - } - - } else { - /* Cannot retrieve full path name */ - dirent_set_errno (ENOENT); - error = 1; - } - - } else { - /* Cannot allocate memory for search pattern */ - error = 1; - } - - } else { - /* Cannot allocate _WDIR structure */ - error = 1; - } - - /* Clean up in case of error */ - if (error && dirp) { - _wclosedir (dirp); - dirp = NULL; - } - - return dirp; -} - -/* - * Read next directory entry. The directory entry is returned in dirent - * structure in the d_name field. Individual directory entries returned by - * 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; - - /* Read next directory entry */ - datap = dirent_next (dirp); - if (datap) { - size_t n; - DWORD attr; - - /* Pointer to directory entry to return */ - 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. - */ - n = 0; - while (n + 1 < PATH_MAX && datap->cFileName[n] != 0) { - entp->d_name[n] = datap->cFileName[n]; - n++; - } - dirp->ent.d_name[n] = 0; - - /* Length of file name excluding zero terminator */ - entp->d_namlen = n; - - /* File type */ - attr = datap->dwFileAttributes; - if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { - entp->d_type = DT_CHR; - } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { - entp->d_type = DT_DIR; - } else { - entp->d_type = DT_REG; - } - - /* Reset dummy fields */ - entp->d_ino = 0; - entp->d_reclen = sizeof (struct _wdirent); - - } else { - - /* Last directory entry read */ - entp = NULL; - - } - - return entp; -} - -/* - * Close directory stream opened by opendir() function. This invalidates the - * DIR structure as well as any directory entry read previously by - * _wreaddir(). - */ -static int -_wclosedir( - _WDIR *dirp) -{ - int ok; - if (dirp) { - - /* Release search handle */ - if (dirp->handle != INVALID_HANDLE_VALUE) { - FindClose (dirp->handle); - dirp->handle = INVALID_HANDLE_VALUE; - } - - /* Release search pattern */ - if (dirp->patt) { - free (dirp->patt); - dirp->patt = NULL; - } - - /* Release directory structure */ - free (dirp); - ok = /*success*/0; - - } else { - /* Invalid directory stream */ - dirent_set_errno (EBADF); - ok = /*failure*/-1; - } - return ok; -} - -/* - * Rewind directory stream such that _wreaddir() returns the very first - * file name again. - */ -static void -_wrewinddir( - _WDIR* dirp) -{ - if (dirp) { - /* Release existing search handle */ - if (dirp->handle != INVALID_HANDLE_VALUE) { - FindClose (dirp->handle); - } - - /* Open new search handle */ - dirent_first (dirp); - } -} - -/* Get first directory entry (internal) */ -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); - if (dirp->handle != INVALID_HANDLE_VALUE) { - - /* a directory entry is now waiting in memory */ - datap = &dirp->data; - dirp->cached = 1; - - } else { - - /* 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; - - /* Get next directory entry */ - if (dirp->cached != 0) { - - /* A valid directory entry already in memory */ - p = &dirp->data; - dirp->cached = 0; - - } else if (dirp->handle != INVALID_HANDLE_VALUE) { - - /* Get the next directory entry from stream */ - 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); - dirp->handle = INVALID_HANDLE_VALUE; - p = NULL; - } - - } else { - - /* End of directory stream reached */ - p = NULL; - - } - - return p; -} - -/* - * Open directory stream using plain old C-string. - */ -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); - return NULL; - } - - /* Allocate memory for DIR structure */ - 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); - if (!error) { - - /* Open directory stream using wide-character name */ - dirp->wdirp = _wopendir (wname); - if (dirp->wdirp) { - /* Directory stream opened */ - error = 0; - } else { - /* Failed to open directory stream */ - error = 1; - } - - } 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. - */ - error = 1; - } - - } else { - /* Cannot allocate DIR structure */ - error = 1; - } - - /* Clean up in case of error */ - if (error && dirp) { - free (dirp); - dirp = NULL; - } - - return dirp; -} - -/* - * Read next directory entry. - * - * When working with text consoles, please note that file names returned by - * readdir() are represented in the default ANSI code page while any output to - * console is typically formatted on another code page. Thus, non-ASCII - * characters in file names will not usually display correctly on console. The - * problem can be fixed in two ways: (1) change the character set of console - * to 1252 using chcp utility and use Lucida Console font, or (2) use - * _cprintf function when writing to console. The _cprinf() will re-encode - * 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; - - /* Read next directory entry */ - 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); - - /* - * 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) { - DWORD attr; - - /* Initialize directory entry for return */ - entp = &dirp->ent; - - /* Length of file name excluding zero terminator */ - entp->d_namlen = n - 1; - - /* File attributes */ - attr = datap->dwFileAttributes; - if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { - entp->d_type = DT_CHR; - } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { - entp->d_type = DT_DIR; - } else { - entp->d_type = DT_REG; - } - - /* Reset dummy fields */ - entp->d_ino = 0; - 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. - */ - entp = &dirp->ent; - entp->d_name[0] = '?'; - entp->d_name[1] = '\0'; - entp->d_namlen = 1; - entp->d_type = DT_UNKNOWN; - entp->d_ino = 0; - entp->d_reclen = 0; - } - - } else { - /* No more directory entries */ - entp = NULL; - } - - return entp; -} - -/* - * Close directory stream. - */ -static int -closedir( - DIR *dirp) -{ - int ok; - if (dirp) { - - /* Close wide-character directory stream */ - ok = _wclosedir (dirp->wdirp); - dirp->wdirp = NULL; - - /* Release multi-byte character version */ - free (dirp); - - } else { - - /* Invalid directory stream */ - dirent_set_errno (EBADF); - ok = /*failure*/-1; - - } - return ok; -} - -/* - * Rewind directory stream to beginning. - */ -static void -rewinddir( - DIR* dirp) -{ - /* Rewind wide-character string directory stream */ - _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) -{ - int error; - -#if defined(_MSC_VER) && _MSC_VER >= 1400 - - /* Microsoft Visual Studio 2005 or later */ - error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count); - + /* Multi-byte character versions */ + struct dirent + { + /* Always zero */ + long d_ino; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + char d_name[PATH_MAX]; + }; + typedef struct dirent dirent; + + struct DIR + { + struct dirent ent; + 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); + + /* Internal utility functions */ + 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_wcstombs_s(size_t* pReturnValue, char* mbstr, size_t sizeInBytes, const wchar_t* wcstr, + size_t count); + + 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; + int error; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') + { + dirent_set_errno(ENOENT); + return NULL; + } + + /* Allocate new _WDIR structure */ + dirp = (_WDIR*)malloc(sizeof(struct _WDIR)); + if (dirp != NULL) + { + DWORD n; + + /* Reset _WDIR structure */ + dirp->handle = INVALID_HANDLE_VALUE; + dirp->patt = NULL; + 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 + + /* Allocate room for absolute directory name and search pattern */ + 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 + if (n > 0) + { + wchar_t* p; + + /* Append search pattern \* to the directory name */ + p = dirp->patt + n; + if (dirp->patt < p) + { + switch (p[-1]) + { + case '\\': + case '/': + case ':': + /* Directory ends in path separator, e.g. c:\temp\ */ + /*NOP*/; + break; + + default: + /* Directory name doesn't end in path separator */ + *p++ = '\\'; + } + } + *p++ = '*'; + *p = '\0'; + + /* Open directory stream and retrieve the first entry */ + if (dirent_first(dirp)) + { + /* Directory stream opened successfully */ + error = 0; + } + else + { + /* Cannot retrieve first entry */ + error = 1; + dirent_set_errno(ENOENT); + } + } + else + { + /* Cannot retrieve full path name */ + dirent_set_errno(ENOENT); + error = 1; + } + } + else + { + /* Cannot allocate memory for search pattern */ + error = 1; + } + } + else + { + /* Cannot allocate _WDIR structure */ + error = 1; + } + + /* Clean up in case of error */ + if (error && dirp) + { + _wclosedir(dirp); + dirp = NULL; + } + + return dirp; + } + + /* + * Read next directory entry. The directory entry is returned in dirent + * structure in the d_name field. Individual directory entries returned by + * 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; + + /* Read next directory entry */ + datap = dirent_next(dirp); + if (datap) + { + size_t n; + DWORD attr; + + /* Pointer to directory entry to return */ + 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. + */ + n = 0; + while (n + 1 < PATH_MAX && datap->cFileName[n] != 0) + { + entp->d_name[n] = datap->cFileName[n]; + n++; + } + dirp->ent.d_name[n] = 0; + + /* Length of file name excluding zero terminator */ + entp->d_namlen = n; + + /* File type */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) + { + entp->d_type = DT_CHR; + } + else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) + { + entp->d_type = DT_DIR; + } + else + { + entp->d_type = DT_REG; + } + + /* Reset dummy fields */ + entp->d_ino = 0; + entp->d_reclen = sizeof(struct _wdirent); + } + else + { + + /* Last directory entry read */ + entp = NULL; + } + + return entp; + } + + /* + * Close directory stream opened by opendir() function. This invalidates the + * DIR structure as well as any directory entry read previously by + * _wreaddir(). + */ + static int _wclosedir(_WDIR* dirp) + { + int ok; + if (dirp) + { + + /* Release search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) + { + FindClose(dirp->handle); + dirp->handle = INVALID_HANDLE_VALUE; + } + + /* Release search pattern */ + if (dirp->patt) + { + free(dirp->patt); + dirp->patt = NULL; + } + + /* Release directory structure */ + free(dirp); + ok = /*success*/ 0; + } + else + { + /* Invalid directory stream */ + dirent_set_errno(EBADF); + ok = /*failure*/ -1; + } + return ok; + } + + /* + * Rewind directory stream such that _wreaddir() returns the very first + * file name again. + */ + static void _wrewinddir(_WDIR* dirp) + { + if (dirp) + { + /* Release existing search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) + { + FindClose(dirp->handle); + } + + /* Open new search handle */ + dirent_first(dirp); + } + } + + /* Get first directory entry (internal) */ + 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); + if (dirp->handle != INVALID_HANDLE_VALUE) + { + + /* a directory entry is now waiting in memory */ + datap = &dirp->data; + dirp->cached = 1; + } + else + { + + /* 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; + + /* Get next directory entry */ + if (dirp->cached != 0) + { + + /* A valid directory entry already in memory */ + p = &dirp->data; + dirp->cached = 0; + } + else if (dirp->handle != INVALID_HANDLE_VALUE) + { + + /* Get the next directory entry from stream */ + 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); + dirp->handle = INVALID_HANDLE_VALUE; + p = NULL; + } + } + else + { + + /* End of directory stream reached */ + p = NULL; + } + + return p; + } + + /* + * Open directory stream using plain old C-string. + */ + 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); + return NULL; + } + + /* Allocate memory for DIR structure */ + 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); + if (!error) + { + + /* Open directory stream using wide-character name */ + dirp->wdirp = _wopendir(wname); + if (dirp->wdirp) + { + /* Directory stream opened */ + error = 0; + } + else + { + /* Failed to open directory stream */ + error = 1; + } + } + 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. + */ + error = 1; + } + } + else + { + /* Cannot allocate DIR structure */ + error = 1; + } + + /* Clean up in case of error */ + if (error && dirp) + { + free(dirp); + dirp = NULL; + } + + return dirp; + } + + /* + * Read next directory entry. + * + * When working with text consoles, please note that file names returned by + * readdir() are represented in the default ANSI code page while any output to + * console is typically formatted on another code page. Thus, non-ASCII + * characters in file names will not usually display correctly on console. The + * problem can be fixed in two ways: (1) change the character set of console + * to 1252 using chcp utility and use Lucida Console font, or (2) use + * _cprintf function when writing to console. The _cprinf() will re-encode + * 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; + + /* Read next directory entry */ + 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); + + /* + * 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) + { + DWORD attr; + + /* Initialize directory entry for return */ + entp = &dirp->ent; + + /* Length of file name excluding zero terminator */ + entp->d_namlen = n - 1; + + /* File attributes */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) + { + entp->d_type = DT_CHR; + } + else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) + { + entp->d_type = DT_DIR; + } + else + { + entp->d_type = DT_REG; + } + + /* Reset dummy fields */ + entp->d_ino = 0; + 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. + */ + entp = &dirp->ent; + entp->d_name[0] = '?'; + entp->d_name[1] = '\0'; + entp->d_namlen = 1; + entp->d_type = DT_UNKNOWN; + entp->d_ino = 0; + entp->d_reclen = 0; + } + } + else + { + /* No more directory entries */ + entp = NULL; + } + + return entp; + } + + /* + * Close directory stream. + */ + static int closedir(DIR* dirp) + { + int ok; + if (dirp) + { + + /* Close wide-character directory stream */ + ok = _wclosedir(dirp->wdirp); + dirp->wdirp = NULL; + + /* Release multi-byte character version */ + free(dirp); + } + else + { + + /* Invalid directory stream */ + dirent_set_errno(EBADF); + ok = /*failure*/ -1; + } + return ok; + } + + /* + * Rewind directory stream to beginning. + */ + static void rewinddir(DIR* dirp) + { + /* Rewind wide-character string directory stream */ + _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) + { + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = mbstowcs_s(pReturnValue, wcstr, sizeInWords, mbstr, count); - /* Older Visual Studio or non-Microsoft compiler */ - size_t n; - - /* Convert to wide-character string (or count characters) */ - n = mbstowcs (wcstr, mbstr, sizeInWords); - if (!wcstr || n < count) { +#else - /* Zero-terminate output buffer */ - if (wcstr && sizeInWords) { - if (n >= sizeInWords) { - n = sizeInWords - 1; - } - wcstr[n] = 0; - } + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; - /* Length of resuting multi-byte string WITH zero terminator */ - if (pReturnValue) { - *pReturnValue = n + 1; - } + /* Convert to wide-character string (or count characters) */ + n = mbstowcs(wcstr, mbstr, sizeInWords); + if (!wcstr || n < count) + { - /* Success */ - error = 0; + /* Zero-terminate output buffer */ + if (wcstr && sizeInWords) + { + if (n >= sizeInWords) + { + n = sizeInWords - 1; + } + wcstr[n] = 0; + } - } else { + /* Length of resuting multi-byte string WITH zero terminator */ + if (pReturnValue) + { + *pReturnValue = n + 1; + } - /* Could not convert string */ - error = 1; + /* Success */ + error = 0; + } + else + { - } + /* Could not convert string */ + error = 1; + } #endif - return error; -} + return error; + } -/* 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) -{ - int error; + /* 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) + { + 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); + /* Microsoft Visual Studio 2005 or later */ + error = wcstombs_s(pReturnValue, mbstr, sizeInBytes, wcstr, count); #else - /* Older Visual Studio or non-Microsoft compiler */ - size_t n; + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; - /* Convert to multi-byte string (or count the number of bytes needed) */ - n = wcstombs (mbstr, wcstr, sizeInBytes); - if (!mbstr || n < count) { + /* Convert to multi-byte string (or count the number of bytes needed) */ + n = wcstombs(mbstr, wcstr, sizeInBytes); + if (!mbstr || n < count) + { - /* Zero-terminate output buffer */ - if (mbstr && sizeInBytes) { - if (n >= sizeInBytes) { - n = sizeInBytes - 1; - } - mbstr[n] = '\0'; - } + /* Zero-terminate output buffer */ + if (mbstr && sizeInBytes) + { + if (n >= sizeInBytes) + { + n = sizeInBytes - 1; + } + mbstr[n] = '\0'; + } - /* Length of resulting multi-bytes string WITH zero-terminator */ - if (pReturnValue) { - *pReturnValue = n + 1; - } + /* Length of resulting multi-bytes string WITH zero-terminator */ + if (pReturnValue) + { + *pReturnValue = n + 1; + } - /* Success */ - error = 0; + /* Success */ + error = 0; + } + else + { - } else { - - /* Cannot convert string */ - error = 1; - - } + /* Cannot convert string */ + error = 1; + } #endif - return error; -} + return error; + } -/* Set errno variable */ -static void -dirent_set_errno( - int error) -{ -#if defined(_MSC_VER) && _MSC_VER >= 1400 + /* Set errno variable */ + static void dirent_set_errno(int error) + { +#if defined(_MSC_VER) && _MSC_VER >= 1400 - /* Microsoft Visual Studio 2005 and later */ - _set_errno (error); + /* Microsoft Visual Studio 2005 and later */ + _set_errno(error); #else - /* Non-Microsoft compiler or older Microsoft compiler */ - errno = error; + /* Non-Microsoft compiler or older Microsoft compiler */ + errno = error; #endif -} - + } #ifdef __cplusplus } diff --git a/Examples/PcapSearch/main.cpp b/Examples/PcapSearch/main.cpp index 427ea27de8..397725837a 100644 --- a/Examples/PcapSearch/main.cpp +++ b/Examples/PcapSearch/main.cpp @@ -1,23 +1,30 @@ /** * 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 + * 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 + * ``` + * 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 * * For more details about modes of operation and parameters please run PcapSearch -h */ @@ -39,72 +46,75 @@ #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} +// clang-format off +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 } }; +// clang-format on - -#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 DIR_SEPARATOR "\\" +# define DIR_SEPARATOR "\\" #else -#define DIR_SEPARATOR "/" +# 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; + << "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; + 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 */ @@ -113,9 +123,9 @@ 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) { @@ -166,7 +176,7 @@ int searchPcap(const std::string& pcapFilePath, std::string searchCriteria, std: // print layer by layer by layer as we want to add a few spaces before each layer std::vector packetLayers; parsedPacket.toStringList(packetLayers); - for (const auto &layer : packetLayers) + for (const auto& layer : packetLayers) (*detailedReportFile) << "\n " << layer; (*detailedReportFile) << std::endl; } @@ -185,7 +195,6 @@ int searchPcap(const std::string& pcapFilePath, std::string searchCriteria, std: (*detailedReportFile) << "\n"; (*detailedReportFile) << " ----> Found " << packetCount << " packets" << std::endl << std::endl; - } // free the reader memory @@ -195,23 +204,23 @@ int searchPcap(const std::string& pcapFilePath, std::string searchCriteria, std: 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::unordered_map extensionsToSearch, - int& totalDirSearched, int& totalFilesSearched, int& totalPacketsFound) +void searchDirectories(const std::string& directory, bool includeSubDirectories, const std::string& searchCriteria, + std::ofstream* detailedReportFile, std::unordered_map extensionsToSearch, + int& totalDirSearched, int& totalFilesSearched, int& totalPacketsFound) { // open the directory - DIR *dir = opendir(directory.c_str()); + 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); + struct dirent* entry = readdir(dir); std::vector pcapList; @@ -223,7 +232,8 @@ void searchDirectories(const std::string &directory, bool includeSubDirectories, // 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 + if (0 != directory.compare(directory.length() - dirSep.length(), dirSep.length(), + dirSep)) // directory doesn't contain separator in the end dirPath += DIR_SEPARATOR; dirPath += name; @@ -239,8 +249,8 @@ void searchDirectories(const std::string &directory, bool includeSubDirectories, // 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) + // 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); @@ -254,10 +264,11 @@ void searchDirectories(const std::string &directory, bool includeSubDirectories, 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 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); + searchDirectories(dirPath, true, searchCriteria, detailedReportFile, extensionsToSearch, totalDirSearched, + totalFilesSearched, totalPacketsFound); // move to the next file entry = readdir(dir); @@ -268,9 +279,9 @@ void searchDirectories(const std::string &directory, bool includeSubDirectories, 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 (const auto &iter : pcapList) + // 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 (const auto& iter : pcapList) { // do the actual search int packetsFound = searchPcap(iter, searchCriteria, detailedReportFile); @@ -285,8 +296,6 @@ void searchDirectories(const std::string &directory, bool includeSubDirectories, } } - - /** * main method of this utility */ @@ -311,55 +320,55 @@ int main(int argc, char* argv[]) int optionIndex = 0; int opt = 0; - while((opt = getopt_long(argc, argv, "d:s:r:e:hvn", PcapSearchOptions, &optionIndex)) != -1) + 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': + 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, ',')) { - // 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; + // add the extension into the map if it doesn't already exist + if (extensionsToSearch.find(extension) == extensionsToSearch.end()) + extensionsToSearch[extension] = true; } - case 'h': - printUsage(); - exit(0); - case 'v': - printAppVersion(); - break; - default: - printUsage(); - exit(-1); + + // 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); } } @@ -373,7 +382,7 @@ int main(int argc, char* argv[]) EXIT_WITH_ERROR("Search criteria was not given"); } - DIR *dir = opendir(inputDirectory.c_str()); + DIR* dir = opendir(inputDirectory.c_str()); if (dir == nullptr) { EXIT_WITH_ERROR("Cannot find or open input directory"); @@ -382,7 +391,7 @@ int main(int argc, char* argv[]) // verify the search criteria is a valid BPF filter pcpp::BPFStringFilter filter(searchCriteria); - if(!filter.verifyFilter()) + if (!filter.verifyFilter()) { EXIT_WITH_ERROR("Search criteria isn't valid"); } @@ -399,22 +408,20 @@ int main(int argc, char* argv[]) } } - 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); + 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; + 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) { diff --git a/Examples/PcapSplitter/ConnectionSplitters.h b/Examples/PcapSplitter/ConnectionSplitters.h index e241cbabc6..2924584e73 100644 --- a/Examples/PcapSplitter/ConnectionSplitters.h +++ b/Examples/PcapSplitter/ConnectionSplitters.h @@ -2,7 +2,6 @@ #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. @@ -12,15 +11,16 @@ 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) {} + 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 + * 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) { @@ -33,7 +33,7 @@ class TwoTupleSplitter : public ValueBasedSplitter // 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 + 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 @@ -44,7 +44,6 @@ class TwoTupleSplitter : public ValueBasedSplitter } }; - /** * Splits a pcap file by connection (IP src + IP dst + port src + port dst + protocol) * Works for IPv4, IPv6, TCP and UDP. @@ -55,7 +54,6 @@ class TwoTupleSplitter : public ValueBasedSplitter 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::unordered_map m_TcpFlowTable; @@ -82,12 +80,12 @@ class FiveTupleSplitter : 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 FiveTupleSplitter(int maxFiles) : ValueBasedSplitter(maxFiles) {} + 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 @@ -110,7 +108,7 @@ class FiveTupleSplitter : public ValueBasedSplitter m_TcpFlowTable[hash] = isTcpSyn(packet); } } - else // flow is found in the flow table + else // flow is found in the flow table { if (packet.isPacketOfType(pcpp::TCP)) { @@ -146,21 +144,16 @@ class FiveTupleSplitter : public ValueBasedSplitter return m_FlowTable[hash]; } - void updateStringStream(std::ostringstream & sstream, const std::string & srcIp, uint16_t srcPort, const std::string & dstIp, uint16_t dstPort) + 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; + 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::string getFileName(pcpp::Packet& packet, const std::string& outputPcapBasePath, int fileNumber) { std::ostringstream sstream; @@ -186,12 +179,13 @@ class FiveTupleSplitter : public ValueBasedSplitter 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) ) + } + else if (((tcpLayer->getTcpHeader()->synFlag == 1) && (tcpLayer->getTcpHeader()->ackFlag == 1)) || + (srcPort < dstPort)) { updateStringStream(sstream, getDstIPString(packet), dstPort, getSrcIPString(packet), srcPort); - } else + } + else { updateStringStream(sstream, getSrcIPString(packet), srcPort, getDstIPString(packet), dstPort); } @@ -205,7 +199,8 @@ class FiveTupleSplitter : public ValueBasedSplitter if (udpLayer != nullptr) { sstream << "udp_"; - updateStringStream(sstream, getSrcIPString(packet), udpLayer->getSrcPort(), getDstIPString(packet), udpLayer->getDstPort()); + updateStringStream(sstream, getSrcIPString(packet), udpLayer->getSrcPort(), getDstIPString(packet), + udpLayer->getDstPort()); return outputPcapBasePath + sstream.str(); } } diff --git a/Examples/PcapSplitter/IPPortSplitters.h b/Examples/PcapSplitter/IPPortSplitters.h index 6a8d23d0cb..11d6b244a9 100644 --- a/Examples/PcapSplitter/IPPortSplitters.h +++ b/Examples/PcapSplitter/IPPortSplitters.h @@ -4,21 +4,20 @@ #include "PacketUtils.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) {} + IPPortSplitter(int maxFiles) : ValueBasedSplitter(maxFiles) + {} /** * Implements Splitter's abstract method. This method takes a packet and decides to which flow it belongs to (can @@ -60,20 +59,23 @@ class IPPortSplitter : public ValueBasedSplitter // SYN packet if (!tcpLayer->getTcpHeader()->ackFlag) { - m_FlowTable[hash] = getFileNumberForValue(getValue(packet, SYN, srcPort, dstPort), filesToClose); + 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); + 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); + m_FlowTable[hash] = + getFileNumberForValue(getValue(packet, TCP_OTHER, srcPort, dstPort), filesToClose); return m_FlowTable[hash]; } } @@ -97,11 +99,10 @@ class IPPortSplitter : public ValueBasedSplitter 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) + std::string getFileName(pcpp::Packet& packet, const std::string& outputPcapBasePath, int fileNumber) { // first set the base string as the outputPcapBasePath std::string result = outputPcapBasePath; @@ -159,7 +160,6 @@ class IPPortSplitter : public ValueBasedSplitter } protected: - /** * An enum for TCP/UDP packet type: can be either TCP-SYN, TCP-SYN/ACK, Other TCP packet of UDP packet */ @@ -185,7 +185,8 @@ class IPPortSplitter : public ValueBasedSplitter * 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; + 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 @@ -234,29 +235,26 @@ class IPPortSplitter : public ValueBasedSplitter } }; - - /** * 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 + * 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) {} + 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 @@ -270,9 +268,12 @@ class ClientIPSplitter : public IPPortSplitter 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); + 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) @@ -293,9 +294,13 @@ class ClientIPSplitter : public IPPortSplitter 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)); + 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) @@ -306,29 +311,26 @@ class ClientIPSplitter : public IPPortSplitter } }; - - /** * 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 + * 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) {} + 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 @@ -342,9 +344,12 @@ class ServerIPSplitter : public IPPortSplitter 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); + 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) @@ -365,9 +370,13 @@ class ServerIPSplitter : public IPPortSplitter 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)); + 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) @@ -376,32 +385,29 @@ class ServerIPSplitter : public IPPortSplitter 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 + * 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) {} + 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 @@ -415,9 +421,12 @@ class ServerPortSplitter : public IPPortSplitter case SYN_ACK: return srcPort; case UDP: - if(isSrcIPMulticast(packet)) return dstPort; - else if(isDstIPMulticast(packet)) return srcPort; - else return std::min(srcPort, dstPort); + 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); @@ -438,10 +447,13 @@ class ServerPortSplitter : public IPPortSplitter res = srcPort; break; case UDP: - if(isSrcIPMulticast(packet)) res = dstPort; - else if(isDstIPMulticast(packet)) res = srcPort; - else res = std::min(srcPort, dstPort); - break; + 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); @@ -455,26 +467,26 @@ class ServerPortSplitter : public IPPortSplitter }; /** - * 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) {} + 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 @@ -488,9 +500,12 @@ class ClientPortSplitter : public IPPortSplitter case SYN_ACK: return dstPort; case UDP: - if(isSrcIPMulticast(packet)) return srcPort; - else if(isDstIPMulticast(packet)) return dstPort; - else return std::max(srcPort, dstPort); + 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); @@ -511,9 +526,12 @@ class ClientPortSplitter : public IPPortSplitter res = dstPort; break; case UDP: - if(isSrcIPMulticast(packet)) res = srcPort; - else if(isDstIPMulticast(packet)) res = dstPort; - else res = std::max(srcPort, dstPort); + if (isSrcIPMulticast(packet)) + res = srcPort; + else if (isDstIPMulticast(packet)) + res = dstPort; + else + res = std::max(srcPort, dstPort); break; // other TCP packet default: diff --git a/Examples/PcapSplitter/SimpleSplitters.h b/Examples/PcapSplitter/SimpleSplitters.h index f69b67cd85..e05abcb575 100644 --- a/Examples/PcapSplitter/SimpleSplitters.h +++ b/Examples/PcapSplitter/SimpleSplitters.h @@ -14,7 +14,6 @@ class PacketCountSplitter : public Splitter int m_MaxPacketsPerFile; public: - /** * A c'tor for this class which gets the packet count for each split file */ @@ -57,8 +56,6 @@ class PacketCountSplitter : public Splitter } }; - - /** * Splits a pcap file by number of byte in each file */ @@ -68,11 +65,10 @@ class FileSizeSplitter : public Splitter 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) + 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 */ @@ -114,13 +110,11 @@ class FileSizeSplitter : public Splitter 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 { @@ -129,7 +123,8 @@ class BpfCriteriaSplitter : public Splitter pcpp::BPFStringFilter filter; public: - explicit BpfCriteriaSplitter(const std::string &bpfFilter) : m_BpfFilter(bpfFilter), filter(bpfFilter) {} + 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 @@ -145,7 +140,7 @@ class BpfCriteriaSplitter : public Splitter * 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) + std::string getFileName(pcpp::Packet& packet, const std::string& outputPcapBasePath, int fileNumber) { if (fileNumber == 0) return outputPcapBasePath + "match-bpf"; @@ -164,7 +159,6 @@ class BpfCriteriaSplitter : public Splitter return false; } - pcpp::BPFStringFilter localFilter(m_BpfFilter); bool filterValid = localFilter.verifyFilter(); if (!filterValid) @@ -174,14 +168,14 @@ class BpfCriteriaSplitter : public Splitter } }; - /** * 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) { } + explicit RoundRobinSplitter(int numOfFiles) : SplitterWithMaxFiles(numOfFiles) + {} /** * Get the next file number, SplitterWithMaxFiles#getNextFileNumber() takes care of the round-robin method diff --git a/Examples/PcapSplitter/Splitters.h b/Examples/PcapSplitter/Splitters.h index debaf6f2ac..c6c43c30a0 100644 --- a/Examples/PcapSplitter/Splitters.h +++ b/Examples/PcapSplitter/Splitters.h @@ -21,7 +21,6 @@ class Splitter { public: - /** * A method that gets a packet and returns: * - The file number to write the packet to @@ -42,20 +41,20 @@ class Splitter * 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) + virtual std::string getFileName(pcpp::Packet& packet, const std::string& outputPcapBasePath, int fileNumber) { - std::ostringstream sstream; - sstream << std::setw(4) << std::setfill( '0' ) << fileNumber; + std::ostringstream sstream; + sstream << std::setw(4) << std::setfill('0') << fileNumber; return outputPcapBasePath.c_str() + sstream.str(); } /** * A virtual d'tor */ - virtual ~Splitter() {} + 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 @@ -91,10 +90,10 @@ class SplitterWithMaxFiles : public Splitter /** * 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 + * 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) { @@ -103,13 +102,12 @@ class SplitterWithMaxFiles : public Splitter // 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 + 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) @@ -121,17 +119,17 @@ class SplitterWithMaxFiles : public Splitter } /** - * 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 + * 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) + 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; /** @@ -154,13 +152,12 @@ class SplitterWithMaxFiles : public Splitter } }; - /** * 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 + * 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 { @@ -173,7 +170,8 @@ class ValueBasedSplitter : public SplitterWithMaxFiles /** * A protected c'tor for this class that only propagate the maxFiles to its ancestor */ - explicit ValueBasedSplitter(int maxFiles) : SplitterWithMaxFiles(maxFiles, 1) {} + 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 diff --git a/Examples/PcapSplitter/main.cpp b/Examples/PcapSplitter/main.cpp index 858d7903ca..ae928113d4 100644 --- a/Examples/PcapSplitter/main.cpp +++ b/Examples/PcapSplitter/main.cpp @@ -23,26 +23,27 @@ * 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 + * 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 + * - 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 @@ -60,117 +61,128 @@ #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} +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' }, + { "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) - - -#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" - +#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 '\\' +# define SEPARATOR '\\' #else -#define SEPARATOR '/' +# 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; + 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; + 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' @@ -186,7 +198,7 @@ std::string getFileNameWithoutExtension(const std::string& path) if (i != std::string::npos) { // extract filename from path - std::string fileNameWithExtension = path.substr(i+1, path.length() - i); + 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()); @@ -207,10 +219,9 @@ std::string getFileNameWithoutExtension(const std::string& path) return path; } - return(""); + return (""); } - /** * main method of this utility */ @@ -233,37 +244,37 @@ int main(int argc, char* argv[]) int optionIndex = 0; int opt = 0; - while((opt = getopt_long(argc, argv, "f:o:m:p:i:vh", PcapSplitterOptions, &optionIndex)) != -1) + 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); + 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); } } @@ -342,7 +353,6 @@ int main(int argc, char* argv[]) else EXIT_WITH_ERROR("Unknown method '" << method << "'"); - // verify splitter param is legal, otherwise return an error std::string errorStr; if (!splitter->isSplitterParamLegal(errorStr)) @@ -352,7 +362,8 @@ int main(int argc, char* argv[]) } // 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) + "-"; + std::string outputPcapFileName = + outputPcapDir + std::string(1, SEPARATOR) + getFileNameWithoutExtension(inputPcapFileName) + "-"; // open a pcap file for reading pcpp::IFileReaderDevice* reader = pcpp::IFileReaderDevice::getReader(inputPcapFileName); @@ -397,7 +408,8 @@ int main(int argc, char* argv[]) 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; + std::string fileName = + splitter->getFileName(parsedPacket, outputPcapFileName, fileNum) + outputFileExtenison; // create a new IFileWriterDevice for this file if (isReaderPcapng) @@ -423,7 +435,8 @@ int main(int argc, char* argv[]) 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; + std::string fileName = + splitter->getFileName(parsedPacket, outputPcapFileName, fileNum) + outputFileExtenison; // re-create the IFileWriterDevice object if (isReaderPcapng) @@ -446,7 +459,7 @@ int main(int argc, char* argv[]) outputFiles[fileNum]->writePacket(*parsedPacket.getRawPacket()); // if splitter wants us to close files - go over the file numbers and close them - for (const auto &fileId : filesToClose) + for (const auto& fileId : filesToClose) { // check if that file number is in the map if (outputFiles.find(fileId) != outputFiles.end()) @@ -463,7 +476,8 @@ int main(int argc, char* argv[]) ++packetCountSoFar; } - std::cout << "Finished. Read and written " << packetCountSoFar << " packets to " << numOfFiles << " files" << std::endl; + std::cout << "Finished. Read and written " << packetCountSoFar << " packets to " << numOfFiles << " files" + << std::endl; // close the reader file reader->close(); @@ -473,7 +487,7 @@ int main(int argc, char* argv[]) delete splitter; // close the writer files which are still open - for(const auto &it : outputFiles) + for (const auto& it : outputFiles) { if (it.second != NULL) { diff --git a/Examples/PfRingExample-FilterTraffic/Common.h b/Examples/PfRingExample-FilterTraffic/Common.h index f54a7bc4d5..587c4916d7 100644 --- a/Examples/PfRingExample-FilterTraffic/Common.h +++ b/Examples/PfRingExample-FilterTraffic/Common.h @@ -12,20 +12,23 @@ #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(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); \ +#define EXIT_WITH_ERROR_AND_PRINT_USAGE(reason) \ + do \ + { \ + printUsage(); \ + std::cout << std::endl << "ERROR: " << reason << std::endl << std::endl; \ + exit(1); \ } while (0) /** @@ -49,7 +52,10 @@ struct PacketStats 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) {} + 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) { @@ -86,12 +92,26 @@ struct PacketStats 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; } + 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::string getStatValuesAsString(const std::string& delimiter) { std::stringstream values; - if (ThreadId == MAX_NUM_OF_CORES+1) + if (ThreadId == MAX_NUM_OF_CORES + 1) values << "Total" << delimiter; else values << (int)ThreadId << delimiter; @@ -110,14 +130,13 @@ struct PacketStats 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; + static const int narrowColumnWidth = 11; + static const int wideColumnWidth = 18; columnNames.push_back("Core ID"); columnNames.push_back("Packet Cnt"); @@ -144,6 +163,5 @@ struct PacketStats 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..1a74629640 100644 --- a/Examples/PfRingExample-FilterTraffic/PacketMatchingEngine.h +++ b/Examples/PfRingExample-FilterTraffic/PacketMatchingEngine.h @@ -7,8 +7,9 @@ #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 { @@ -19,11 +20,13 @@ class PacketMatchingEngine 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) + 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; diff --git a/Examples/PfRingExample-FilterTraffic/main.cpp b/Examples/PfRingExample-FilterTraffic/main.cpp index d9b2cdbc9f..81b58c65dc 100644 --- a/Examples/PfRingExample-FilterTraffic/main.cpp +++ b/Examples/PfRingExample-FilterTraffic/main.cpp @@ -2,29 +2,31 @@ * 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 - * of matched packets. + * 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 + * 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' */ @@ -44,24 +46,23 @@ #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} +// clang-format off +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 } }; - +// clang-format on /** * A struct that holds all arguments passed to capture threads: packetArrived() @@ -74,58 +75,67 @@ struct CaptureThreadArgs pcpp::PfRingDevice* sendPacketsTo; pcpp::PcapFileWriterDevice** pcapWriters; - CaptureThreadArgs() : packetStatArr(NULL), matchingEngine(NULL), flowTables(NULL), sendPacketsTo(NULL), pcapWriters(NULL) {} + 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 exit" << 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; + 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 exit" << 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; + 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 */ @@ -135,7 +145,7 @@ void listPfRingDevices() pcpp::Logger::getInstance().suppressLogs(); const std::vector& devList = pcpp::PfRingDeviceList::getInstance().getPfRingDevicesList(); - for (const auto &dev : devList) + for (const auto& dev : devList) { std::ostringstream interfaceIndex; if (dev->getInterfaceIndex() <= 9999) @@ -147,26 +157,24 @@ void listPfRingDevices() interfaceIndex << "N/A"; } - std::cout - << " -> Name: " << std::left << std::setw(8) << dev->getDeviceName() - << " Index: " << std::setw(5) << interfaceIndex.str() - << " MAC address: " << std::setw(19) << (dev->getMacAddress() == pcpp::MacAddress::Zero ? "N/A" : dev->getMacAddress().toString()) - << " Available RX channels: " << std::setw(3) << (int)dev->getTotalNumOfRxChannels() - << " MTU: " << dev->getMtu() - << std::endl; + std::cout << " -> Name: " << std::left << std::setw(8) << dev->getDeviceName() << " Index: " << std::setw(5) + << interfaceIndex.str() << " MAC address: " << std::setw(19) + << (dev->getMacAddress() == pcpp::MacAddress::Zero ? "N/A" : dev->getMacAddress().toString()) + << " Available RX channels: " << std::setw(3) << (int)dev->getTotalNumOfRxChannels() + << " MTU: " << dev->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) +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++) @@ -179,16 +187,17 @@ void packetArrived(pcpp::RawPacket* packets, uint32_t numOfPackets, uint8_t thre 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 + // 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::unordered_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) + if (iter != args->flowTables[threadId].end() && iter->second) { packetMatched = true; } - else // packet belongs to a new flow + else // packet belongs to a new flow { packetMatched = args->matchingEngine->isMatched(packet); if (packetMatched) @@ -196,7 +205,7 @@ void packetArrived(pcpp::RawPacket* packets, uint32_t numOfPackets, uint8_t thre // put new flow in flow table args->flowTables[threadId][hash] = true; - //collect stats + // collect stats if (packet.isPacketOfType(pcpp::TCP)) { args->packetStatArr[threadId].MatchedTcpFlows++; @@ -205,7 +214,6 @@ void packetArrived(pcpp::RawPacket* packets, uint32_t numOfPackets, uint8_t thre { args->packetStatArr[threadId].MatchedUdpFlows++; } - } } @@ -228,7 +236,6 @@ void packetArrived(pcpp::RawPacket* packets, uint32_t numOfPackets, uint8_t thre } } - /** * The callback to be called when application is terminated by ctrl-c. Do cleanup and print summary stats */ @@ -239,7 +246,6 @@ void onApplicationInterrupted(void* cookie) *shouldStop = true; } - int main(int argc, char* argv[]) { pcpp::AppName::init(argc, argv); @@ -247,154 +253,156 @@ int main(int argc, char* argv[]) pcpp::PfRingDevice* dev = NULL; int totalNumOfCores = pcpp::getNumOfCores(); - int numOfCaptureThreads = totalNumOfCores-1; + 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; + 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) + 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 << "'"); + 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 += "/"; + 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': - { - try - { - srcIPToMatch = pcpp::IPv4Address(optarg); - } - catch(const std::exception&) - { - EXIT_WITH_ERROR_AND_PRINT_USAGE("Source IP to match isn't a valid IP address"); - } - break; - } - case 'I': - { - try - { - dstIPToMatch = pcpp::IPv4Address(optarg); - } - catch(const std::exception&) - { - EXIT_WITH_ERROR_AND_PRINT_USAGE("Destination IP to match isn't a valid IP address"); - } - break; - } - case 'p': + writePacketsToDisk = true; + break; + } + case 'i': + { + try { - 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; + srcIPToMatch = pcpp::IPv4Address(optarg); } - case 'P': + catch (const std::exception&) { - 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; + EXIT_WITH_ERROR_AND_PRINT_USAGE("Source IP to match isn't a valid IP address"); } - case 'r': + break; + } + case 'I': + { + try { - 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; + dstIPToMatch = pcpp::IPv4Address(optarg); } - case 'h': + catch (const std::exception&) { - printUsage(); - exit(0); + EXIT_WITH_ERROR_AND_PRINT_USAGE("Destination IP to match isn't a valid IP address"); } - case 'v': + break; + } + case 'p': + { + int ret = atoi(optarg); + if (ret <= 0 || ret > 65535) { - printAppVersion(); - break; + EXIT_WITH_ERROR_AND_PRINT_USAGE("Source port to match isn't a valid TCP/UDP port"); } - case 'l': + srcPortToMatch = ret; + break; + } + case 'P': + { + int ret = atoi(optarg); + if (ret <= 0 || ret > 65535) { - listPfRingDevices(); - exit(0); + EXIT_WITH_ERROR_AND_PRINT_USAGE("Destination port to match isn't a valid TCP/UDP port"); } - default: + 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 { - printUsage(); - exit(0); + 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) + // 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() << "'"); + 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"); + EXIT_WITH_ERROR("Couldn't open PF_RING device '" << sendPacketsToIface->getDeviceName() + << "' for sending matched packets"); pcpp::CoreMask coreMask = 0; int threadId = 0; @@ -405,7 +413,7 @@ int main(int argc, char* argv[]) // 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; + 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 @@ -453,8 +461,8 @@ int main(int argc, char* argv[]) } } - - std::cout << "Start capturing on " << numOfCaptureThreads << " threads core mask = 0x" << std::hex << coreMask << std::dec << std::endl; + std::cout << "Start capturing on " << numOfCaptureThreads << " threads core mask = 0x" << std::hex << coreMask + << std::dec << std::endl; // prepare packet capture configuration CaptureThreadArgs args; @@ -467,7 +475,8 @@ int main(int argc, char* argv[]) // 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() << "'"); + EXIT_WITH_ERROR("Couldn't start capturing on core mask 0x" << std::hex << coreMask << " on interface '" + << dev->getDeviceName() << "'"); } bool shouldStop = false; @@ -498,9 +507,7 @@ int main(int argc, char* argv[]) } } - std::cout << std::endl << std::endl - << "Application stopped" - << std::endl; + 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; @@ -513,7 +520,7 @@ int main(int argc, char* argv[]) for (int i = 0; i < totalNumOfCores; i++) { - if (packetStatsArr[i].ThreadId == MAX_NUM_OF_CORES+1) + if (packetStatsArr[i].ThreadId == MAX_NUM_OF_CORES + 1) continue; aggregatedStats.collectStats(packetStatsArr[i]); diff --git a/Examples/SSLAnalyzer/SSLStatsCollector.h b/Examples/SSLAnalyzer/SSLStatsCollector.h index ebb036c73d..6cf55a0a87 100644 --- a/Examples/SSLAnalyzer/SSLStatsCollector.h +++ b/Examples/SSLAnalyzer/SSLStatsCollector.h @@ -9,14 +9,15 @@ #include "SSLLayer.h" #include "SystemUtils.h" - /** * An auxiliary struct for encapsulating rate stats */ struct Rate { - double currentRate; // periodic rate - double totalRate; // overall rate + // periodic rate + double currentRate; + // overall rate + double totalRate; }; /** @@ -24,19 +25,32 @@ struct Rate */ 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::unordered_map sslVersionCount; // number of flows per SSL/TLS version - std::unordered_map sslPortCount; // number of flows per TCP port + // total number of SSL flows + int numOfSSLFlows; + // rate of SSL flows + Rate sslFlowRate; + // total number of SSL packets + int numOfSSLPackets; + // rate of SSL packets + Rate sslPacketRate; + // average number of SSL packets per flow + double averageNumOfPacketsPerFlow; + // total SSL traffic in bytes + int amountOfSSLTraffic; + // average number of SSL traffic per flow + double averageAmountOfDataPerFlow; + // rate of SSL traffic + Rate sslTrafficRate; + // total stats collection time + double sampleTime; + // number of flows which handshake was complete + int numOfHandshakeCompleteFlows; + // number of flows that were terminated because of SSL/TLS alert + int numOfFlowsWithAlerts; + // number of flows per SSL/TLS version + std::unordered_map sslVersionCount; + // number of flows per TCP port + std::unordered_map sslPortCount; void clear() { @@ -59,17 +73,20 @@ struct SSLGeneralStats } }; - /** * 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::unordered_map serverNameCount; // a map for counting the server names seen in traffic + // total number of client-hello messages + int numOfMessages; + // rate of client-hello messages + Rate messageRate; + // a map for counting the server names seen in traffic + std::unordered_map serverNameCount; - virtual ~ClientHelloStats() {} + virtual ~ClientHelloStats() + {} virtual void clear() { @@ -85,11 +102,15 @@ struct ClientHelloStats */ struct ServerHelloStats { - int numOfMessages; // total number of server-hello messages - Rate messageRate; // rate of server-hello messages - std::unordered_map cipherSuiteCount; // count of the different chosen cipher-suites + // total number of server-hello messages + int numOfMessages; + // rate of server-hello messages + Rate messageRate; + // count of the different chosen cipher-suites + std::unordered_map cipherSuiteCount; - virtual ~ServerHelloStats() {} + virtual ~ServerHelloStats() + {} virtual void clear() { @@ -100,14 +121,12 @@ struct ServerHelloStats } }; - /** * 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 */ @@ -149,22 +168,27 @@ class SSLStatsCollector // 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 + // 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; + 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 + // 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; @@ -201,27 +225,37 @@ class SSLStatsCollector /** * Get SSL general stats */ - SSLGeneralStats& getGeneralStats() { return m_GeneralStats; } + SSLGeneralStats& getGeneralStats() + { + return m_GeneralStats; + } /** * Get client-hello stats */ - ClientHelloStats& getClientHelloStats() { return m_ClientHelloStats; } + ClientHelloStats& getClientHelloStats() + { + return m_ClientHelloStats; + } /** * Get server-hello stats */ - ServerHelloStats& getServerHelloStats() { return m_ServerHelloStats; } + 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 + // was SSL application data seen in this flow + bool seenAppDataPacket; + // was SSL alert packet seen in this flow + bool seenAlertPacket; void clear() { @@ -230,7 +264,6 @@ class SSLStatsCollector } }; - /** * Collect stats relevant for every SSL packet (any SSL message) * This method calculates and returns the flow key for this packet @@ -268,8 +301,10 @@ class SSLStatsCollector // 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(); + m_GeneralStats.averageAmountOfDataPerFlow = + (double)m_GeneralStats.amountOfSSLTraffic / (double)m_FlowTable.size(); + m_GeneralStats.averageNumOfPacketsPerFlow = + (double)m_GeneralStats.numOfSSLPackets / (double)m_FlowTable.size(); } return hashVal; @@ -315,7 +350,8 @@ class SSLStatsCollector continue; // try to find client-hello message - pcpp::SSLClientHelloMessage* clientHelloMessage = handshakeLayer->getHandshakeMessageOfType(); + pcpp::SSLClientHelloMessage* clientHelloMessage = + handshakeLayer->getHandshakeMessageOfType(); // collect client-hello stats if (clientHelloMessage != NULL) @@ -324,7 +360,8 @@ class SSLStatsCollector } // try to find server-hello message - pcpp::SSLServerHelloMessage* serverHelloMessage = handshakeLayer->getHandshakeMessageOfType(); + pcpp::SSLServerHelloMessage* serverHelloMessage = + handshakeLayer->getHandshakeMessageOfType(); // collect server-hello stats if (serverHelloMessage != NULL) @@ -345,7 +382,8 @@ class SSLStatsCollector { m_ClientHelloStats.numOfMessages++; - pcpp::SSLServerNameIndicationExtension* sniExt = clientHelloMessage->getExtensionOfType(); + pcpp::SSLServerNameIndicationExtension* sniExt = + clientHelloMessage->getExtensionOfType(); if (sniExt != NULL) m_ClientHelloStats.serverNameCount[sniExt->getHostName()]++; } @@ -364,11 +402,11 @@ class SSLStatsCollector double getCurTime(void) { - struct timeval tv; + struct timeval tv; - gettimeofday(&tv, NULL); + gettimeofday(&tv, NULL); - return (((double) tv.tv_sec) + (double) (tv.tv_usec / 1000000.0)); + return (((double)tv.tv_sec) + (double)(tv.tv_usec / 1000000.0)); } SSLGeneralStats m_GeneralStats; diff --git a/Examples/SSLAnalyzer/main.cpp b/Examples/SSLAnalyzer/main.cpp index 0bba4d8155..bbc6941cbe 100644 --- a/Examples/SSLAnalyzer/main.cpp +++ b/Examples/SSLAnalyzer/main.cpp @@ -1,9 +1,9 @@ /** * 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, @@ -31,37 +31,34 @@ #include "PcapPlusPlusVersion.h" #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) - - -#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 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} +// clang-format off +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 } }; - +// clang-format on struct SSLPacketArrivedData { @@ -69,70 +66,73 @@ struct SSLPacketArrivedData 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; + << "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; + 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(); + const std::vector& devList = + pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDevicesList(); std::cout << std::endl << "Network interfaces:" << std::endl; - for (const auto &dev : devList) + for (const auto& dev : devList) { - std::cout << " -> Name: '" << dev->getName() << "' IP address: " << dev->getIPv4Address().toString() << std::endl; + std::cout << " -> Name: '" << dev->getName() << "' IP address: " << dev->getIPv4Address().toString() + << std::endl; } exit(0); } - -void printStatsHeadline(const std::string &description) +void printStatsHeadline(const std::string& description) { std::string underline; for (size_t i = 0; i < description.length(); i++) @@ -143,7 +143,6 @@ void printStatsHeadline(const std::string &description) std::cout << std::endl << description << std::endl << underline << std::endl << std::endl; } - /** * packet capture callback - called whenever a packet arrives */ @@ -152,7 +151,7 @@ void sslPacketArrive(pcpp::RawPacket* packet, pcpp::PcapLiveDevice* dev, void* c // 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); @@ -164,7 +163,6 @@ void sslPacketArrive(pcpp::RawPacket* packet, pcpp::PcapLiveDevice* dev, void* c } } - /** * An auxiliary method for sorting the string count map. Used in printServerNames() and in printCipherSuites() */ @@ -177,7 +175,6 @@ bool stringCountComparer(const std::pair& first, const std::pa return first.second > second.second; } - /** * An auxiliary method for sorting the uint16_t count map. Used in printPorts() */ @@ -190,7 +187,6 @@ bool uint16CountComparer(std::pair first, std::pair second.second; } - /** * Print the server-name count map to a table sorted by popularity (most popular names will be first) */ @@ -207,11 +203,12 @@ void printServerNames(ClientHelloStats& clientHelloStatsCollector) // sort the server-name count map so the most popular names will be first // since it's not possible to sort a std::unordered_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); + 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(const auto &iter : map2vec) + for (const auto& iter : map2vec) { std::stringstream values; values << iter.first << "|" << iter.second; @@ -219,7 +216,6 @@ void printServerNames(ClientHelloStats& clientHelloStatsCollector) } } - /** * Print SSL record version map */ @@ -236,11 +232,11 @@ void printVersions(std::unordered_map& versionMap, const std::str // sort the version map so the most popular version will be first // since it's not possible to sort a std::unordered_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); + 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(const auto &iter : map2vec) + for (const auto& iter : map2vec) { std::stringstream values; values << pcpp::SSLVersion(iter.first).toString() << "|" << iter.second; @@ -248,7 +244,6 @@ void printVersions(std::unordered_map& versionMap, const std::str } } - /** * Print used cipher-suite map to a table sorted by popularity (most popular cipher-suite will be first) */ @@ -265,11 +260,12 @@ void printCipherSuites(ServerHelloStats& serverHelloStats) // sort the cipher-suite count map so the most popular names will be first // since it's not possible to sort a std::unordered_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); + 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(const auto &iter : map2vec) + for (const auto& iter : map2vec) { std::stringstream values; values << iter.first << "|" << iter.second; @@ -277,7 +273,6 @@ void printCipherSuites(ServerHelloStats& serverHelloStats) } } - void printPorts(SSLGeneralStats& stats) { // create the table @@ -291,11 +286,11 @@ void printPorts(SSLGeneralStats& stats) // sort the port count map so the most popular names will be first // since it's not possible to sort a std::unordered_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); + 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(const auto &iter : map2vec) + for (const auto& iter : map2vec) { std::stringstream values; values << iter.first << "|" << iter.second; @@ -303,9 +298,9 @@ void printPorts(SSLGeneralStats& stats) } } - /** - * 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) { @@ -321,7 +316,8 @@ void printStatsSummary(SSLStatsCollector& collector) 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 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"); @@ -335,10 +331,8 @@ void printStatsSummary(SSLStatsCollector& collector) printStatsHeadline("Server-name count"); printServerNames(collector.getClientHelloStats()); - } - /** * Print the current rates. Should be called periodically during traffic capture */ @@ -352,7 +346,6 @@ void printCurrentRates(SSLStatsCollector& collector) 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 */ @@ -362,7 +355,6 @@ void onApplicationInterrupted(void* cookie) *shouldStop = true; } - /** * activate SSL/TLS analysis from pcap file */ @@ -377,16 +369,14 @@ void analyzeSSLFromPcapFile(const std::string& pcapFileName) // 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)) + 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; + std::cout << std::endl << std::endl << "STATS SUMMARY" << std::endl << "=============" << std::endl; printStatsSummary(collector); // close input file @@ -396,11 +386,11 @@ void analyzeSSLFromPcapFile(const std::string& pcapFileName) delete reader; } - /** * activate SSL analysis from live traffic */ -void analyzeSSLFromLiveTraffic(pcpp::PcapLiveDevice* dev, bool printRatesPeriodically, int printRatePeriod, const std::string& savePacketsToFileName) +void analyzeSSLFromLiveTraffic(pcpp::PcapLiveDevice* dev, bool printRatesPeriodically, int printRatePeriod, + const std::string& savePacketsToFileName) { // open the device if (!dev->open()) @@ -410,7 +400,8 @@ void analyzeSSLFromLiveTraffic(pcpp::PcapLiveDevice* dev, bool printRatesPeriodi 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. + // 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)); @@ -426,7 +417,6 @@ void analyzeSSLFromLiveTraffic(pcpp::PcapLiveDevice* dev, bool printRatesPeriodi 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 != "") @@ -445,12 +435,11 @@ void analyzeSSLFromLiveTraffic(pcpp::PcapLiveDevice* dev, bool printRatesPeriodi 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) + while (!shouldStop) { pcpp::multiPlatformSleep(printRatePeriod); @@ -470,9 +459,7 @@ void analyzeSSLFromLiveTraffic(pcpp::PcapLiveDevice* dev, bool printRatesPeriodi collector.calcRates(); // print stats summary - std::cout << std::endl << std::endl - << "STATS SUMMARY" << std::endl - << "=============" << std::endl; + std::cout << std::endl << std::endl << "STATS SUMMARY" << std::endl << "=============" << std::endl; printStatsSummary(collector); // close and free the writer device @@ -497,44 +484,43 @@ int main(int argc, char* argv[]) std::string readPacketsFromPcapFileName = ""; - int optionIndex = 0; int opt = 0; - while((opt = getopt_long(argc, argv, "i:f:o:r:hvld", SSLAnalyzerOptions, &optionIndex)) != -1) + 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); + 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); } } @@ -547,10 +533,11 @@ int main(int argc, char* argv[]) { analyzeSSLFromPcapFile(readPacketsFromPcapFileName); } - else // analyze in live traffic mode + else // analyze in live traffic mode { // extract pcap live device by interface name or IP address - pcpp::PcapLiveDevice* dev = pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDeviceByIpOrName(interfaceNameOrIP); + pcpp::PcapLiveDevice* dev = + pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDeviceByIpOrName(interfaceNameOrIP); if (dev == nullptr) EXIT_WITH_ERROR("Couldn't find interface by provided IP address or name"); diff --git a/Examples/TLSFingerprinting/main.cpp b/Examples/TLSFingerprinting/main.cpp index 683f480a2d..87acfa5af0 100644 --- a/Examples/TLSFingerprinting/main.cpp +++ b/Examples/TLSFingerprinting/main.cpp @@ -26,36 +26,36 @@ #include "PcapFileDevice.h" #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} +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' }, + { "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) +#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" +#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() */ @@ -68,74 +68,91 @@ bool stringCountComparer(const std::pair& first, const st 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; + 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; + 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(); + const std::vector& devList = + pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDevicesList(); std::cout << std::endl << "Network interfaces:" << std::endl; - for (const auto &dev : devList) + for (const auto& dev : devList) { - std::cout << " -> Name: '" << dev->getName() << "' IP address: " << dev->getIPv4Address().toString() << std::endl; + std::cout << " -> Name: '" << dev->getName() << "' IP address: " << dev->getIPv4Address().toString() + << std::endl; } exit(0); } - /** * The callback to be called when application is terminated by ctrl-c */ @@ -145,7 +162,6 @@ static void onApplicationInterrupted(void* cookie) *shouldStop = true; } - /** * Return a packet source and dest IP addresses */ @@ -161,7 +177,6 @@ std::pair getIPs(const pcpp::Packet& packet) return std::pair(srcIP, dstIP); } - /** * Return a packet source and dest TCP ports */ @@ -178,47 +193,36 @@ std::pair getTcpPorts(const pcpp::Packet& packet) 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) +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; + *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) +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; + 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) { } + TLSFingerprintingStats() : numOfPacketsTotal(0), numOfCHPackets(0), numOfSHPackets(0) + {} uint64_t numOfPacketsTotal; uint64_t numOfCHPackets; uint64_t numOfSHPackets; @@ -235,7 +239,6 @@ struct HandlePacketData TLSFingerprintingStats* stats; }; - /** * Print cipher-suite map in a table sorted by number of occurrences (most common cipher-suite will be first) */ @@ -252,11 +255,11 @@ void printCommonTLSFingerprints(const std::unordered_map& // sort the TLS fingerprint map so the most popular will be first // since it's not possible to sort a std::unordered_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); + 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(auto iter = map2vec.begin(); iter != map2vec.end(); ++iter) + for (auto iter = map2vec.begin(); iter != map2vec.end(); ++iter) { if (iter - map2vec.begin() >= printCountItems) break; @@ -267,7 +270,6 @@ void printCommonTLSFingerprints(const std::unordered_map& } } - /** * Print TLS fingerprinting stats */ @@ -318,9 +320,9 @@ void printStats(const TLSFingerprintingStats& stats, bool chFP, bool shFP) } } - /** - * 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) { @@ -336,17 +338,20 @@ void handlePacket(pcpp::RawPacket* rawPacket, const HandlePacketData* data) if (data->chFP) { // check if the SSL/TLS handhsake layer contains a ClientHello message - pcpp::SSLClientHelloMessage* clientHelloMessage = sslHandshakeLayer->getHandshakeMessageOfType(); + pcpp::SSLClientHelloMessage* clientHelloMessage = + sslHandshakeLayer->getHandshakeMessageOfType(); if (clientHelloMessage != nullptr) { data->stats->numOfCHPackets++; // extract the TLS fingerprint - pcpp::SSLClientHelloMessage::ClientHelloTLSFingerprint tlsFingerprint = clientHelloMessage->generateTLSFingerprint(); + 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); + writeToOutputFile(data->outputFile, parsedPacket, tlsFingerprintStringAndMD5.first, + tlsFingerprintStringAndMD5.second, "ClientHello", data->separator); return; } } @@ -354,28 +359,31 @@ void handlePacket(pcpp::RawPacket* rawPacket, const HandlePacketData* data) if (data->shFP) { // check if the SSL/TLS handhsake layer contains a ServerHello message - pcpp::SSLServerHelloMessage* servertHelloMessage = sslHandshakeLayer->getHandshakeMessageOfType(); + pcpp::SSLServerHelloMessage* servertHelloMessage = + sslHandshakeLayer->getHandshakeMessageOfType(); if (servertHelloMessage != nullptr) { data->stats->numOfSHPackets++; // extract the TLS fingerprint - pcpp::SSLServerHelloMessage::ServerHelloTLSFingerprint tlsFingerprint = servertHelloMessage->generateTLSFingerprint(); + 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); + 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) +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()); @@ -389,7 +397,8 @@ void doTlsFingerprintingOnPcapFile(const std::string& inputPcapFileName, std::st { size_t fileNameOffset = inputPcapFileName.find_last_of("\\/") + 1; size_t extensionOffset = inputPcapFileName.find_last_of("."); - std::string fileNameWithoutExtension = inputPcapFileName.substr(fileNameOffset, extensionOffset - fileNameOffset); + std::string fileNameWithoutExtension = + inputPcapFileName.substr(fileNameOffset, extensionOffset - fileNameOffset); outputFileName = fileNameWithoutExtension + ".txt"; } @@ -412,7 +421,6 @@ void doTlsFingerprintingOnPcapFile(const std::string& inputPcapFileName, std::st std::cout << "Start reading '" << inputPcapFileName << "'..." << std::endl; - TLSFingerprintingStats stats; HandlePacketData data; data.chFP = chFP; @@ -438,7 +446,6 @@ void doTlsFingerprintingOnPcapFile(const std::string& inputPcapFileName, std::st 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) */ @@ -448,11 +455,11 @@ static void onPacketArrives(pcpp::RawPacket* rawPacket, pcpp::PcapLiveDevice* de 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) +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); @@ -467,10 +474,8 @@ void doTlsFingerprintingOnLiveTraffic(const std::string& interfaceNameOrIP, std: { // 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.erase(remove_if(outputFileName.begin(), outputFileName.end(), isNotAlphanumeric), + outputFileName.end()); outputFileName += ".txt"; } @@ -510,7 +515,7 @@ void doTlsFingerprintingOnLiveTraffic(const std::string& interfaceNameOrIP, std: pcpp::ApplicationEventHandler::getInstance().onApplicationInterrupted(onApplicationInterrupted, &shouldStop); // run in an endless loop until the user press ctrl+c - while(!shouldStop) + while (!shouldStop) pcpp::multiPlatformSleep(1); // stop capturing and close the live device @@ -539,64 +544,71 @@ int main(int argc, char* argv[]) int optionIndex = 0; int opt = 0; - while((opt = getopt_long(argc, argv, "i:r:o:t:f:s:vhl", TLSFingerprintingOptions, &optionIndex)) != -1) + 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); + 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 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: '.', ',', ':', '-' + // 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()) + 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 ',', '.', ':', '-'"); + 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) + 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"); + 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; diff --git a/Examples/TcpReassembly/main.cpp b/Examples/TcpReassembly/main.cpp index d8c6e688b2..8a19f86e60 100644 --- a/Examples/TcpReassembly/main.cpp +++ b/Examples/TcpReassembly/main.cpp @@ -1,27 +1,27 @@ /** * 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 @@ -36,59 +36,59 @@ #include "LRUList.h" #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 '\\' +# define SEPARATOR '\\' #else -#define SEPARATOR '/' +# define SEPARATOR '/' #endif - // 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 + 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; @@ -98,13 +98,13 @@ class GlobalConfig // 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 + // 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) @@ -113,7 +113,8 @@ class GlobalConfig { 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 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; @@ -127,19 +128,18 @@ class GlobalConfig // 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 + 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 + * 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 + 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) @@ -152,7 +152,6 @@ class GlobalConfig return new std::ofstream(fileName.c_str(), std::ios_base::binary); } - /** * Close a file stream */ @@ -170,15 +169,14 @@ class GlobalConfig } } - /** * 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 + // 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); @@ -186,7 +184,6 @@ class GlobalConfig return m_RecentConnsWithActivity; } - /** * The singleton implementation of this class */ @@ -205,16 +202,18 @@ class GlobalConfig } }; - /** - * 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) + // 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) + // 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 @@ -228,7 +227,12 @@ struct TcpReassemblyData /** * the default c'tor */ - TcpReassemblyData() { fileStreams[0] = nullptr; fileStreams[1] = nullptr; clear(); } + TcpReassemblyData() + { + fileStreams[0] = nullptr; + fileStreams[1] = nullptr; + clear(); + } /** * The default d'tor @@ -273,51 +277,54 @@ struct TcpReassemblyData } }; - // 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; + << "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; + 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 */ @@ -329,12 +336,12 @@ void listInterfaces() for (auto dev : devList) { - std::cout << " -> Name: '" << dev->getName() << "' IP address: " << dev->getIPv4Address().toString() << std::endl; + 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 */ @@ -353,7 +360,8 @@ static void tcpReassemblyMsgReadyCallback(const int8_t sideIndex, const pcpp::Tc 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 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 @@ -362,13 +370,15 @@ static void tcpReassemblyMsgReadyCallback(const int8_t sideIndex, const pcpp::Tc // 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 + // 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); + 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 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 @@ -384,18 +394,24 @@ static void tcpReassemblyMsgReadyCallback(const int8_t sideIndex, const pcpp::Tc 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) + // 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; } } } } + // clang-format off // 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]); + std::string fileName = GlobalConfig::getInstance().getFileName(tcpData.getConnectionData(), sideIndex, GlobalConfig::getInstance().separateSides) + + ".txt"; + // clang-format on + + // 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 @@ -416,9 +432,9 @@ static void tcpReassemblyMsgReadyCallback(const int8_t sideIndex, const pcpp::Tc 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) { @@ -436,12 +452,12 @@ static void tcpReassemblyConnectionStartCallback(const pcpp::ConnectionData& con } } - /** - * 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) +static void tcpReassemblyConnectionEndCallback(const pcpp::ConnectionData& connectionData, + pcpp::TcpReassembly::ConnectionEndReason reason, void* userCookie) { // get a pointer to the connection manager auto connMgr = (TcpReassemblyConnMgr*)userCookie; @@ -460,14 +476,18 @@ static void tcpReassemblyConnectionEndCallback(const pcpp::ConnectionData& conne 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 << "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 << "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 << "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(); } @@ -475,7 +495,6 @@ static void tcpReassemblyConnectionEndCallback(const pcpp::ConnectionData& conne connMgr->erase(connection); } - /** * The callback to be called when application is terminated by ctrl-c. Stops the endless while loop */ @@ -485,7 +504,6 @@ static void onApplicationInterrupted(void* cookie) *shouldStop = true; } - /** * packet capture callback - called whenever a packet arrives on the live device (in live device capturing mode) */ @@ -496,11 +514,11 @@ static void onPacketArrives(pcpp::RawPacket* packet, pcpp::PcapLiveDevice* dev, 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 = "") +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); @@ -538,11 +556,11 @@ void doTcpReassemblyOnPcapFile(const std::string& fileName, pcpp::TcpReassembly& 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 = "") +void doTcpReassemblyOnLiveTraffic(pcpp::PcapLiveDevice* dev, pcpp::TcpReassembly& tcpReassembly, + const std::string& bpfFilter = "") { // try to open device if (!dev->open()) @@ -565,7 +583,7 @@ void doTcpReassemblyOnLiveTraffic(pcpp::PcapLiveDevice* dev, pcpp::TcpReassembly pcpp::ApplicationEventHandler::getInstance().onApplicationInterrupted(onApplicationInterrupted, &shouldStop); // run in an endless loop until the user presses ctrl+c - while(!shouldStop) + while (!shouldStop) pcpp::multiPlatformSleep(1); // stop capturing and close the live device @@ -578,7 +596,6 @@ void doTcpReassemblyOnLiveTraffic(pcpp::PcapLiveDevice* dev, pcpp::TcpReassembly std::cout << "Done! processed " << tcpReassembly.getConnectionInformation().size() << " connections" << std::endl; } - /** * main method of this utility */ @@ -598,48 +615,48 @@ int main(int argc, char* argv[]) int optionIndex = 0; int opt; - while((opt = getopt_long(argc, argv, "i:r:o:e:f:mcsvhl", TcpAssemblyOptions, &optionIndex)) != -1) + 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); + 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); } } @@ -662,17 +679,19 @@ int main(int argc, char* argv[]) TcpReassemblyConnMgr connMgr; // create the TCP reassembly instance - pcpp::TcpReassembly tcpReassembly(tcpReassemblyMsgReadyCallback, &connMgr, tcpReassemblyConnectionStartCallback, tcpReassemblyConnectionEndCallback); + 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 + else // analyze in live traffic mode { // extract pcap live device by interface name or IP address - pcpp::PcapLiveDevice* dev = pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDeviceByIpOrName(interfaceNameOrIP); + pcpp::PcapLiveDevice* dev = + pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDeviceByIpOrName(interfaceNameOrIP); if (dev == nullptr) EXIT_WITH_ERROR("Couldn't find interface by provided IP address or name"); diff --git a/Examples/Tutorials/Tutorial-DpdkL2Fwd/WorkerThread.cpp b/Examples/Tutorials/Tutorial-DpdkL2Fwd/WorkerThread.cpp index 50417731f3..0260cd7cb1 100644 --- a/Examples/Tutorials/Tutorial-DpdkL2Fwd/WorkerThread.cpp +++ b/Examples/Tutorials/Tutorial-DpdkL2Fwd/WorkerThread.cpp @@ -1,10 +1,8 @@ #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) { diff --git a/Examples/Tutorials/Tutorial-DpdkL2Fwd/WorkerThread.h b/Examples/Tutorials/Tutorial-DpdkL2Fwd/WorkerThread.h index 3e2a7ebb40..aff9e96933 100644 --- a/Examples/Tutorials/Tutorial-DpdkL2Fwd/WorkerThread.h +++ b/Examples/Tutorials/Tutorial-DpdkL2Fwd/WorkerThread.h @@ -5,18 +5,19 @@ class L2FwdWorkerThread : public pcpp::DpdkWorkerThread { - private: +private: pcpp::DpdkDevice* m_RxDevice; pcpp::DpdkDevice* m_TxDevice; bool m_Stop; uint32_t m_CoreId; public: - // c'tor + // c'tor L2FwdWorkerThread(pcpp::DpdkDevice* rxDevice, pcpp::DpdkDevice* txDevice); // d'tor (does nothing) - ~L2FwdWorkerThread() { } + ~L2FwdWorkerThread() + {} // implement abstract method diff --git a/Examples/Tutorials/Tutorial-DpdkL2Fwd/main.cpp b/Examples/Tutorials/Tutorial-DpdkL2Fwd/main.cpp index a240fde53b..2e8eb2092e 100644 --- a/Examples/Tutorials/Tutorial-DpdkL2Fwd/main.cpp +++ b/Examples/Tutorials/Tutorial-DpdkL2Fwd/main.cpp @@ -6,7 +6,7 @@ #include "TablePrinter.h" #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 @@ -20,7 +20,6 @@ void onApplicationInterrupted(void* cookie) std::cout << std::endl << "Shutting down..." << std::endl; } - void printStats(pcpp::DpdkDevice* rxDevice, pcpp::DpdkDevice* txDevice) { pcpp::DpdkDevice::DpdkDeviceStats rxStats; @@ -28,21 +27,24 @@ void printStats(pcpp::DpdkDevice* rxDevice, pcpp::DpdkDevice* txDevice) rxDevice->getStatistics(rxStats); txDevice->getStatistics(txStats); - std::vector columnNames = {" ", "Total Packets", "Packets/sec", "Bytes", "Bits/sec"}; - std::vector columnLengths = {10, 15, 15, 15, 15}; + std::vector columnNames = { " ", "Total Packets", "Packets/sec", "Bytes", "Bits/sec" }; + std::vector columnLengths = { 10, 15, 15, 15, 15 }; pcpp::TablePrinter printer(columnNames, columnLengths); std::stringstream totalRx; - totalRx << "rx" << "|" << rxStats.aggregatedRxStats.packets << "|" << rxStats.aggregatedRxStats.packetsPerSec << "|" << rxStats.aggregatedRxStats.bytes << "|" << rxStats.aggregatedRxStats.bytesPerSec*8; + 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; + 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 @@ -70,13 +72,15 @@ int main(int argc, char* argv[]) // Open DPDK devices if (!device1->openMultiQueues(1, 1)) { - std::cerr << "Couldn't open device1 #" << device1->getDeviceId() << ", PMD '" << device1->getPMDName() << "'" << std::endl; + 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; + std::cerr << "Couldn't open device2 #" << device2->getDeviceId() << ", PMD '" << device2->getPMDName() << "'" + << std::endl; return 1; } @@ -115,21 +119,14 @@ int main(int argc, char* argv[]) // Clear screen and move to top left std::cout << "\033[2J\033[1;1H"; - std::cout - << "Stats #" << statsCounter++ << std::endl - << "==========" << std::endl - << std::endl; + 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; + 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; + std::cout << std::endl << "Device2->Device1 stats:" << std::endl << std::endl; printStats(device2, device1); } counter++; diff --git a/Examples/Tutorials/Tutorial-HelloWorld/main.cpp b/Examples/Tutorials/Tutorial-HelloWorld/main.cpp index fcad0e09fb..89f3bdf6d2 100644 --- a/Examples/Tutorials/Tutorial-HelloWorld/main.cpp +++ b/Examples/Tutorials/Tutorial-HelloWorld/main.cpp @@ -32,10 +32,8 @@ int main(int argc, char* argv[]) pcpp::IPv4Address destIP = parsedPacket.getLayerOfType()->getDstIPv4Address(); // print source and dest IPs - std::cout - << "Source IP is '" << srcIP << "'; " - << "Dest IP is '" << destIP << "'" - << std::endl; + std::cout << "Source IP is '" << srcIP << "'; " + << "Dest IP is '" << destIP << "'" << std::endl; } // close the file diff --git a/Examples/Tutorials/Tutorial-LiveTraffic/main.cpp b/Examples/Tutorials/Tutorial-LiveTraffic/main.cpp index b80acf7bc9..38d0d433da 100644 --- a/Examples/Tutorials/Tutorial-LiveTraffic/main.cpp +++ b/Examples/Tutorials/Tutorial-LiveTraffic/main.cpp @@ -17,11 +17,14 @@ struct PacketStats int httpPacketCount = 0; int sslPacketCount = 0; - /** * Clear all stats */ - void clear() { ethPacketCount = ipv4PacketCount = ipv6PacketCount = tcpPacketCount = udpPacketCount = dnsPacketCount = httpPacketCount = sslPacketCount = 0; } + void clear() + { + ethPacketCount = ipv4PacketCount = ipv6PacketCount = tcpPacketCount = udpPacketCount = dnsPacketCount = + httpPacketCount = sslPacketCount = 0; + } // Constructor is optional here since the members are already initialized PacketStats() = default; @@ -54,19 +57,17 @@ struct PacketStats */ 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; + 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 */ @@ -82,7 +83,6 @@ static void onPacketArrives(pcpp::RawPacket* packet, pcpp::PcapLiveDevice* dev, stats->consumePacket(parsedPacket); } - /** * a callback function for the blocking mode capture which is called each time a packet is captured */ @@ -101,7 +101,6 @@ static bool onPacketArrivesBlockingMode(pcpp::RawPacket* packet, pcpp::PcapLiveD return false; } - /** * main method of the application */ @@ -122,13 +121,12 @@ int main(int argc, char* argv[]) // ~~~~~~~~~~~~~~~ // 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 + 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().empty()) { @@ -145,13 +143,13 @@ int main(int argc, char* argv[]) // 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 + // 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 @@ -167,7 +165,6 @@ int main(int argc, char* argv[]) // clear stats stats.clear(); - // Capturing packets in a packet vector // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -199,13 +196,13 @@ int main(int argc, char* argv[]) // clear stats stats.clear(); - // Capturing packets in blocking mode // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 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 + // 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); // thread is blocked until capture is finished @@ -216,16 +213,14 @@ int main(int argc, char* argv[]) stats.clear(); - // Sending single packets // ~~~~~~~~~~~~~~~~~~~~~~ std::cout << std::endl << "Sending " << packetVec.size() << " packets one by one..." << std::endl; // go over the vector of packets and send them one by one - bool allSent = std::all_of(packetVec.begin(), packetVec.end(), [dev](pcpp::RawPacket* packet) { - return dev->sendPacket(*packet); - }); + bool allSent = std::all_of(packetVec.begin(), packetVec.end(), + [dev](pcpp::RawPacket* packet) { return dev->sendPacket(*packet); }); if (!allSent) { @@ -235,18 +230,17 @@ int main(int argc, char* argv[]) std::cout << packetVec.size() << " packets sent" << std::endl; - // Sending batch of packets // ~~~~~~~~~~~~~~~~~~~~~~~~ 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) + // 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); std::cout << packetsSent << " packets sent" << std::endl; - // Using filters // ~~~~~~~~~~~~~ @@ -266,7 +260,8 @@ int main(int argc, char* argv[]) std::cout << std::endl << "Starting packet capture with a filter in place..." << 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 + // 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 diff --git a/Examples/Tutorials/Tutorial-PacketCraftAndEdit/main.cpp b/Examples/Tutorials/Tutorial-PacketCraftAndEdit/main.cpp index 71823e5748..bf02192c18 100644 --- a/Examples/Tutorials/Tutorial-PacketCraftAndEdit/main.cpp +++ b/Examples/Tutorials/Tutorial-PacketCraftAndEdit/main.cpp @@ -83,7 +83,8 @@ int main(int argc, char* argv[]) // 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"); + 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"); @@ -104,7 +105,6 @@ int main(int argc, char* argv[]) writer.close(); } - // Packet Creation // ~~~~~~~~~~~~~~~ diff --git a/Examples/Tutorials/Tutorial-PacketParsing/main.cpp b/Examples/Tutorials/Tutorial-PacketParsing/main.cpp index 15999f00c4..0e437eb00b 100644 --- a/Examples/Tutorials/Tutorial-PacketParsing/main.cpp +++ b/Examples/Tutorials/Tutorial-PacketParsing/main.cpp @@ -31,14 +31,22 @@ std::string printTcpFlags(pcpp::TcpLayer* tcpLayer) { std::string result; auto* tcpHeader = tcpLayer->getTcpHeader(); - if (tcpHeader->synFlag) result += "SYN "; - if (tcpHeader->ackFlag) result += "ACK "; - if (tcpHeader->pshFlag) result += "PSH "; - if (tcpHeader->cwrFlag) result += "CWR "; - if (tcpHeader->urgFlag) result += "URG "; - if (tcpHeader->eceFlag) result += "ECE "; - if (tcpHeader->rstFlag) result += "RST "; - if (tcpHeader->finFlag) result += "FIN "; + if (tcpHeader->synFlag) + result += "SYN "; + if (tcpHeader->ackFlag) + result += "ACK "; + if (tcpHeader->pshFlag) + result += "PSH "; + if (tcpHeader->cwrFlag) + result += "CWR "; + if (tcpHeader->urgFlag) + result += "URG "; + if (tcpHeader->eceFlag) + result += "ECE "; + if (tcpHeader->rstFlag) + result += "RST "; + if (tcpHeader->finFlag) + result += "FIN "; return result; } @@ -102,15 +110,17 @@ int main(int argc, char* argv[]) // 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 + // 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 (auto* 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; + 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 @@ -123,9 +133,10 @@ int main(int argc, char* argv[]) // 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; + << "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 auto* ipLayer = parsedPacket.getLayerOfType(); @@ -137,10 +148,10 @@ int main(int argc, char* argv[]) // 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; + << "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 auto* tcpLayer = parsedPacket.getLayerOfType(); @@ -152,13 +163,14 @@ int main(int argc, char* argv[]) // 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; + << "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)) + for (pcpp::TcpOption tcpOption = tcpLayer->getFirstTcpOption(); tcpOption.isNotNull(); + tcpOption = tcpLayer->getNextTcpOption(tcpOption)) { std::cout << printTcpOptionType(tcpOption.getTcpOptionEnumType()) << " "; } @@ -174,14 +186,15 @@ int main(int argc, char* argv[]) // 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; + << "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; + 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 eedb5a1730..739265c4d8 100644 --- a/Examples/Tutorials/Tutorial-PcapFiles/main.cpp +++ b/Examples/Tutorials/Tutorial-PcapFiles/main.cpp @@ -69,8 +69,8 @@ int main(int argc, char* argv[]) // Use lambda to simplify statistics output auto printStats = [](const std::string& writerName, const pcpp::IPcapDevice::PcapStats& stats) { - std::cout << "Written " << stats.packetsRecv << " packets successfully to " << writerName - << " and " << stats.packetsDrop << " packets could not be written" << std::endl; + std::cout << "Written " << stats.packetsRecv << " packets successfully to " << writerName << " and " + << stats.packetsDrop << " packets could not be written" << std::endl; }; // create the stats object @@ -78,7 +78,8 @@ int main(int argc, char* argv[]) // 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; + 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); diff --git a/Examples/XdpExample-FilterTraffic/PacketMatchingEngine.h b/Examples/XdpExample-FilterTraffic/PacketMatchingEngine.h index ef249f114a..e5354898ed 100644 --- a/Examples/XdpExample-FilterTraffic/PacketMatchingEngine.h +++ b/Examples/XdpExample-FilterTraffic/PacketMatchingEngine.h @@ -7,8 +7,9 @@ #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 { @@ -19,11 +20,13 @@ class PacketMatchingEngine 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) + 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; diff --git a/Examples/XdpExample-FilterTraffic/main.cpp b/Examples/XdpExample-FilterTraffic/main.cpp index 747984737d..f08b180c44 100644 --- a/Examples/XdpExample-FilterTraffic/main.cpp +++ b/Examples/XdpExample-FilterTraffic/main.cpp @@ -1,12 +1,12 @@ /** -* Filter Traffic AF_XDP example application -* ========================================= -* -* This application demonstrates PcapPlusPlus AF_XDP APIs. -* Please read the README.md file for more information. -* -* You can also run `XdpTrafficFilter -h` for modes of operation and parameters. -*/ + * Filter Traffic AF_XDP example application + * ========================================= + * + * This application demonstrates PcapPlusPlus AF_XDP APIs. + * Please read the README.md file for more information. + * + * You can also run `XdpTrafficFilter -h` for modes of operation and parameters. + */ #include "PacketMatchingEngine.h" #include "SystemUtils.h" @@ -22,15 +22,19 @@ #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 EXIT_WITH_ERROR_AND_PRINT_USAGE(reason) do { \ - printUsage(); \ - std::cout << std::endl << "ERROR: " << reason << std::endl << std::endl; \ - exit(1); \ +#define EXIT_WITH_ERROR_AND_PRINT_USAGE(reason) \ + do \ + { \ + printUsage(); \ + std::cout << std::endl << "ERROR: " << reason << std::endl << std::endl; \ + exit(1); \ } while (0) /** @@ -55,9 +59,11 @@ struct PacketStats int matchedUdpFlows; int matchedPacketCount; - PacketStats() : packetCount(0), ethCount(0), arpCount(0), ip4Count(0), ip6Count(0), tcpCount(0), udpCount(0), - httpCount(0), dnsCount(0), sslCount(0), - totalTcpFlows(0), totalUdpFlows(0), matchedTcpFlows(0), matchedUdpFlows(0), matchedPacketCount(0) {} + PacketStats() + : packetCount(0), ethCount(0), arpCount(0), ip4Count(0), ip6Count(0), tcpCount(0), udpCount(0), httpCount(0), + dnsCount(0), sslCount(0), totalTcpFlows(0), totalUdpFlows(0), matchedTcpFlows(0), matchedUdpFlows(0), + matchedPacketCount(0) + {} /** * Collect stats per packet @@ -98,22 +104,26 @@ struct PacketCaptureArgs pcpp::PcapFileWriterDevice* pcapWriter; bool stopCapture; - PacketCaptureArgs() : packetStats(nullptr), matchingEngine(nullptr), sendPacketsTo(nullptr), pcapWriter(nullptr), stopCapture(false) {} + PacketCaptureArgs() + : packetStats(nullptr), matchingEngine(nullptr), sendPacketsTo(nullptr), pcapWriter(nullptr), stopCapture(false) + {} }; +// clang-format off static struct option XdpFilterTrafficOptions[] = { - {"interface", required_argument, nullptr, 'n'}, - {"send-matched-packets", required_argument, nullptr, 's'}, - {"save-matched-packets", required_argument, nullptr, 'f'}, - {"match-source-ip", required_argument, nullptr, 'i'}, - {"match-dest-ip", required_argument, nullptr, 'I'}, - {"match-source-port", required_argument, nullptr, 'p'}, - {"match-dest-port", required_argument, nullptr, 'P'}, - {"match-protocol", required_argument, nullptr, 'r'}, - {"help", no_argument, nullptr, 'h'}, - {"version", no_argument, nullptr, 'v'}, - {"list-interfaces", no_argument, nullptr, 'l'} + { "interface", required_argument, nullptr, 'n' }, + { "send-matched-packets", required_argument, nullptr, 's' }, + { "save-matched-packets", required_argument, nullptr, 'f' }, + { "match-source-ip", required_argument, nullptr, 'i' }, + { "match-dest-ip", required_argument, nullptr, 'I' }, + { "match-source-port", required_argument, nullptr, 'p' }, + { "match-dest-port", required_argument, nullptr, 'P' }, + { "match-protocol", required_argument, nullptr, 'r' }, + { "help", no_argument, nullptr, 'h' }, + { "version", no_argument, nullptr, 'v' }, + { "list-interfaces", no_argument, nullptr, 'l' } }; +// clang-format on /** * A callback to handle packets that were received on the AF_XDP socket @@ -140,7 +150,8 @@ void onPacketsArrive(pcpp::RawPacket packets[], uint32_t packetCount, pcpp::XdpD // collect stats for packet args->packetStats->collectStats(packet); - // hash the packet by 5-tuple and look in the flow table to see whether this packet belongs to an existing or new flow + // 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); auto iter = args->flowTable.find(hash); @@ -151,7 +162,7 @@ void onPacketsArrive(pcpp::RawPacket packets[], uint32_t packetCount, pcpp::XdpD { packetMatched = true; } - else // packet belongs to a new flow + else // packet belongs to a new flow { auto isTcpFlow = packet.isPacketOfType(pcpp::TCP); auto isUdpFlow = packet.isPacketOfType(pcpp::UDP); @@ -171,7 +182,7 @@ void onPacketsArrive(pcpp::RawPacket packets[], uint32_t packetCount, pcpp::XdpD // put new flow in flow table args->flowTable[hash] = true; - //collect stats + // collect stats if (isTcpFlow) { args->packetStats->matchedTcpFlows++; @@ -210,10 +221,11 @@ void onPacketsArrive(pcpp::RawPacket packets[], uint32_t packetCount, pcpp::XdpD /** * Print the stats in a table */ -void printStats(PacketStats* packetStats, pcpp::XdpDevice::XdpDeviceStats* rxDeviceStats, pcpp::XdpDevice::XdpDeviceStats* txDeviceStats) +void printStats(PacketStats* packetStats, pcpp::XdpDevice::XdpDeviceStats* rxDeviceStats, + pcpp::XdpDevice::XdpDeviceStats* txDeviceStats) { - std::vector columnNames = {"Stat", "Count"}; - std::vector columnsWidths = {21, 10}; + std::vector columnNames = { "Stat", "Count" }; + std::vector columnsWidths = { 21, 10 }; pcpp::TablePrinter printer(columnNames, columnsWidths); printer.printRow("Eth count|" + std::to_string(packetStats->ethCount), '|'); @@ -268,7 +280,7 @@ void collectStats(std::future futureObj, PacketStats* packetStats, pcpp::X { txStats = new pcpp::XdpDevice::XdpDeviceStats(sendDev->getStatistics()); } - else // send and receive sockets are the same + else // send and receive sockets are the same { txStats = &rxStats; } @@ -290,39 +302,51 @@ void collectStats(std::future futureObj, PacketStats* packetStats, pcpp::X 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] -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 network interfaces and exit" << std::endl - << " -n|--interface-name INTERFACE_NAME : An interface name to open AF_XDP socket and receive packets from." << std::endl - << " To see all available interfaces use the -l switch" << std::endl - << " -s|--send-matched-packets INTERFACE_NAME : Network interface name to send matched packets to." << std::endl - << " The app will open another AF_XDP socket for sending packets." << std::endl - << " Note: this interface can be the same one used to receive packets." << std::endl - << " -f|--save-matched-packets FILEPATH : Save matched packets to pcap files under FILEPATH." << 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 - << 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] -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 network interfaces and exit" + << std::endl + << " -n|--interface-name INTERFACE_NAME : An interface name to open AF_XDP socket and receive " + "packets from." + << std::endl + << " To see all available interfaces use the -l switch" + << std::endl + << " -s|--send-matched-packets INTERFACE_NAME : Network interface name to send matched packets to." + << std::endl + << " The app will open another AF_XDP socket for sending " + "packets." + << std::endl + << " Note: this interface can be the same one used to " + "receive packets." + << std::endl + << " -f|--save-matched-packets FILEPATH : Save matched packets to pcap files under FILEPATH." + << 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 + << 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; + std::cout << pcpp::AppName::get() << " " << pcpp::getPcapPlusPlusVersionFull() << std::endl + << "Built: " << pcpp::getBuildDateTime() << std::endl + << "Built from: " << pcpp::getGitInfo() << std::endl; exit(0); } @@ -332,11 +356,12 @@ void printAppVersion() void listInterfaces() { std::cout << std::endl << "Network interfaces:" << std::endl; - for(const auto& device : pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDevicesList()) + for (const auto& device : pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDevicesList()) { if (device->getIPv4Address() != pcpp::IPv4Address::Zero) { - std::cout << " -> Name: '" << device->getName() << "' IP address: " << device->getIPv4Address().toString() << std::endl; + std::cout << " -> Name: '" << device->getName() + << "' IP address: " << device->getIPv4Address().toString() << std::endl; } } exit(0); @@ -351,119 +376,119 @@ int main(int argc, char* argv[]) std::string interfaceName; - pcpp::IPv4Address srcIPToMatch = pcpp::IPv4Address::Zero; - pcpp::IPv4Address dstIPToMatch = pcpp::IPv4Address::Zero; - uint16_t srcPortToMatch = 0; - uint16_t dstPortToMatch = 0; + 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; std::string writePacketsToFileName; std::string sendInterfaceName; - while((opt = getopt_long(argc, argv, "n:f:s:i:I:p:P:r:vhl", XdpFilterTrafficOptions, &optionIndex)) != -1) + while ((opt = getopt_long(argc, argv, "n:f:s:i:I:p:P:r:vhl", XdpFilterTrafficOptions, &optionIndex)) != -1) { switch (opt) { - case 0: - { - break; - } - case 'n': - { - interfaceName = std::string(optarg); - break; - } - case 'f': - { - writePacketsToFileName = std::string(optarg); - break; - } - case 's': - { - sendInterfaceName = std::string(optarg); - break; - } - case 'i': + case 0: + { + break; + } + case 'n': + { + interfaceName = std::string(optarg); + break; + } + case 'f': + { + writePacketsToFileName = std::string(optarg); + break; + } + case 's': + { + sendInterfaceName = std::string(optarg); + break; + } + case 'i': + { + try { - try - { - srcIPToMatch = pcpp::IPv4Address(optarg); - } - catch(const std::exception&) - { - EXIT_WITH_ERROR_AND_PRINT_USAGE("Source IP to match isn't a valid IP address"); - } - break; + srcIPToMatch = pcpp::IPv4Address(optarg); } - case 'I': + catch (const std::exception&) { - try - { - dstIPToMatch = pcpp::IPv4Address(optarg); - } - catch(const std::exception&) - { - EXIT_WITH_ERROR_AND_PRINT_USAGE("Destination IP to match isn't a valid IP address"); - } - break; + EXIT_WITH_ERROR_AND_PRINT_USAGE("Source IP to match isn't a valid IP address"); } - case 'p': + break; + } + case 'I': + { + try { - int ret = std::stoi(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; + dstIPToMatch = pcpp::IPv4Address(optarg); } - case 'P': + catch (const std::exception&) { - int ret = std::stoi(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; + EXIT_WITH_ERROR_AND_PRINT_USAGE("Destination IP to match isn't a valid IP address"); } - case 'r': + break; + } + case 'p': + { + int ret = std::stoi(optarg); + if (ret <= 0 || ret > 65535) { - 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; + EXIT_WITH_ERROR_AND_PRINT_USAGE("Source port to match isn't a valid TCP/UDP port"); } - case 'h': + srcPortToMatch = ret; + break; + } + case 'P': + { + int ret = std::stoi(optarg); + if (ret <= 0 || ret > 65535) { - printUsage(); - exit(0); + EXIT_WITH_ERROR_AND_PRINT_USAGE("Destination port to match isn't a valid TCP/UDP port"); } - case 'v': + dstPortToMatch = ret; + break; + } + case 'r': + { + std::string protocol = std::string(optarg); + if (protocol == "TCP") { - printAppVersion(); - break; + protocolToMatch = pcpp::TCP; } - case 'l': + else if (protocol == "UDP") { - listInterfaces(); - exit(0); + protocolToMatch = pcpp::UDP; } - default: + else { - printUsage(); - exit(0); + 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': + { + listInterfaces(); + exit(0); + } + default: + { + printUsage(); + exit(0); + } } } @@ -532,7 +557,8 @@ int main(int argc, char* argv[]) std::thread collectStatsThread(&collectStats, std::move(futureObj), &packetStats, &dev, sendDev); // add an handler for app interrupted signal, i.e ctrl+c - pcpp::ApplicationEventHandler::getInstance().onApplicationInterrupted([](void* args){reinterpret_cast(args)->stopCapture = true;}, &args); + pcpp::ApplicationEventHandler::getInstance().onApplicationInterrupted( + [](void* args) { reinterpret_cast(args)->stopCapture = true; }, &args); // start receiving packets on the AF_XDP socket auto res = dev.receivePackets(onPacketsArrive, &args, -1); @@ -549,7 +575,8 @@ int main(int argc, char* argv[]) { pcpp::IPcapDevice::PcapStats stats; pcapWriter->getStatistics(stats); - additionalStats.push_back("Wrote " + std::to_string(stats.packetsRecv) + " packets to '" + pcapWriter->getFileName() + "'"); + additionalStats.push_back("Wrote " + std::to_string(stats.packetsRecv) + " packets to '" + + pcapWriter->getFileName() + "'"); pcapWriter->close(); delete pcapWriter; } diff --git a/Tests/Pcap++Test/Tests/LiveDeviceTests.cpp b/Tests/Pcap++Test/Tests/LiveDeviceTests.cpp index b93c799899..1536131312 100644 --- a/Tests/Pcap++Test/Tests/LiveDeviceTests.cpp +++ b/Tests/Pcap++Test/Tests/LiveDeviceTests.cpp @@ -302,7 +302,8 @@ PTF_TEST_CASE(TestPcapLiveDevice) { // Should probably be refactored as PTF_ASSERT_CONTAINS or similar. auto const ipAddresses = liveDev->getIPAddresses(); - PTF_ASSERT_TRUE(std::any_of(ipAddresses.begin(), ipAddresses.end(), [ipToSearch](pcpp::IPAddress const& addr) { return addr == ipToSearch; })); + PTF_ASSERT_TRUE(std::any_of(ipAddresses.begin(), ipAddresses.end(), + [ipToSearch](pcpp::IPAddress const& addr) { return addr == ipToSearch; })); } DeviceTeardown devTeardown(liveDev);