diff --git a/Pcap++/header/XdpDevice.h b/Pcap++/header/XdpDevice.h index effb4b07a7..866aa93255 100644 --- a/Pcap++/header/XdpDevice.h +++ b/Pcap++/header/XdpDevice.h @@ -11,6 +11,8 @@ /// @ namespace pcpp { +#define XDP_MAX_RXTX_QUEUES 16 + /// @class XdpDevice /// A class wrapping the main functionality of using AF_XDP (XSK) sockets /// which are optimized for high performance packet processing. @@ -76,6 +78,10 @@ namespace pcpp /// The max number of packets to be received or sent in one batch uint16_t rxTxBatchSize; + /// 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,10 +93,11 @@ 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] 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, uint32_t queueId = 0) { this->attachMode = attachMode; this->umemNumFrames = umemNumFrames; @@ -100,6 +107,7 @@ namespace pcpp this->rxSize = rxSize; this->txSize = txSize; this->rxTxBatchSize = rxTxBatchSize; + this->queueId = queueId; } }; @@ -238,6 +246,24 @@ 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 numQueues(const std::string& interfaceName, bool tx = false); + private: class XdpUmem { diff --git a/Pcap++/src/XdpDevice.cpp b/Pcap++/src/XdpDevice.cpp index 1f17cc4eea..2ec047b7fa 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 @@ -403,8 +405,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,7 +421,7 @@ 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, + int ret = xsk_socket__create(&socketInfo->xsk, m_InterfaceName.c_str(), m_Config->queueId, umemInfo->umem, &socketInfo->rx, &socketInfo->tx, &xskConfig); if (ret) { @@ -429,6 +431,7 @@ namespace pcpp } m_SocketInfo = socketInfo; + return true; } @@ -449,6 +452,7 @@ namespace pcpp 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()) { @@ -499,6 +503,13 @@ namespace pcpp return false; } + unsigned int nhwqueues = numQueues(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.fillRingSize = fillRingSize; @@ -506,6 +517,7 @@ namespace pcpp config.rxSize = rxSize; config.txSize = txSize; config.rxTxBatchSize = batchSize; + config.queueId = qId; return true; } @@ -636,4 +648,36 @@ namespace pcpp return m_Stats; } + uint32_t XdpDevice::numQueues(const std::string& iface, bool tx) + { + // returns number of hardware queues associated with the device + uint32_t rxtxqueues = 0; + 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