-/* Copyright (C) 2021 Open Information Security Foundation
+/* Copyright (C) 2021-2025 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
#include "util-dpdk.h"
#include "util-debug.h"
#include "util-dpdk-bonding.h"
+#include "util-dpdk-rss.h"
#ifdef HAVE_DPDK
-#define I40E_RSS_HKEY_LEN 52
+#if RTE_VERSION < RTE_VERSION_NUM(21, 0, 0, 0)
+#define I40E_RSS_HKEY_LEN 40
+#define I40E_RSS_HASH_FUNCTION RTE_ETH_HASH_FUNCTION_SYMMETRIC_TOEPLITZ
+#else
+#define I40E_RSS_HKEY_LEN 52
+#define I40E_RSS_HASH_FUNCTION RTE_ETH_HASH_FUNCTION_TOEPLITZ
+#endif // RTE_VERSION < RTE_VERSION_NUM(21, 0, 0, 0)
#if RTE_VERSION < RTE_VERSION_NUM(20, 0, 0, 0)
static int i40eDeviceEnableSymHash(
retval = rte_eth_dev_filter_supported(port_id, RTE_ETH_FILTER_HASH);
#pragma GCC diagnostic pop
if (retval < 0) {
- SCLogError("RTE_ETH_FILTER_HASH not supported on port: %s", port_name);
+ SCLogError("%s: RTE_ETH_FILTER_HASH not supported", port_name);
return retval;
}
#pragma GCC diagnostic pop
if (retval < 0) {
- SCLogError("Cannot set global hash configurations on port %s", port_name);
+ SCLogError("%s: cannot set global hash configurations", port_name);
return retval;
}
#pragma GCC diagnostic pop
if (ret < 0) {
- SCLogError("RTE_ETH_FILTER_HASH not supported on port: %s", port_name);
+ SCLogError("%s: RTE_ETH_FILTER_HASH not supported", port_name);
return ret;
}
#pragma GCC diagnostic pop
if (ret < 0) {
- SCLogError("Cannot set symmetric hash enable per port on port %s", port_name);
+ SCLogError("%s: cannot set symmetric hash enable per port", port_name);
return ret;
}
#else
-static int i40eDeviceSetRSSFlowQueues(
- int port_id, const char *port_name, struct rte_eth_rss_conf rss_conf, int nb_rx_queues)
-{
- struct rte_flow_action_rss rss_action_conf = { 0 };
- struct rte_flow_attr attr = { 0 };
- struct rte_flow_item pattern[] = { { 0 }, { 0 }, { 0 }, { 0 } };
- struct rte_flow_action action[] = { { 0 }, { 0 } };
- struct rte_flow *flow;
- struct rte_flow_error flow_error = { 0 };
- uint16_t queues[RTE_MAX_QUEUES_PER_PORT];
-
- for (int i = 0; i < nb_rx_queues; ++i)
- queues[i] = i;
-
- rss_action_conf.func = RTE_ETH_HASH_FUNCTION_DEFAULT;
- rss_action_conf.level = 0;
- rss_action_conf.types = 0; // queues region can not be configured with types
- rss_action_conf.key_len = 0;
- rss_action_conf.key = NULL;
-
- if (nb_rx_queues < 1) {
- FatalError("The number of queues for RSS configuration must be "
- "configured with a positive number");
- }
-
- rss_action_conf.queue_num = nb_rx_queues;
- rss_action_conf.queue = queues;
-
- attr.ingress = 1;
- pattern[0].type = RTE_FLOW_ITEM_TYPE_END;
- action[0].type = RTE_FLOW_ACTION_TYPE_RSS;
- action[0].conf = &rss_action_conf;
- action[1].type = RTE_FLOW_ACTION_TYPE_END;
-
- flow = rte_flow_create(port_id, &attr, pattern, action, &flow_error);
- if (flow == NULL) {
- SCLogError("Error when creating rte_flow rule on %s: %s", port_name, flow_error.message);
- int ret = rte_flow_validate(port_id, &attr, pattern, action, &flow_error);
- SCLogError("Error on rte_flow validation for port %s: %s errmsg: %s", port_name,
- rte_strerror(-ret), flow_error.message);
- return ret;
- } else {
- SCLogInfo("RTE_FLOW queue region created for port %s", port_name);
- }
- return 0;
-}
-
-static int i40eDeviceCreateRSSFlow(int port_id, const char *port_name,
- struct rte_eth_rss_conf rss_conf, uint64_t rss_type, struct rte_flow_item *pattern)
-{
- struct rte_flow_action_rss rss_action_conf = { 0 };
- struct rte_flow_attr attr = { 0 };
- struct rte_flow_action action[] = { { 0 }, { 0 } };
- struct rte_flow *flow;
- struct rte_flow_error flow_error = { 0 };
-
- rss_action_conf.func = RTE_ETH_HASH_FUNCTION_SYMMETRIC_TOEPLITZ;
- rss_action_conf.level = 0;
- rss_action_conf.types = rss_type;
- rss_action_conf.key_len = rss_conf.rss_key_len;
- rss_action_conf.key = rss_conf.rss_key;
- rss_action_conf.queue_num = 0;
- rss_action_conf.queue = NULL;
-
- attr.ingress = 1;
- action[0].type = RTE_FLOW_ACTION_TYPE_RSS;
- action[0].conf = &rss_action_conf;
- action[1].type = RTE_FLOW_ACTION_TYPE_END;
-
- flow = rte_flow_create(port_id, &attr, pattern, action, &flow_error);
- if (flow == NULL) {
- SCLogError("Error when creating rte_flow rule on %s: %s", port_name, flow_error.message);
- int ret = rte_flow_validate(port_id, &attr, pattern, action, &flow_error);
- SCLogError("Error on rte_flow validation for port %s: %s errmsg: %s", port_name,
- rte_strerror(-ret), flow_error.message);
- return ret;
- } else {
- SCLogInfo("RTE_FLOW flow rule created for port %s", port_name);
- }
-
- return 0;
-}
-
static int i40eDeviceSetRSSFlowIPv4(
- int port_id, const char *port_name, struct rte_eth_rss_conf rss_conf)
+ int port_id, const char *port_name, struct rte_flow_action_rss rss_conf)
{
int ret = 0;
struct rte_flow_item pattern[] = { { 0 }, { 0 }, { 0 }, { 0 } };
pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV4;
pattern[2].type = RTE_FLOW_ITEM_TYPE_END;
- ret |= i40eDeviceCreateRSSFlow(
- port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV4_OTHER, pattern);
+ ret |= DPDKCreateRSSFlow(port_id, port_name, rss_conf,
+ RTE_ETH_RSS_NONFRAG_IPV4_OTHER | RTE_ETH_RSS_L3_SRC_ONLY | RTE_ETH_RSS_L3_DST_ONLY,
+ pattern);
memset(pattern, 0, sizeof(pattern));
pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV4;
pattern[2].type = RTE_FLOW_ITEM_TYPE_UDP;
pattern[3].type = RTE_FLOW_ITEM_TYPE_END;
- ret |= i40eDeviceCreateRSSFlow(
- port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV4_UDP, pattern);
+ ret |= DPDKCreateRSSFlow(port_id, port_name, rss_conf,
+ RTE_ETH_RSS_NONFRAG_IPV4_UDP | RTE_ETH_RSS_L3_SRC_ONLY | RTE_ETH_RSS_L3_DST_ONLY,
+ pattern);
memset(pattern, 0, sizeof(pattern));
pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV4;
pattern[2].type = RTE_FLOW_ITEM_TYPE_TCP;
pattern[3].type = RTE_FLOW_ITEM_TYPE_END;
- ret |= i40eDeviceCreateRSSFlow(
- port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV4_TCP, pattern);
+ ret |= DPDKCreateRSSFlow(port_id, port_name, rss_conf,
+ RTE_ETH_RSS_NONFRAG_IPV4_TCP | RTE_ETH_RSS_L3_SRC_ONLY | RTE_ETH_RSS_L3_DST_ONLY,
+ pattern);
memset(pattern, 0, sizeof(pattern));
pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV4;
pattern[2].type = RTE_FLOW_ITEM_TYPE_SCTP;
pattern[3].type = RTE_FLOW_ITEM_TYPE_END;
- ret |= i40eDeviceCreateRSSFlow(
- port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV4_SCTP, pattern);
+ ret |= DPDKCreateRSSFlow(port_id, port_name, rss_conf,
+ RTE_ETH_RSS_NONFRAG_IPV4_SCTP | RTE_ETH_RSS_L3_SRC_ONLY | RTE_ETH_RSS_L3_DST_ONLY,
+ pattern);
memset(pattern, 0, sizeof(pattern));
- pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
- pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV4;
- pattern[2].type = RTE_FLOW_ITEM_TYPE_END;
- ret |= i40eDeviceCreateRSSFlow(port_id, port_name, rss_conf, RTE_ETH_RSS_FRAG_IPV4, pattern);
-
return ret;
}
static int i40eDeviceSetRSSFlowIPv6(
- int port_id, const char *port_name, struct rte_eth_rss_conf rss_conf)
+ int port_id, const char *port_name, struct rte_flow_action_rss rss_conf)
{
int ret = 0;
struct rte_flow_item pattern[] = { { 0 }, { 0 }, { 0 }, { 0 } };
pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV6;
pattern[2].type = RTE_FLOW_ITEM_TYPE_END;
- ret |= i40eDeviceCreateRSSFlow(
- port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV6_OTHER, pattern);
+ ret |= DPDKCreateRSSFlow(port_id, port_name, rss_conf,
+ RTE_ETH_RSS_NONFRAG_IPV6_OTHER | RTE_ETH_RSS_L3_SRC_ONLY | RTE_ETH_RSS_L3_DST_ONLY,
+ pattern);
memset(pattern, 0, sizeof(pattern));
pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV6;
pattern[2].type = RTE_FLOW_ITEM_TYPE_UDP;
pattern[3].type = RTE_FLOW_ITEM_TYPE_END;
- ret |= i40eDeviceCreateRSSFlow(
- port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV6_UDP, pattern);
+ ret |= DPDKCreateRSSFlow(port_id, port_name, rss_conf,
+ RTE_ETH_RSS_NONFRAG_IPV6_UDP | RTE_ETH_RSS_L3_SRC_ONLY | RTE_ETH_RSS_L3_DST_ONLY,
+ pattern);
memset(pattern, 0, sizeof(pattern));
pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV6;
pattern[2].type = RTE_FLOW_ITEM_TYPE_TCP;
pattern[3].type = RTE_FLOW_ITEM_TYPE_END;
- ret |= i40eDeviceCreateRSSFlow(
- port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV6_TCP, pattern);
+ ret |= DPDKCreateRSSFlow(port_id, port_name, rss_conf,
+ RTE_ETH_RSS_NONFRAG_IPV6_TCP | RTE_ETH_RSS_L3_SRC_ONLY | RTE_ETH_RSS_L3_DST_ONLY,
+ pattern);
memset(pattern, 0, sizeof(pattern));
pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV6;
pattern[2].type = RTE_FLOW_ITEM_TYPE_SCTP;
pattern[3].type = RTE_FLOW_ITEM_TYPE_END;
- ret |= i40eDeviceCreateRSSFlow(
- port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV6_SCTP, pattern);
+ ret |= DPDKCreateRSSFlow(port_id, port_name, rss_conf,
+ RTE_ETH_RSS_NONFRAG_IPV6_SCTP | RTE_ETH_RSS_L3_SRC_ONLY | RTE_ETH_RSS_L3_DST_ONLY,
+ pattern);
memset(pattern, 0, sizeof(pattern));
- pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
- pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV6;
- pattern[2].type = RTE_FLOW_ITEM_TYPE_END;
- ret |= i40eDeviceCreateRSSFlow(port_id, port_name, rss_conf, RTE_ETH_RSS_FRAG_IPV6, pattern);
-
return ret;
}
static int i40eDeviceSetRSSWithFlows(int port_id, const char *port_name, int nb_rx_queues)
{
- int retval;
- uint8_t rss_key[I40E_RSS_HKEY_LEN];
+ uint16_t queues[RTE_MAX_QUEUES_PER_PORT];
struct rte_flow_error flush_error = { 0 };
struct rte_eth_rss_conf rss_conf = {
- .rss_key = rss_key,
+ .rss_key = RSS_HKEY,
.rss_key_len = I40E_RSS_HKEY_LEN,
};
- retval = rte_eth_dev_rss_hash_conf_get(port_id, &rss_conf);
- if (retval != 0) {
- SCLogError("Unable to get RSS hash configuration of port %s", port_name);
- return retval;
+ if (nb_rx_queues < 1) {
+ FatalError("The number of queues for RSS configuration must be "
+ "configured with a positive number");
}
- retval = 0;
- retval |= i40eDeviceSetRSSFlowQueues(port_id, port_name, rss_conf, nb_rx_queues);
- retval |= i40eDeviceSetRSSFlowIPv4(port_id, port_name, rss_conf);
- retval |= i40eDeviceSetRSSFlowIPv6(port_id, port_name, rss_conf);
+ struct rte_flow_action_rss rss_action_conf =
+ DPDKInitRSSAction(rss_conf, nb_rx_queues, queues, RTE_ETH_HASH_FUNCTION_DEFAULT, false);
+
+ int retval = DPDKSetRSSFlowQueues(port_id, port_name, rss_action_conf);
+
+ memset(&rss_action_conf, 0, sizeof(struct rte_flow_action_rss));
+ rss_action_conf = DPDKInitRSSAction(rss_conf, 0, queues, I40E_RSS_HASH_FUNCTION, true);
+
+ retval |= i40eDeviceSetRSSFlowIPv4(port_id, port_name, rss_action_conf);
+ retval |= i40eDeviceSetRSSFlowIPv6(port_id, port_name, rss_action_conf);
if (retval != 0) {
retval = rte_flow_flush(port_id, &flush_error);
if (retval != 0) {
- SCLogError("Unable to flush rte_flow rules of %s: %s Flush error msg: %s", port_name,
+ SCLogError("%s: unable to flush rte_flow rules: %s Flush error msg: %s", port_name,
rte_strerror(-retval), flush_error.message);
}
return retval;
#endif /* RTE_VERSION < RTE_VERSION_NUM(20,0,0,0) */
-int i40eDeviceSetRSS(int port_id, int nb_rx_queues)
+int i40eDeviceSetRSS(int port_id, int nb_rx_queues, char *port_name)
{
- int retval;
(void)nb_rx_queues; // avoid unused variable warnings
- char port_name[RTE_ETH_NAME_MAX_LEN];
-
- retval = rte_eth_dev_get_name_by_port(port_id, port_name);
- if (unlikely(retval != 0)) {
- SCLogError("Failed to convert port id %d to the interface name: %s", port_id,
- strerror(-retval));
- return retval;
- }
#if RTE_VERSION >= RTE_VERSION_NUM(20, 0, 0, 0)
i40eDeviceSetRSSWithFlows(port_id, port_name, nb_rx_queues);
--- /dev/null
+/* Copyright (C) 2025 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \defgroup dpdk DPDK rte_flow RSS helpers functions
+ *
+ * @{
+ */
+
+/**
+ * \file
+ *
+ * \author Adam Kiripolsky <adam.kiripolsky@cesnet.cz>
+ *
+ * DPDK rte_flow RSS helper functions
+ *
+ */
+
+#include "util-dpdk-rss.h"
+#include "util-dpdk.h"
+#include "util-debug.h"
+
+#ifdef HAVE_DPDK
+
+uint8_t RSS_HKEY[] = {
+ 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A,
+ 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A,
+ 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, // 40
+ 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, // 52
+};
+
+/**
+ * \brief Initialize RSS action configuration for
+ * RTE_FLOW RSS rule based on input arguments
+ *
+ * \param rss_conf RSS configuration
+ * \param nb_rx_queues number of rx queues
+ * \param queues array of queue indexes
+ * \param func RSS hash function
+ * \param set_key flag to set RSS hash key and its length
+ * \return struct rte_flow_action_rss RSS action configuration
+ * to be used in a rule
+ */
+struct rte_flow_action_rss DPDKInitRSSAction(struct rte_eth_rss_conf rss_conf, int nb_rx_queues,
+ uint16_t *queues, enum rte_eth_hash_function func, bool set_key)
+{
+ struct rte_flow_action_rss rss_action_conf = { 0 };
+ rss_action_conf.func = func;
+ rss_action_conf.level = 0;
+
+ if (set_key) {
+ rss_action_conf.key = rss_conf.rss_key;
+ rss_action_conf.key_len = rss_conf.rss_key_len;
+ } else {
+ rss_action_conf.key = NULL;
+ rss_action_conf.key_len = 0;
+ }
+
+ if (nb_rx_queues != 0) {
+ for (int i = 0; i < nb_rx_queues; ++i)
+ queues[i] = i;
+
+ rss_action_conf.queue = queues;
+ } else {
+ rss_action_conf.queue = NULL;
+ }
+ rss_action_conf.queue_num = nb_rx_queues;
+
+ return rss_action_conf;
+}
+
+/**
+ * \brief Create RTE_FLOW RSS rule configured with pattern and rss_type
+ * but with no rx_queues configured. This is specific way of setting RTE_FLOW RSS rule
+ * for some drivers (mostly Intel NICs). This function's call must be preceded by
+ * call to function DeviceSetRSSFlowQueues().
+ *
+ * \param port_id The port identifier of the Ethernet device
+ * \param port_name The port name of the Ethernet device
+ * \param rss_conf RSS configuration
+ * \param rss_type RSS hash type - only this type is used when creating hash with RSS hash function
+ * \param pattern pattern to match incoming traffic
+ * \return int 0 on success, a negative errno value otherwise
+ */
+int DPDKCreateRSSFlow(int port_id, const char *port_name, struct rte_flow_action_rss rss_conf,
+ uint64_t rss_type, struct rte_flow_item *pattern)
+{
+ struct rte_flow_attr attr = { 0 };
+ struct rte_flow_action action[] = { { 0 }, { 0 } };
+ struct rte_flow_error flow_error = { 0 };
+
+ rss_conf.types = rss_type;
+
+ attr.ingress = 1;
+ action[0].type = RTE_FLOW_ACTION_TYPE_RSS;
+ action[0].conf = &rss_conf;
+ action[1].type = RTE_FLOW_ACTION_TYPE_END;
+
+ struct rte_flow *flow = rte_flow_create(port_id, &attr, pattern, action, &flow_error);
+ if (flow == NULL) {
+ SCLogError("%s: rte_flow rule creation error: %s", port_name, flow_error.message);
+ int ret = rte_flow_validate(port_id, &attr, pattern, action, &flow_error);
+ SCLogError("%s: rte_flow rule validation error: %s, errmsg: %s", port_name,
+ rte_strerror(-ret), flow_error.message);
+ return ret;
+ } else {
+ SCLogDebug("%s: rte_flow rule created", port_name);
+ }
+
+ return 0;
+}
+
+/**
+ * \brief Some drivers (mostly Intel NICs) require specific way of setting RTE_FLOW RSS rules
+ * with one rule that sets up only queues and other rules that specify patterns to match with
+ * queues configured (created with function DeviceCreateRSSFlow() that should follow after
+ * this function's call).
+ *
+ * \param port_id The port identifier of the Ethernet device
+ * \param port_name The port name of the Ethernet device
+ * \param rss_conf RSS configuration
+ * \return int 0 on success, a negative errno value otherwise
+ */
+int DPDKSetRSSFlowQueues(int port_id, const char *port_name, struct rte_flow_action_rss rss_conf)
+{
+ struct rte_flow_attr attr = { 0 };
+ struct rte_flow_item pattern[] = { { 0 } };
+ struct rte_flow_action action[] = { { 0 }, { 0 } };
+ struct rte_flow_error flow_error = { 0 };
+
+ rss_conf.types = 0; // queues region can not be configured with types
+
+ attr.ingress = 1;
+ pattern[0].type = RTE_FLOW_ITEM_TYPE_END;
+ action[0].type = RTE_FLOW_ACTION_TYPE_RSS;
+ action[0].conf = &rss_conf;
+ action[1].type = RTE_FLOW_ACTION_TYPE_END;
+
+ struct rte_flow *flow = rte_flow_create(port_id, &attr, pattern, action, &flow_error);
+ if (flow == NULL) {
+ SCLogError("%s: rte_flow rule creation error: %s", port_name, flow_error.message);
+ int ret = rte_flow_validate(port_id, &attr, pattern, action, &flow_error);
+ SCLogError("%s: rte_flow rule validation error: %s, errmsg: %s", port_name,
+ rte_strerror(-ret), flow_error.message);
+ return ret;
+ } else {
+ SCLogDebug("%s: rte_flow rule created", port_name);
+ }
+ return 0;
+}
+
+#endif /* HAVE_DPDK */
+/**
+ * @}
+ */