Skip to content

Commit

Permalink
support RawSocketDevice with timeout in millisecond precision. (#1529)
Browse files Browse the repository at this point in the history
* support RawSocketDevice with timeout in millisecond precision.

* support RawSocketDevice with timeout in millisecond precision

* use steady_clock
  • Loading branch information
tigercosmos authored Aug 8, 2024
1 parent 316ea6e commit 0a84311
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 16 deletions.
10 changes: 6 additions & 4 deletions Pcap++/header/RawSocketDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,26 +86,28 @@ namespace pcpp
* @param[in] blocking Indicates whether to run in blocking or non-blocking mode. Default value is blocking
* @param[in] timeout When in blocking mode, specifies the timeout [in seconds] to wait for a packet. If timeout expired
* and no packets were captured the method will return RawSocketDevice#RecvTimeout. Zero or negative values mean no
* timeout. The default value is no timeout
* timeout. The default value is no timeout. The timeout precision is in milliseconds, for example a timeout of 0.123
* means 123 milliseconds.
* @return The method returns one on the following values:
* - RawSocketDevice#RecvSuccess is returned if a packet was received successfully
* - RawSocketDevice#RecvTimeout is returned if in blocking mode and timeout expired
* - RawSocketDevice#RecvWouldBlock is returned if in non-blocking mode and no packets were captured
* - RawSocketDevice#RecvError is returned if an error occurred such as device is not opened or the recv operation
* returned some error. A log message will be followed specifying the error and error code
*/
RecvPacketResult receivePacket(RawPacket& rawPacket, bool blocking = true, int timeout = -1);
RecvPacketResult receivePacket(RawPacket& rawPacket, bool blocking = true, double timeout = -1);

/**
* Receive packets into a packet vector for a certain amount of time. This method starts a timer and invokes the
* receivePacket() method in blocking mode repeatedly until the timeout expires. All packets received successfully are
* put into a packet vector
* @param[out] packetVec The packet vector to add the received packet to
* @param[in] timeout Timeout in seconds to receive packets on the raw socket
* @param[in] timeout Timeout in seconds to receive packets on the raw socket. The timeout precision is in milliseconds,
* for example a timeout of 0.123 means 123 milliseconds.
* @param[out] failedRecv Number of receive attempts that failed
* @return The number of packets received successfully
*/
int receivePackets(RawPacketVector& packetVec, int timeout, int& failedRecv);
int receivePackets(RawPacketVector& packetVec, double timeout, int& failedRecv);

/**
* Send an Ethernet packet to the network. L2 protocols other than Ethernet are not supported in raw sockets.
Expand Down
31 changes: 19 additions & 12 deletions Pcap++/src/RawSocketDevice.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "RawSocketDevice.h"
#include "EndianPortable.h"
#include <chrono>
#ifdef __linux__
#include <fcntl.h>
#include <errno.h>
Expand Down Expand Up @@ -95,7 +96,7 @@ RawSocketDevice::~RawSocketDevice()
close();
}

RawSocketDevice::RecvPacketResult RawSocketDevice::receivePacket(RawPacket& rawPacket, bool blocking, int timeout)
RawSocketDevice::RecvPacketResult RawSocketDevice::receivePacket(RawPacket& rawPacket, bool blocking, double timeout)
{
#if defined(_WIN32)

Expand All @@ -116,7 +117,7 @@ RawSocketDevice::RecvPacketResult RawSocketDevice::receivePacket(RawPacket& rawP
u_long blockingMode = (blocking? 0 : 1);
ioctlsocket(fd, FIONBIO, &blockingMode);

DWORD timeoutVal = timeout * 1000;
DWORD timeoutVal = timeout * 1000; // convert to milliseconds
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeoutVal, sizeof(timeoutVal));

//recvfrom(fd, buffer, RAW_SOCKET_BUFFER_LEN, 0, (struct sockaddr*)&sockAddr,(socklen_t*)&sockAddrLen);
Expand Down Expand Up @@ -179,8 +180,8 @@ RawSocketDevice::RecvPacketResult RawSocketDevice::receivePacket(RawPacket& rawP

// set timeout on socket
struct timeval timeoutVal;
timeoutVal.tv_sec = timeout;
timeoutVal.tv_usec = 0;
timeoutVal.tv_sec = static_cast<int>(timeout);
timeoutVal.tv_usec = static_cast<long int>((timeout - timeoutVal.tv_sec) * 1000000);
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeoutVal, sizeof(timeoutVal));

int bufferLen = recv(fd, buffer, RAW_SOCKET_BUFFER_LEN, 0);
Expand Down Expand Up @@ -216,26 +217,34 @@ RawSocketDevice::RecvPacketResult RawSocketDevice::receivePacket(RawPacket& rawP
#endif
}

int RawSocketDevice::receivePackets(RawPacketVector& packetVec, int timeout, int& failedRecv)
int RawSocketDevice::receivePackets(RawPacketVector& packetVec, double timeout, int& failedRecv)
{
if (!isOpened())
{
PCPP_LOG_ERROR("Device is not open");
return 0;
}

long curSec, curNsec;
clockGetTime(curSec, curNsec);
int64_t timeoutMilli = timeout * 1000;

int packetCount = 0;
failedRecv = 0;

long timeoutSec = curSec + timeout;
auto start = std::chrono::steady_clock::now();

while (curSec < timeoutSec)
while (true)
{
auto now = std::chrono::steady_clock::now();
auto elapsedMilli = std::chrono::duration_cast<std::chrono::milliseconds>(now - start).count();
double elapsedSec = static_cast<double>(elapsedMilli) / 1000;

if (elapsedMilli >= timeoutMilli)
{
break;
}

RawPacket* rawPacket = new RawPacket();
if (receivePacket(*rawPacket, true, timeoutSec-curSec) == RecvSuccess)
if (receivePacket(*rawPacket, true, elapsedSec) == RecvSuccess)
{
packetVec.pushBack(rawPacket);
packetCount++;
Expand All @@ -245,8 +254,6 @@ int RawSocketDevice::receivePackets(RawPacketVector& packetVec, int timeout, int
failedRecv++;
delete rawPacket;
}

clockGetTime(curSec, curNsec);
}

return packetCount;
Expand Down

0 comments on commit 0a84311

Please sign in to comment.