diff --git a/src/Makefile.am b/src/Makefile.am index 615816953542..818a398f4f05 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -480,7 +480,9 @@ noinst_HEADERS = \ util-dpdk-i40e.h \ util-dpdk-ice.h \ util-dpdk-ixgbe.h \ + util-dpdk-mlx5.h \ util-dpdk-bonding.h \ + util-dpdk-rss.h \ util-ebpf.h \ util-enum.h \ util-error.h \ @@ -1030,7 +1032,9 @@ libsuricata_c_a_SOURCES = \ util-dpdk-i40e.c \ util-dpdk-ice.c \ util-dpdk-ixgbe.c \ + util-dpdk-mlx5.c \ util-dpdk-bonding.c \ + util-dpdk-rss.c \ util-ebpf.c \ util-enum.c \ util-error.c \ diff --git a/src/runmode-dpdk.c b/src/runmode-dpdk.c index 5df81f685883..cf6141209883 100644 --- a/src/runmode-dpdk.c +++ b/src/runmode-dpdk.c @@ -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, }; diff --git a/src/source-dpdk.c b/src/source-dpdk.c index 1453b3ba7a13..62953e9382b7 100644 --- a/src/source-dpdk.c +++ b/src/source-dpdk.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Open Information Security Foundation +/* Copyright (C) 2021-2024 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,9 @@ 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-mlx5.h" #include "util-dpdk-bonding.h" #include @@ -190,14 +193,16 @@ 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); + else if (strcmp(driver_name, "net_ixgbe") == 0) + ixgbeDeviceSetRSS(ptv->port_id, ptv->threads, ptv->livedev->dev); + else if (strcmp(driver_name, "net_ice") == 0) + iceDeviceSetRSS(ptv->port_id, ptv->threads, ptv->livedev->dev); + else if (strcmp(driver_name, "mlx5_pci") == 0) + mlx5DeviceSetRSS(ptv->port_id, ptv->threads, ptv->livedev->dev); } static void DevicePreClosePMDSpecificActions(DPDKThreadVars *ptv, const char *driver_name) @@ -207,7 +212,19 @@ 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); + if (retval != 0) { + 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) */ + } + + if (strcmp(driver_name, "net_ixgbe") == 0 || strcmp(driver_name, "net_ice") == 0 || + strcmp(driver_name, "mlx5_pci") == 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 +232,6 @@ 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) */ } } diff --git a/src/util-dpdk-i40e.c b/src/util-dpdk-i40e.c index 2484b45c793b..38e063789f52 100644 --- a/src/util-dpdk-i40e.c +++ b/src/util-dpdk-i40e.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Open Information Security Foundation +/* Copyright (C) 2021-2024 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,203 +173,163 @@ 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 } }; +#if RTE_VERSION < RTE_VERSION_NUM(21, 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, 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, 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, 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, pattern); memset(pattern, 0, sizeof(pattern)); - +#else 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); + ret |= DPDKCreateRSSFlow(port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV4_OTHER, 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 |= DPDKCreateRSSFlow(port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV4_UDP, 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 |= DPDKCreateRSSFlow(port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV4_TCP, 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 |= DPDKCreateRSSFlow(port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV4_SCTP, pattern); + memset(pattern, 0, sizeof(pattern)); +#endif // RTE_VERSION < RTE_VERSION_NUM(21, 0, 0, 0) 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 } }; - +#if RTE_VERSION < RTE_VERSION_NUM(21, 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, 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, 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, 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, pattern); memset(pattern, 0, sizeof(pattern)); - +#else 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); + ret |= DPDKCreateRSSFlow(port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV6_OTHER, 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 |= DPDKCreateRSSFlow(port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV6_UDP, 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 |= DPDKCreateRSSFlow(port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV6_TCP, 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 |= DPDKCreateRSSFlow(port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV6_SCTP, pattern); + memset(pattern, 0, sizeof(pattern)); +#endif // RTE_VERSION < RTE_VERSION_NUM(21, 0, 0, 0) 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 +340,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); diff --git a/src/util-dpdk-i40e.h b/src/util-dpdk-i40e.h index 6133aed5d771..964d68074752 100644 --- a/src/util-dpdk-i40e.h +++ b/src/util-dpdk-i40e.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Open Information Security Foundation +/* Copyright (C) 2021-2024 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 */ diff --git a/src/util-dpdk-ice.c b/src/util-dpdk-ice.c index 8cd6b777e02f..ab90cb0240a4 100644 --- a/src/util-dpdk-ice.c +++ b/src/util-dpdk-ice.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Open Information Security Foundation +/* Copyright (C) 2021-2024 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 @@ -32,6 +32,9 @@ #include "util-dpdk-ice.h" #include "util-dpdk.h" +#include "util-dpdk-rss.h" +#include "util-debug.h" +#include "util-dpdk-bonding.h" #ifdef HAVE_DPDK @@ -46,6 +49,75 @@ static void iceDeviceSetRSSHashFunction(uint64_t *rss_hf) #endif } +/** + * \brief Creates RTE_FLOW pattern to match ipv4 traffic + * + * \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 + */ +static int iceDeviceSetRSSFlowIPv4( + int port_id, const char *port_name, struct rte_flow_action_rss rss_conf) +{ + struct rte_flow_item pattern[] = { { 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; + + return DPDKCreateRSSFlow(port_id, port_name, rss_conf, RTE_ETH_RSS_IPV4, pattern); +} + +/** + * \brief Creates RTE_FLOW pattern to match ipv6 traffic + * + * \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 + */ + +static int iceDeviceSetRSSFlowIPv6( + int port_id, const char *port_name, struct rte_flow_action_rss rss_conf) +{ + struct rte_flow_item pattern[] = { { 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; + + return DPDKCreateRSSFlow(port_id, port_name, rss_conf, RTE_ETH_RSS_IPV6, pattern); +} + +int iceDeviceSetRSS(int port_id, int nb_rx_queues, char *port_name) +{ + uint16_t queues[RTE_MAX_QUEUES_PER_PORT]; + struct rte_flow_error flush_error = { 0 }; + struct rte_eth_rss_conf rss_conf = { 0 }; + + if (nb_rx_queues < 1) { + FatalError("The number of queues for RSS configuration must be " + "configured with a positive number"); + } + + struct rte_flow_action_rss rss_action_conf = + DPDKInitRSSAction(rss_conf, 0, queues, RTE_ETH_HASH_FUNCTION_TOEPLITZ, false); + + int retval = iceDeviceSetRSSFlowIPv4(port_id, port_name, rss_action_conf); + retval |= iceDeviceSetRSSFlowIPv6(port_id, port_name, rss_action_conf); + if (retval != 0) { + retval = rte_flow_flush(port_id, &flush_error); + if (retval != 0) { + SCLogError("%s: unable to flush rte_flow rules: %s Flush error msg: %s", port_name, + rte_strerror(-retval), flush_error.message); + } + return retval; + } + + return 0; +} + void iceDeviceSetRSSConf(struct rte_eth_rss_conf *rss_conf) { iceDeviceSetRSSHashFunction(&rss_conf->rss_hf); diff --git a/src/util-dpdk-ice.h b/src/util-dpdk-ice.h index d535fa092f9f..1cf36bfecb7d 100644 --- a/src/util-dpdk-ice.h +++ b/src/util-dpdk-ice.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Open Information Security Foundation +/* Copyright (C) 2021-2024 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,6 +30,7 @@ #include "util-dpdk.h" +int iceDeviceSetRSS(int port_id, int nb_rx_queues, char *port_name); void iceDeviceSetRSSConf(struct rte_eth_rss_conf *rss_conf); #endif /* HAVE_DPDK */ diff --git a/src/util-dpdk-ixgbe.c b/src/util-dpdk-ixgbe.c index 5627c45270d1..09f750274e81 100644 --- a/src/util-dpdk-ixgbe.c +++ b/src/util-dpdk-ixgbe.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Open Information Security Foundation +/* Copyright (C) 2021-2024 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 @@ -32,14 +32,49 @@ #include "util-dpdk-ixgbe.h" #include "util-dpdk.h" +#include "util-debug.h" +#include "util-dpdk-bonding.h" +#include "util-dpdk-rss.h" #ifdef HAVE_DPDK +#define IXGBE_RSS_HKEY_LEN 40 + void ixgbeDeviceSetRSSHashFunction(uint64_t *rss_hf) { *rss_hf = RTE_ETH_RSS_IPV4 | RTE_ETH_RSS_IPV6 | RTE_ETH_RSS_IPV6_EX; } +int ixgbeDeviceSetRSS(int port_id, int nb_rx_queues, char *port_name) +{ + 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_HKEY, + .rss_key_len = IXGBE_RSS_HKEY_LEN, + }; + + if (nb_rx_queues < 1) { + FatalError("The number of queues for RSS configuration must be " + "configured with a positive number"); + } + + struct rte_flow_action_rss rss_action_conf = + DPDKInitRSSAction(rss_conf, nb_rx_queues, queues, RTE_ETH_HASH_FUNCTION_DEFAULT, true); + + int retval = DPDKCreateRSSFlowGeneric(port_id, port_name, rss_action_conf); + if (retval != 0) { + retval = rte_flow_flush(port_id, &flush_error); + if (retval != 0) { + SCLogError("%s: unable to flush rte_flow rules: %s Flush error msg: %s", port_name, + rte_strerror(-retval), flush_error.message); + } + return retval; + } + + return 0; +} + #endif /* HAVE_DPDK */ /** * @} diff --git a/src/util-dpdk-ixgbe.h b/src/util-dpdk-ixgbe.h index 3cb18fa55d75..79b7d5edb87c 100644 --- a/src/util-dpdk-ixgbe.h +++ b/src/util-dpdk-ixgbe.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Open Information Security Foundation +/* Copyright (C) 2021-2024 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 @@ -28,6 +28,7 @@ #ifdef HAVE_DPDK +int ixgbeDeviceSetRSS(int port_id, int nb_rx_queues, char *port_name); void ixgbeDeviceSetRSSHashFunction(uint64_t *rss_conf); #endif /* HAVE_DPDK */ diff --git a/src/util-dpdk-mlx5.c b/src/util-dpdk-mlx5.c new file mode 100644 index 000000000000..463b054d2e31 --- /dev/null +++ b/src/util-dpdk-mlx5.c @@ -0,0 +1,76 @@ +/* Copyright (C) 2024 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 NVIDIA mlx5 driver helpers functions + * + * @{ + */ + +/** + * \file + * + * \author Adam Kiripolsky + * + * DPDK driver's helper functions + * + */ + +#include "util-debug.h" +#include "util-dpdk.h" +#include "util-dpdk-bonding.h" +#include "util-dpdk-mlx5.h" +#include "util-dpdk-rss.h" + +#ifdef HAVE_DPDK + +#define MLX5_RSS_HKEY_LEN 40 + +int mlx5DeviceSetRSS(int port_id, int nb_rx_queues, char *port_name) +{ + 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_HKEY, + .rss_key_len = MLX5_RSS_HKEY_LEN, + }; + + if (nb_rx_queues < 1) { + FatalError("The number of queues for RSS configuration must be " + "configured with a positive number"); + } + + struct rte_flow_action_rss rss_action_conf = + DPDKInitRSSAction(rss_conf, nb_rx_queues, queues, RTE_ETH_HASH_FUNCTION_TOEPLITZ, true); + + int retval = DPDKCreateRSSFlowGeneric(port_id, port_name, rss_action_conf); + if (retval != 0) { + retval = rte_flow_flush(port_id, &flush_error); + if (retval != 0) { + SCLogError("%s: unable to flush rte_flow rules: %s Flush error msg: %s", port_name, + rte_strerror(-retval), flush_error.message); + } + return retval; + } + + return 0; +} + +#endif /* HAVE_DPDK */ +/** + * @} + */ diff --git a/src/util-dpdk-mlx5.h b/src/util-dpdk-mlx5.h new file mode 100644 index 000000000000..c7b871405c01 --- /dev/null +++ b/src/util-dpdk-mlx5.h @@ -0,0 +1,35 @@ +/* Copyright (C) 2024 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 + */ + +#ifndef UTIL_DPDK_MLX5_H +#define UTIL_DPDK_MLX5_H + +#include "suricata-common.h" + +#ifdef HAVE_DPDK + +int mlx5DeviceSetRSS(int port_id, int nb_rx_queues, char *port_name); + +#endif /* HAVE_DPDK */ + +#endif /* UTIL_DPDK_MLX5_H */ diff --git a/src/util-dpdk-rss.c b/src/util-dpdk-rss.c new file mode 100644 index 000000000000..70d49e3e6e15 --- /dev/null +++ b/src/util-dpdk-rss.c @@ -0,0 +1,211 @@ +/* Copyright (C) 2024 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 + * + * 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 Creates RTE_FLOW RSS rule used by NIC drivers + * to redistribute packets to different queues based + * on IP adresses. + * + * \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 DPDKCreateRSSFlowGeneric( + int port_id, const char *port_name, struct rte_flow_action_rss rss_conf) +{ + struct rte_flow_attr attr = { 0 }; + struct rte_flow_action action[] = { { 0 }, { 0 } }; + struct rte_flow_error flow_error = { 0 }; + struct rte_flow_item pattern[] = { { 0 }, { 0 } }; + + rss_conf.types = RTE_ETH_RSS_IPV4 | RTE_ETH_RSS_IPV6; + + attr.ingress = 1; + action[0].type = RTE_FLOW_ACTION_TYPE_RSS; + action[0].conf = &rss_conf; + action[1].type = RTE_FLOW_ACTION_TYPE_END; + + pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH; + pattern[1].type = RTE_FLOW_ITEM_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 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 */ +/** + * @} + */ diff --git a/src/util-dpdk-rss.h b/src/util-dpdk-rss.h new file mode 100644 index 000000000000..6be7914a0130 --- /dev/null +++ b/src/util-dpdk-rss.h @@ -0,0 +1,47 @@ +/* Copyright (C) 2024 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 + */ + +#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 DPDKCreateRSSFlowGeneric( + 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 */