dpdk/rss: move and change rss rte_flow functions

Move and adjust the base of  RSS configuration from util-dpdk-i40e.c to
a new file that can be later utilized by other cards.

RSS configuration can be configured via rte_flow rules. This is useful
for possible future features such as specific header offload
(vxlan, nvgre) also implemented via rte_flow rules, as rte_flow
rules can be chained via groups and priorities.

i40e uses multiple different rte_flow rules to setup RSS. At first,
function DeviceSetRSSFlowQueues() is used to setup rx queues.
This rule matches all types of traffic, so the equivalent
to dpdk-testpmd pattern would be "pattern end"
This rule can not contain hash types (ipv4, ipv6 etc.) nor hash key.
The hash function used here is RTE_ETH_HASH_FUNCTION_DEFAULT.

The syntax in dpdk-testpmd for this rule with attributes:
	port index == 0
	used rx queue indices == 0 1 2 3
is as follows:
"flow create 0 ingress pattern end actions rss queues 0 1 2 3 end
func default / end"

The other rules configured by i40eDeviceSetRSSFlowIPv4() and
i40eDeviceSetRSSFlowIPv6() match specific type of traffic by l4 protocol
(none, TCP, UDP, SCTP). For example, pattern to match l3 ipv4 with l4
tcp traffic in dpdk-testpmd syntax would be equivalent of
"pattern eth / ipv4 / tcp / end".
These rules can not have rx queues configured, but have hash types
(l3 src and dst address). This means that the traffic distribution
is affected only by l3 addresses, independent of the l4 specifics.

Also these pattern matching rules have symmetric 6d5a
hash key configured. The length of the key is dependent on DPDK version.
The hash function (either RTE_ETH_HASH_FUNCTION_SYMMETRIC_TOEPLITZ or
RTE_ETH_HASH_FUNCTION_TOEPLITZ, depending on DPKD version) used
in these rules hashes symmetricaly due to the symmetric hash key.

The syntax in dpdk-testpmd for rule to match ipv4-tcp traffic with
attributes:
	port index == 0
	<hash_key> == 52 bytes long 6d5a symmetric hash key
is as follows:
"flow create 0 ingress pattern eth / ipv4 / tcp / end actions rss types
ipv4-tcp l3-src-only l3-dst-only end queues end key <hash_key>
key_len 52 func toeplitz / end"
(queues need to be set to NULL)

Ticket: 7337
pull/12553/head
Adam Kiripolsky 8 months ago committed by Victor Julien
parent e47f4f997d
commit ffe0cf88e4

@ -483,6 +483,7 @@ noinst_HEADERS = \
util-dpdk-ice.h \
util-dpdk-ixgbe.h \
util-dpdk-bonding.h \
util-dpdk-rss.h \
util-ebpf.h \
util-enum.h \
util-error.h \
@ -1037,6 +1038,7 @@ libsuricata_c_a_SOURCES = \
util-dpdk-ice.c \
util-dpdk-ixgbe.c \
util-dpdk-bonding.c \
util-dpdk-rss.c \
util-ebpf.c \
util-enum.c \
util-error.c \

@ -1,4 +1,4 @@
/* 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
@ -41,10 +41,11 @@
#include "util-debug.h"
#include "util-device.h"
#include "util-dpdk.h"
#include "util-dpdk-bonding.h"
#include "util-dpdk-i40e.h"
#include "util-dpdk-ice.h"
#include "util-dpdk-ixgbe.h"
#include "util-dpdk-bonding.h"
#include "util-dpdk-rss.h"
#include "util-time.h"
#include "util-conf.h"
#include "suricata.h"
@ -52,15 +53,6 @@
#ifdef HAVE_DPDK
#define RSS_HKEY_LEN 40
// General purpose RSS key for symmetric bidirectional flow distribution
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
};
// Calculates the closest multiple of y from x
#define ROUNDUP(x, y) ((((x) + ((y)-1)) / (y)) * (y))
@ -883,7 +875,6 @@ static DPDKIfaceConfig *ConfigParse(const char *iface)
static void DeviceSetPMDSpecificRSS(struct rte_eth_rss_conf *rss_conf, const char *driver_name)
{
// RSS is configured in a specific way for a driver i40e and DPDK version <= 19.xx
if (strcmp(driver_name, "net_i40e") == 0)
i40eDeviceSetRSSConf(rss_conf);
if (strcmp(driver_name, "net_ice") == 0)
@ -1157,7 +1148,7 @@ static void PortConfSetRSSConf(const DPDKIfaceConfig *iconf,
if (iconf->nb_rx_queues > 1) {
SCLogConfig("%s: RSS enabled for %d queues", iconf->iface, iconf->nb_rx_queues);
port_conf->rx_adv_conf.rss_conf = (struct rte_eth_rss_conf){
.rss_key = rss_hkey,
.rss_key = RSS_HKEY,
.rss_key_len = RSS_HKEY_LEN,
.rss_hf = iconf->rss_hf,
};

@ -1,4 +1,4 @@
/* 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
@ -88,6 +88,8 @@ TmEcode NoDPDKSupportExit(ThreadVars *tv, const void *initdata, void **data)
#include "util-affinity.h"
#include "util-dpdk.h"
#include "util-dpdk-i40e.h"
#include "util-dpdk-ice.h"
#include "util-dpdk-ixgbe.h"
#include "util-dpdk-bonding.h"
#include <numa.h>
@ -190,14 +192,11 @@ static inline void DPDKFreeMbufArray(
static void DevicePostStartPMDSpecificActions(DPDKThreadVars *ptv, const char *driver_name)
{
if (strcmp(driver_name, "net_bonding") == 0) {
if (strcmp(driver_name, "net_bonding") == 0)
driver_name = BondingDeviceDriverGet(ptv->port_id);
}
// The PMD Driver i40e has a special way to set the RSS, it can be set via rte_flow rules
// and only after the start of the port
if (strcmp(driver_name, "net_i40e") == 0)
i40eDeviceSetRSS(ptv->port_id, ptv->threads);
i40eDeviceSetRSS(ptv->port_id, ptv->threads, ptv->livedev->dev);
}
static void DevicePreClosePMDSpecificActions(DPDKThreadVars *ptv, const char *driver_name)
@ -207,7 +206,7 @@ static void DevicePreClosePMDSpecificActions(DPDKThreadVars *ptv, const char *dr
}
if (strcmp(driver_name, "net_i40e") == 0) {
#if RTE_VERSION > RTE_VERSION_NUM(20, 0, 0, 0)
#if RTE_VERSION >= RTE_VERSION_NUM(20, 0, 0, 0)
// Flush the RSS rules that have been inserted in the post start section
struct rte_flow_error flush_error = { 0 };
int32_t retval = rte_flow_flush(ptv->port_id, &flush_error);
@ -215,7 +214,7 @@ static void DevicePreClosePMDSpecificActions(DPDKThreadVars *ptv, const char *dr
SCLogError("%s: unable to flush rte_flow rules: %s Flush error msg: %s",
ptv->livedev->dev, rte_strerror(-retval), flush_error.message);
}
#endif /* RTE_VERSION > RTE_VERSION_NUM(20, 0, 0, 0) */
#endif /* RTE_VERSION >= RTE_VERSION_NUM(20, 0, 0, 0) */
}
}

@ -1,4 +1,4 @@
/* 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
@ -34,10 +34,17 @@
#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(
@ -54,7 +61,7 @@ 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;
}
@ -72,7 +79,7 @@ static int i40eDeviceEnableSymHash(
#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;
}
@ -92,7 +99,7 @@ static int i40eDeviceSetSymHash(int port_id, const char *port_name, int enable)
#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;
}
@ -104,7 +111,7 @@ static int i40eDeviceSetSymHash(int port_id, const char *port_name, int enable)
#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;
}
@ -166,91 +173,8 @@ static int32_t i40eDeviceSetRSSWithFilter(int port_id, const char *port_name)
#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 } };
@ -258,44 +182,43 @@ static int i40eDeviceSetRSSFlowIPv4(
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 } };
@ -303,66 +226,69 @@ static int i40eDeviceSetRSSFlowIPv6(
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;
@ -373,18 +299,9 @@ static int i40eDeviceSetRSSWithFlows(int port_id, const char *port_name, int nb_
#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);

@ -1,4 +1,4 @@
/* 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
@ -30,7 +30,7 @@
#include "util-dpdk.h"
int i40eDeviceSetRSS(int port_id, int nb_rx_queues);
int i40eDeviceSetRSS(int port_id, int nb_rx_queues, char *port_name);
void i40eDeviceSetRSSConf(struct rte_eth_rss_conf *rss_conf);
#endif /* HAVE_DPDK */

@ -0,0 +1,169 @@
/* 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 */
/**
* @}
*/

@ -0,0 +1,47 @@
/* 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.
*/
/**
* \file
*
* \author Adam Kiripolsky <adam.kiripolsky@cesnet.cz>
*/
#ifndef UTIL_DPDK_RSS
#define UTIL_DPDK_RSS
#include "suricata-common.h"
#ifdef HAVE_DPDK
#include "util-dpdk.h"
#define RSS_HKEY_LEN 40
extern uint8_t RSS_HKEY[];
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);
int DeviceCreateRSSFlowGeneric(
int port_id, const char *port_name, struct rte_flow_action_rss rss_conf);
int DPDKSetRSSFlowQueues(int port_id, const char *port_name, struct rte_flow_action_rss rss_conf);
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);
#endif /* HAVE_DPDK */
#endif /* UTIL_DPDK_RSS */
Loading…
Cancel
Save