Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 32 additions & 2 deletions Pcap++/header/XdpDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@ namespace pcpp
/// The max number of packets to be received or sent in one batch
uint16_t rxTxBatchSize;

/// This parameter specifies the amount of space designated for prepending data to the packet in the frame.
/// NOTE: the headroom size should be less than the frame size
uint16_t frameHeadroomSize;

/// The queue identifier for the underlying socket. This value should be less than the number
/// of hardware queues supported by the device
uint16_t queueId;

/// A c'tor for this struct. Each parameter has a default value described below.
/// @param[in] attachMode AF_XDP operation mode. The default value is auto mode
/// @param[in] umemNumFrames Number of UMEM frames to allocate. The default value is 4096
Expand All @@ -87,19 +95,24 @@ namespace pcpp
/// @param[in] txSize The size of the TX ring used by the AF_XDP socket. The default value is 2048
/// @param[in] rxTxBatchSize The max number of packets to be received or sent in one batch. The default
/// value is 64
/// @param[in] frameHeadroomSize Space for prepending to packets. The default value is 0
/// @param[in] queueId The hardware queue id of the underlying socket. The default value is 0
explicit XdpDeviceConfiguration(AttachMode attachMode = AutoMode, uint16_t umemNumFrames = 0,
uint16_t umemFrameSize = 0, uint32_t fillRingSize = 0,
uint32_t completionRingSize = 0, uint32_t rxSize = 0, uint32_t txSize = 0,
uint16_t rxTxBatchSize = 0)
uint16_t rxTxBatchSize = 0, uint16_t frameHeadroomSize = 0,
uint32_t queueId = 0)
{
this->attachMode = attachMode;
this->umemNumFrames = umemNumFrames;
this->umemFrameSize = umemFrameSize;
this->frameHeadroomSize = frameHeadroomSize;
this->fillRingSize = fillRingSize;
this->completionRingSize = completionRingSize;
this->rxSize = rxSize;
this->txSize = txSize;
this->rxTxBatchSize = rxTxBatchSize;
this->queueId = queueId;
}
};

Expand Down Expand Up @@ -238,11 +251,28 @@ namespace pcpp
/// @return Current device statistics
XdpDeviceStats getStatistics();

/// @return Return queue identifier for underlying socket
uint32_t getQueueId() const
{
if (m_Config)
{
return m_Config->queueId;
}

return 0;
}

/// Get number of RX or TX hardware queues for device
/// @param[in] interfaceName The interface name to use to detect hardware queues
/// @param[in] tx If true, return TX queues, otherwise RX. Default is false
/// @return The number of hardware queues associated with the device.
static uint32_t numOfHardwareQueues(const std::string& interfaceName, bool tx = false);

private:
class XdpUmem
{
public:
explicit XdpUmem(uint16_t numFrames, uint16_t frameSize, uint32_t fillRingSize,
explicit XdpUmem(uint16_t numFrames, uint16_t frameSize, uint16_t frameHeadroomSize, uint32_t fillRingSize,
uint32_t completionRingSize);

virtual ~XdpUmem();
Expand Down
73 changes: 64 additions & 9 deletions Pcap++/src/XdpDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@
#include <net/if.h>
#include <sys/mman.h>
#include <unistd.h>
#include <dirent.h>
#include <vector>
#include <functional>
#include <algorithm>
#include <regex>
#include <poll.h>

namespace pcpp
Expand All @@ -36,10 +38,12 @@ namespace pcpp
#define DEFAULT_FILL_RING_SIZE (XSK_RING_PROD__DEFAULT_NUM_DESCS * 2)
#define DEFAULT_COMPLETION_RING_SIZE XSK_RING_PROD__DEFAULT_NUM_DESCS
#define DEFAULT_BATCH_SIZE 64
#define DEFAULT_NUM_QUEUES 1
#define DEFAULT_FRAME_HEADROOM_SIZE XSK_UMEM__DEFAULT_FRAME_HEADROOM
#define IS_POWER_OF_TWO(num) (num && ((num & (num - 1)) == 0))

XdpDevice::XdpUmem::XdpUmem(uint16_t numFrames, uint16_t frameSize, uint32_t fillRingSize,
uint32_t completionRingSize)
XdpDevice::XdpUmem::XdpUmem(uint16_t numFrames, uint16_t frameSize, uint16_t frameHeadroomSize,
uint32_t fillRingSize, uint32_t completionRingSize)
{
size_t bufferSize = numFrames * frameSize;

Expand All @@ -51,7 +55,7 @@ namespace pcpp
struct xsk_umem_config cfg = { .fill_size = fillRingSize,
.comp_size = completionRingSize,
.frame_size = frameSize,
.frame_headroom = XSK_UMEM__DEFAULT_FRAME_HEADROOM,
.frame_headroom = frameHeadroomSize,
.flags = 0 };

struct xsk_umem_info* umem = new xsk_umem_info;
Expand Down Expand Up @@ -403,8 +407,8 @@ namespace pcpp
auto umemInfo = static_cast<xsk_umem_info*>(m_Umem->getInfo());

struct xsk_socket_config xskConfig;
xskConfig.rx_size = m_Config->txSize;
xskConfig.tx_size = m_Config->rxSize;
xskConfig.rx_size = m_Config->rxSize;
xskConfig.tx_size = m_Config->txSize;
Comment on lines +410 to +411
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was already fixed in #2030

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will that be a problem if I leave it like this? A merge issue?

xskConfig.libbpf_flags = 0;
xskConfig.xdp_flags = 0;
xskConfig.bind_flags = 0;
Expand All @@ -419,8 +423,8 @@ namespace pcpp
xskConfig.xdp_flags = XDP_FLAGS_DRV_MODE;
}

int ret = xsk_socket__create(&socketInfo->xsk, m_InterfaceName.c_str(), 0, umemInfo->umem, &socketInfo->rx,
&socketInfo->tx, &xskConfig);
int ret = xsk_socket__create(&socketInfo->xsk, m_InterfaceName.c_str(), m_Config->queueId, umemInfo->umem,
&socketInfo->rx, &socketInfo->tx, &xskConfig);
if (ret)
{
PCPP_LOG_ERROR("xsk_socket__create returned an error: " << ret);
Expand All @@ -429,26 +433,29 @@ namespace pcpp
}

m_SocketInfo = socketInfo;

return true;
}

bool XdpDevice::initUmem()
{
m_Umem = new XdpUmem(m_Config->umemNumFrames, m_Config->umemFrameSize, m_Config->fillRingSize,
m_Config->completionRingSize);
m_Umem = new XdpUmem(m_Config->umemNumFrames, m_Config->umemFrameSize, m_Config->frameHeadroomSize,
m_Config->fillRingSize, m_Config->completionRingSize);
return true;
}

bool XdpDevice::populateConfigDefaults(XdpDeviceConfiguration& config) const
{
uint16_t numFrames = config.umemNumFrames ? config.umemNumFrames : DEFAULT_UMEM_NUM_FRAMES;
uint16_t frameSize = config.umemFrameSize ? config.umemFrameSize : getpagesize();
uint16_t frameHeadroomSize = config.frameHeadroomSize ? config.frameHeadroomSize : DEFAULT_FRAME_HEADROOM_SIZE;
uint32_t fillRingSize = config.fillRingSize ? config.fillRingSize : DEFAULT_FILL_RING_SIZE;
uint32_t completionRingSize =
config.completionRingSize ? config.completionRingSize : DEFAULT_COMPLETION_RING_SIZE;
uint32_t rxSize = config.rxSize ? config.rxSize : XSK_RING_CONS__DEFAULT_NUM_DESCS;
uint32_t txSize = config.txSize ? config.txSize : XSK_RING_PROD__DEFAULT_NUM_DESCS;
uint32_t batchSize = config.rxTxBatchSize ? config.rxTxBatchSize : DEFAULT_BATCH_SIZE;
uint32_t qId = config.queueId; // default is zero

if (frameSize != getpagesize())
{
Expand All @@ -463,6 +470,12 @@ namespace pcpp
return false;
}

if (frameHeadroomSize > frameSize)
{
PCPP_LOG_ERROR("Frame headroom size must be less than the frame size");
return false;
}

if (fillRingSize > numFrames)
{
PCPP_LOG_ERROR("Fill ring size (" << fillRingSize
Expand Down Expand Up @@ -499,13 +512,23 @@ namespace pcpp
return false;
}

unsigned int nhwqueues = numOfHardwareQueues(m_InterfaceName);
if (qId >= nhwqueues)
{
PCPP_LOG_ERROR("Queue Id (" << qId << ") must be less than the number hardware queues (" << nhwqueues
<< ") of device");
return false;
}

config.umemNumFrames = numFrames;
config.umemFrameSize = frameSize;
config.frameHeadroomSize = frameHeadroomSize;
config.fillRingSize = fillRingSize;
config.completionRingSize = completionRingSize;
config.rxSize = rxSize;
config.txSize = txSize;
config.rxTxBatchSize = batchSize;
config.queueId = qId;

return true;
}
Expand Down Expand Up @@ -636,4 +659,36 @@ namespace pcpp
return m_Stats;
}

uint32_t XdpDevice::numOfHardwareQueues(const std::string& iface, bool tx)
{
// returns number of hardware queues associated with the device
uint32_t rxtxqueues = DEFAULT_NUM_QUEUES;
std::string prefix = tx ? "tx-" : "rx-";
std::string path = "/sys/class/net/" + iface + "/queues/";
DIR* dir = opendir(path.c_str());

if (dir)
{
std::regex rxtx_regex("^" + prefix + "[0-9]+$");

struct dirent* entry;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Curious why the struct dirent*? Can't we just do dirent*?

while ((entry = readdir(dir)) != nullptr)
{
if (std::regex_match(entry->d_name, rxtx_regex))
{
rxtxqueues++;
}
}

closedir(dir);
}

else
{
PCPP_LOG_ERROR("Error getting number of hardware queues from " << iface);
}

return rxtxqueues;
}

} // namespace pcpp
Loading