diff --git a/Pcap++/header/XdpDevice.h b/Pcap++/header/XdpDevice.h index effb4b07a7..93fe8e0c25 100644 --- a/Pcap++/header/XdpDevice.h +++ b/Pcap++/header/XdpDevice.h @@ -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 @@ -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; } }; @@ -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(); diff --git a/Pcap++/src/XdpDevice.cpp b/Pcap++/src/XdpDevice.cpp index 1f17cc4eea..d20b057053 100644 --- a/Pcap++/src/XdpDevice.cpp +++ b/Pcap++/src/XdpDevice.cpp @@ -10,9 +10,11 @@ #include #include #include +#include #include #include #include +#include #include namespace pcpp @@ -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; @@ -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; @@ -403,8 +407,8 @@ namespace pcpp auto umemInfo = static_cast(m_Umem->getInfo()); struct xsk_socket_config xskConfig; - xskConfig.rx_size = m_Config->txSize; - xskConfig.tx_size = m_Config->rxSize; + xskConfig.rx_size = m_Config->rxSize; + xskConfig.tx_size = m_Config->txSize; xskConfig.libbpf_flags = 0; xskConfig.xdp_flags = 0; xskConfig.bind_flags = 0; @@ -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); @@ -429,13 +433,14 @@ 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; } @@ -443,12 +448,14 @@ namespace pcpp { 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()) { @@ -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 @@ -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; } @@ -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; + 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