From 93eca5bfaa5ff7c5b90720c2c3ddebc74c2885bc Mon Sep 17 00:00:00 2001 From: Dimitar Krastev Date: Sat, 8 Jun 2024 09:33:30 +0300 Subject: [PATCH] Centralize code for fetching pcap devices. (#1434) * Added device utilities header for pcap device utility functions. Added memory utilities header for smart pointer utilities. * Replaced usages of pcap_findalldevs(_ex) with internal::getAll(Local/Remote)PcapDevices. * Removed code creating pcap_capture string as it is unused. * Fixed returning nullptr on clone fail. * Renamed MemoryUtils to PcapUtils. * Moved getAllRemotePcapDevices to anonymous namespace in PcapRemoteDeviceList.cpp * Removed duplicate of PcapCloseDeleter. * Remoted internal utilitiy headers from the public header list. * Removed unused forward declare. * Added doxygen conditionals to exclude the internal classes from the public documentation. --- Pcap++/CMakeLists.txt | 2 + Pcap++/header/DeviceUtils.h | 24 ++++++++++ Pcap++/header/PcapUtils.h | 35 ++++++++++++++ Pcap++/src/DeviceUtils.cpp | 27 +++++++++++ Pcap++/src/PcapFilter.cpp | 12 +---- Pcap++/src/PcapLiveDevice.cpp | 37 +++++++-------- Pcap++/src/PcapLiveDeviceList.cpp | 22 ++++----- Pcap++/src/PcapRemoteDeviceList.cpp | 71 +++++++++++++++++++---------- Pcap++/src/PcapUtils.cpp | 13 ++++++ 9 files changed, 178 insertions(+), 65 deletions(-) create mode 100644 Pcap++/header/DeviceUtils.h create mode 100644 Pcap++/header/PcapUtils.h create mode 100644 Pcap++/src/DeviceUtils.cpp create mode 100644 Pcap++/src/PcapUtils.cpp diff --git a/Pcap++/CMakeLists.txt b/Pcap++/CMakeLists.txt index a23efa7e17..f28fcb6be6 100644 --- a/Pcap++/CMakeLists.txt +++ b/Pcap++/CMakeLists.txt @@ -1,11 +1,13 @@ add_library( Pcap++ + src/DeviceUtils.cpp $<$:src/DpdkDevice.cpp> $<$:src/DpdkDeviceList.cpp> $<$:src/KniDevice.cpp> $<$:src/KniDeviceList.cpp> $<$:src/LinuxNicInformationSocket.cpp> $<$:src/MBufRawPacket.cpp> + src/PcapUtils.cpp src/NetworkUtils.cpp src/PcapFileDevice.cpp src/PcapDevice.cpp diff --git a/Pcap++/header/DeviceUtils.h b/Pcap++/header/DeviceUtils.h new file mode 100644 index 0000000000..7a33e2e277 --- /dev/null +++ b/Pcap++/header/DeviceUtils.h @@ -0,0 +1,24 @@ +#pragma once + +/// @file + +#include +#include "IpAddress.h" +#include "PcapUtils.h" + +namespace pcpp +{ + /// @cond PCPP_INTERNAL + + namespace internal + { + /** + * Fetches a list of all network devices on the local machine that LibPcap/WinPcap/NPcap can find. + * @return A smart pointer to an interface list structure. + * @throws std::runtime_error The system encountered an error fetching the devices. + */ + std::unique_ptr getAllLocalPcapDevices(); + } + + /// @endcond +} diff --git a/Pcap++/header/PcapUtils.h b/Pcap++/header/PcapUtils.h new file mode 100644 index 0000000000..d9f76033fe --- /dev/null +++ b/Pcap++/header/PcapUtils.h @@ -0,0 +1,35 @@ +#pragma once + +// Forward declarations +struct pcap; +typedef pcap pcap_t; +struct pcap_if; +typedef pcap_if pcap_if_t; + +namespace pcpp +{ + /// @cond PCPP_INTERNAL + + namespace internal + { + /** + * @class PcapCloseDeleter + * A deleter that cleans up a pcap_t structure by calling pcap_close. + */ + struct PcapCloseDeleter + { + void operator()(pcap_t* ptr) const; + }; + + /** + * @class PcapFreeAllDevsDeleter + * A deleter that frees an interface list of pcap_if_t ptr by calling 'pcap_freealldevs' function on it. + */ + struct PcapFreeAllDevsDeleter + { + void operator()(pcap_if_t* ptr) const; + }; + } + + /// @endcond +} diff --git a/Pcap++/src/DeviceUtils.cpp b/Pcap++/src/DeviceUtils.cpp new file mode 100644 index 0000000000..785c726b47 --- /dev/null +++ b/Pcap++/src/DeviceUtils.cpp @@ -0,0 +1,27 @@ +#include "DeviceUtils.h" + +#include +#include + +#include "pcap.h" +#include "Logger.h" +#include "IpAddress.h" + +namespace pcpp +{ + namespace internal + { + std::unique_ptr getAllLocalPcapDevices() + { + pcap_if_t* interfaceListRaw; + std::array errbuf; + int err = pcap_findalldevs(&interfaceListRaw, errbuf.data()); + if (err < 0) + { + throw std::runtime_error("Error searching for devices: " + std::string(errbuf.begin(), errbuf.end())); + } + // Assigns the raw pointer to the smart pointer with specialized deleter. + return std::unique_ptr(interfaceListRaw); + } + } +} diff --git a/Pcap++/src/PcapFilter.cpp b/Pcap++/src/PcapFilter.cpp index 28e83f4a75..eb850077bf 100644 --- a/Pcap++/src/PcapFilter.cpp +++ b/Pcap++/src/PcapFilter.cpp @@ -3,6 +3,7 @@ #include "PcapFilter.h" #include "Logger.h" #include "IPv4Layer.h" +#include "PcapUtils.h" #include #include #if defined(_WIN32) @@ -35,15 +36,6 @@ namespace internal pcap_freecode(ptr); delete ptr; } - - /** - * @class PcapTDeleter - * A deleter that cleans up a pcap_t structure by calling pcap_close. - */ - struct PcapTDeleter - { - void operator()(pcap_t* ptr) const { pcap_close(ptr); } - }; } BpfFilterWrapper::BpfFilterWrapper() : m_LinkType(LinkLayerType::LINKTYPE_ETHERNET) {} @@ -66,7 +58,7 @@ bool BpfFilterWrapper::setFilter(const std::string& filter, LinkLayerType linkTy if (filter != m_FilterStr || linkType != m_LinkType) { - std::unique_ptr pcap = std::unique_ptr(pcap_open_dead(linkType, DEFAULT_SNAPLEN)); + auto pcap = std::unique_ptr(pcap_open_dead(linkType, DEFAULT_SNAPLEN)); if (pcap == nullptr) { return false; diff --git a/Pcap++/src/PcapLiveDevice.cpp b/Pcap++/src/PcapLiveDevice.cpp index dcc32f0b74..c3a690bd95 100644 --- a/Pcap++/src/PcapLiveDevice.cpp +++ b/Pcap++/src/PcapLiveDevice.cpp @@ -1,6 +1,8 @@ #define LOG_MODULE PcapLogModuleLiveDevice #include "IpUtils.h" +#include "DeviceUtils.h" +#include "PcapUtils.h" #include "PcapLiveDevice.h" #include "PcapLiveDeviceList.h" #include "Packet.h" @@ -11,7 +13,7 @@ #include #include "Logger.h" #include "SystemUtils.h" -#include +#include #include #include #include @@ -405,32 +407,27 @@ void PcapLiveDevice::close() PcapLiveDevice* PcapLiveDevice::clone() const { - PcapLiveDevice* retval = nullptr; - - pcap_if_t* interfaceList; - char errbuf[PCAP_ERRBUF_SIZE]; - int err = pcap_findalldevs(&interfaceList, errbuf); - if (err < 0) + std::unique_ptr interfaceList; + try + { + interfaceList = internal::getAllLocalPcapDevices(); + } + catch (const std::exception& e) { - PCPP_LOG_ERROR("Error searching for devices: " << errbuf); + PCPP_LOG_ERROR(e.what()); return nullptr; } - pcap_if_t* currInterface = interfaceList; - while (currInterface != nullptr) + for (pcap_if_t* currInterface = interfaceList.get(); currInterface != nullptr; currInterface = currInterface->next) { - if(!strcmp(currInterface->name, getName().c_str())) - break; - currInterface = currInterface->next; + if (!std::strcmp(currInterface->name, getName().c_str())) + { + return cloneInternal(*currInterface); + } } - if(currInterface) - retval = cloneInternal(*currInterface); - else - PCPP_LOG_ERROR("Can't find interface " << getName().c_str()); - - pcap_freealldevs(interfaceList); - return retval; + PCPP_LOG_ERROR("Can't find interface " << getName().c_str()); + return nullptr; } PcapLiveDevice* PcapLiveDevice::cloneInternal(pcap_if_t& devInterface) const diff --git a/Pcap++/src/PcapLiveDeviceList.cpp b/Pcap++/src/PcapLiveDeviceList.cpp index 47f179f479..82efc59703 100644 --- a/Pcap++/src/PcapLiveDeviceList.cpp +++ b/Pcap++/src/PcapLiveDeviceList.cpp @@ -4,6 +4,8 @@ #include "IpAddressUtils.h" #include "PcapLiveDeviceList.h" #include "Logger.h" +#include "PcapUtils.h" +#include "DeviceUtils.h" #include "SystemUtils.h" #include "pcap.h" #include @@ -39,32 +41,30 @@ PcapLiveDeviceList::~PcapLiveDeviceList() void PcapLiveDeviceList::init() { - pcap_if_t* interfaceList; - char errbuf[PCAP_ERRBUF_SIZE]; - int err = pcap_findalldevs(&interfaceList, errbuf); - if (err < 0) + std::unique_ptr interfaceList; + try + { + interfaceList = internal::getAllLocalPcapDevices(); + } + catch (const std::exception& e) { - PCPP_LOG_ERROR("Error searching for devices: " << errbuf); + PCPP_LOG_ERROR(e.what()); } PCPP_LOG_DEBUG("Pcap lib version info: " << IPcapDevice::getPcapLibVersionInfo()); - pcap_if_t* currInterface = interfaceList; - while (currInterface != nullptr) + + for (pcap_if_t* currInterface = interfaceList.get(); currInterface != nullptr; currInterface = currInterface->next) { #if defined(_WIN32) PcapLiveDevice* dev = new WinPcapLiveDevice(currInterface, true, true, true); #else //__linux__, __APPLE__, __FreeBSD__ PcapLiveDevice* dev = new PcapLiveDevice(currInterface, true, true, true); #endif - currInterface = currInterface->next; m_LiveDeviceList.insert(m_LiveDeviceList.end(), dev); } setDnsServers(); - - PCPP_LOG_DEBUG("Freeing live device data"); - pcap_freealldevs(interfaceList); } void PcapLiveDeviceList::setDnsServers() diff --git a/Pcap++/src/PcapRemoteDeviceList.cpp b/Pcap++/src/PcapRemoteDeviceList.cpp index af107fee91..e6ff31205a 100644 --- a/Pcap++/src/PcapRemoteDeviceList.cpp +++ b/Pcap++/src/PcapRemoteDeviceList.cpp @@ -5,6 +5,7 @@ #include "PcapRemoteDeviceList.h" #include "Logger.h" #include "IpUtils.h" +#include "PcapUtils.h" #include "IpAddressUtils.h" #include "pcap.h" #include @@ -13,6 +14,40 @@ namespace pcpp { + namespace + { + /** + * Fetches a list of all network devices on a remote machine that WinPcap/NPcap can find. + * @param[in] ipAddress IP address of the remote machine. + * @param[in] port Port to use when connecting to the remote machine. + * @param[in] pRmAuth Pointer to an authentication structure to use when connecting to the remote machine. Nullptr if no authentication is required. + * @return A smart pointer to an interface list structure. + * @throws std::runtime_error The system encountered an error fetching the devices. + */ + std::unique_ptr getAllRemotePcapDevices(const IPAddress& ipAddress, uint16_t port, pcap_rmtauth* pRmAuth = nullptr) + { + PCPP_LOG_DEBUG("Searching remote devices on IP: " << ipAddress << " and port: " << port); + std::array remoteCaptureString; + std::array errorBuf; + if (pcap_createsrcstr(remoteCaptureString.data(), PCAP_SRC_IFREMOTE, ipAddress.toString().c_str(), + std::to_string(port).c_str(), nullptr, errorBuf.data()) != 0) + { + throw std::runtime_error("Error creating the remote connection string. Error: " + + std::string(errorBuf.begin(), errorBuf.end())); + } + + PCPP_LOG_DEBUG("Remote capture string: " << remoteCaptureString.data()); + + pcap_if_t* interfaceListRaw; + if (pcap_findalldevs_ex(remoteCaptureString.data(), pRmAuth, &interfaceListRaw, errorBuf.data()) < 0) + { + throw std::runtime_error("Error retrieving device on remote machine. Error: " + + std::string(errorBuf.begin(), errorBuf.end())); + } + return std::unique_ptr(interfaceListRaw); + } + } + PcapRemoteDeviceList* PcapRemoteDeviceList::getRemoteDeviceList(const IPAddress& ipAddress, uint16_t port) { return PcapRemoteDeviceList::getRemoteDeviceList(ipAddress, port, NULL); @@ -20,34 +55,24 @@ PcapRemoteDeviceList* PcapRemoteDeviceList::getRemoteDeviceList(const IPAddress& PcapRemoteDeviceList* PcapRemoteDeviceList::getRemoteDeviceList(const IPAddress& ipAddress, uint16_t port, PcapRemoteAuthentication* remoteAuth) { - PCPP_LOG_DEBUG("Searching remote devices on IP: " << ipAddress << " and port: " << port); - char remoteCaptureString[PCAP_BUF_SIZE]; - char errbuf[PCAP_ERRBUF_SIZE]; - std::ostringstream portAsString; - portAsString << port; - if (pcap_createsrcstr(remoteCaptureString, PCAP_SRC_IFREMOTE, ipAddress.toString().c_str(), portAsString.str().c_str(), NULL, errbuf) != 0) - { - PCPP_LOG_ERROR("Error in creating the remote connection string. Error was: " << errbuf); - return NULL; - } - - PCPP_LOG_DEBUG("Remote capture string: " << remoteCaptureString); - - pcap_rmtauth* pRmAuth = NULL; + pcap_rmtauth* pRmAuth = nullptr; pcap_rmtauth rmAuth; - if (remoteAuth != NULL) + if (remoteAuth != nullptr) { PCPP_LOG_DEBUG("Authentication requested. Username: " << remoteAuth->userName << ", Password: " << remoteAuth->password); rmAuth = remoteAuth->getPcapRmAuth(); pRmAuth = &rmAuth; } - pcap_if_t* interfaceList; - char errorBuf[PCAP_ERRBUF_SIZE]; - if (pcap_findalldevs_ex(remoteCaptureString, pRmAuth, &interfaceList, errorBuf) < 0) + std::unique_ptr interfaceList; + try { - PCPP_LOG_ERROR("Error retrieving device on remote machine. Error string is: " << errorBuf); - return NULL; + interfaceList = getAllRemotePcapDevices(ipAddress, port, pRmAuth); + } + catch (const std::exception& e) + { + PCPP_LOG_ERROR(e.what()); + return nullptr; } PcapRemoteDeviceList* resultList = new PcapRemoteDeviceList(); @@ -55,16 +80,14 @@ PcapRemoteDeviceList* PcapRemoteDeviceList::getRemoteDeviceList(const IPAddress& resultList->setRemoteMachinePort(port); resultList->setRemoteAuthentication(remoteAuth); - pcap_if_t* currInterface = interfaceList; - while (currInterface != NULL) + + for (pcap_if_t* currInterface = interfaceList.get(); currInterface != nullptr; currInterface = currInterface->next) { PcapRemoteDevice* pNewRemoteDevice = new PcapRemoteDevice(currInterface, resultList->m_RemoteAuthentication, resultList->getRemoteMachineIpAddress(), resultList->getRemoteMachinePort()); resultList->m_RemoteDeviceList.push_back(pNewRemoteDevice); - currInterface = currInterface->next; } - pcap_freealldevs(interfaceList); return resultList; } diff --git a/Pcap++/src/PcapUtils.cpp b/Pcap++/src/PcapUtils.cpp new file mode 100644 index 0000000000..b3df4eca44 --- /dev/null +++ b/Pcap++/src/PcapUtils.cpp @@ -0,0 +1,13 @@ +#include "PcapUtils.h" + +#include "pcap.h" + +namespace pcpp +{ + namespace internal + { + void PcapCloseDeleter::operator()(pcap_t* ptr) const { pcap_close(ptr); } + + void PcapFreeAllDevsDeleter::operator()(pcap_if_t* ptr) const { pcap_freealldevs(ptr); } + } +}